Skip to content

Commit 7496138

Browse files
roed314claude
andcommitted
Allow multiple intermediate fields in number field search (LMFDB#6827)
The "Intermediate field" search box now accepts a comma-separated list of subfields (polynomials, field labels, or nicknames, freely mixed). The search returns number fields containing every listed subfield, i.e. the AND of the containment conditions (equivalently, fields containing their compositum). This makes it easy to search for e.g. Q(sqrt2, sqrt3, sqrt5) without first computing a defining polynomial for the compositum. parse_subfield splits the input on commas and parses each piece with the existing input_to_subfield helper, emitting {"$contains": [...]} on the text[] subfields column. This compiles to a single "subfields @> ARRAY[...]" Postgres containment test (no per-row recomputation) -- the same operator the single-subfield search already used, so single-field behavior is unchanged. Whitespace is stripped by the search parser, so "x^2-2, x^2-3" works too. A malformed entry raises the existing clean SearchParsingError. Help text and a test were added. Verified with the flask test client: single label/polynomial (unchanged), multiple polynomials and multiple labels both returning the compositum, three quadratics correctly excluding the degree-4 field, and malformed input giving a flashed error rather than a 500. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5ef81bd commit 7496138

3 files changed

Lines changed: 26 additions & 4 deletions

File tree

lmfdb/number_fields/number_field.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1316,7 +1316,8 @@ def __init__(self):
13161316
label="Intermediate field",
13171317
knowl="nf.intermediate_fields",
13181318
example_span="2.2.5.1 or x^2-5 or a "
1319-
+ display_knowl("nf.nickname", "field nickname"),
1319+
+ display_knowl("nf.nickname", "field nickname")
1320+
+ ", or a comma-separated list, e.g. x^2-2,x^2-3",
13201321
example="x^2-5")
13211322
completion = TextBox(
13221323
name="completions",

lmfdb/number_fields/test_numberfield.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ def test_search_multiple_fields(self):
4646
self.check_args('/NumberField/?jump=Qsqrt5%2c+x%5E2-3&search=Go', '2.2.5.1')
4747
self.check_args('/NumberField/?jump=Qsqrt5%2c+x%5E2-3&search=Go', '2.2.12.1')
4848

49+
def test_search_subfield(self):
50+
# Single intermediate field (unchanged behavior): fields containing Q(sqrt2)
51+
self.check_args_with_timeout('/NumberField/?subfield=x%5E2-2', '4.0.256.1')
52+
# Multiple intermediate fields: a comma-separated list matches fields
53+
# containing every listed subfield (the compositum). Q(sqrt2,sqrt3) =
54+
# 4.4.2304.1 contains both Q(sqrt2) and Q(sqrt3).
55+
self.check_args_with_timeout('/NumberField/?subfield=x%5E2-2%2Cx%5E2-3', '4.4.2304.1')
56+
# Same search given by field labels instead of polynomials
57+
self.check_args_with_timeout('/NumberField/?subfield=2.2.8.1%2C2.2.12.1', '4.4.2304.1')
58+
# A malformed entry in the list gives a clean error, not a 500
59+
self.check_args('/NumberField/?subfield=x%5E2-2%2Cnotafield', 'not a valid field nickname or label')
60+
4961
def test_search_disc(self):
5062
self.check_args('/NumberField/?discriminant=1988-2014', '401') # factor of one of the discriminants
5163

lmfdb/utils/search_parsing.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1615,9 +1615,18 @@ def notq():
16151615

16161616
@search_parser # see SearchParser.__call__ for actual arguments when calling
16171617
def parse_subfield(inp, query, qfield):
1618-
sf = input_to_subfield(inp)
1619-
if sf: # Might return none
1620-
query[qfield] = {"$contains": sf}
1618+
if "," in inp:
1619+
# A comma-separated list of subfields means AND of the containment
1620+
# conditions, i.e. fields containing every listed subfield (equivalently,
1621+
# their compositum). input_to_subfield may return None for empty pieces
1622+
# (e.g. a trailing comma), which we drop.
1623+
sfs = [sf for sf in (input_to_subfield(f) for f in inp.split(",")) if sf]
1624+
if sfs:
1625+
query[qfield] = {"$contains": sfs}
1626+
else:
1627+
sf = input_to_subfield(inp)
1628+
if sf: # Might return none
1629+
query[qfield] = {"$contains": sf}
16211630

16221631
@search_parser # see SearchParser.__call__ for actual arguments when calling
16231632
def parse_nf_string(inp, query, qfield):

0 commit comments

Comments
 (0)