The co-element filter #771 was built for – this agent in this role – hangs Typesense 30.2 indefinitely whenever the nested fields hold arrays, which is every document a qualified relation produces in practice.
Reproducer
One document, typesense/typesense:30.2, no LDE code in the path:
curl -XPOST 'http://localhost:8108/collections' -H 'X-TYPESENSE-API-KEY: dev' -d '{
"name": "weld", "enable_nested_fields": true,
"fields": [ {"name": "credits", "type": "object[]"},
{"name": "credits.role", "type": "string[]"},
{"name": "credits.agent_id", "type": "string[]"} ] }'
# array-valued nested fields
curl -XPOST 'http://localhost:8108/collections/weld/documents' -H 'X-TYPESENSE-API-KEY: dev' \
-d '{"id": "w1", "credits": [{"role": ["etser"], "agent_id": ["p1"]}]}'
curl -G 'http://localhost:8108/collections/weld/documents/search' -H 'X-TYPESENSE-API-KEY: dev' \
--data-urlencode 'q=*' --data-urlencode 'query_by=' \
--data-urlencode 'filter_by=credits.{role:=etser && agent_id:=p1}'
The search never returns. The identical document with scalar nested values – {"role": "etser", "agent_id": "p1"} – answers 1 hit instantly, same schema and same filter.
What we narrowed it to
- It is a hang, not a slow scan: 0.4 % CPU throughout,
inflight_searches: 0 in the log, no response in 300 s. Other queries keep being served normally.
- The trigger is the array inside each object, not the syntax, the field types or the values. These all answer in milliseconds: one condition inside the braces (either field); the two conditions joined outside the braces (
credits.role:=etser && credits.agent_id:=p1); two string[] conditions welded over scalar-valued objects; and Typesense’s own documented example, ingredients.{name:=cheese && concentration:<50}, whose objects also hold scalars.
- IRIs, backquoting and the partial-match operator make no difference.
The docs’ example holding scalars is presumably why the design work behind #771 cleared the query side against a live 30.2 container.
Why it reaches every real document
A qualified relation declares its edge fields array: true for good reasons – a role may be stated once per language, and the endpoint is multi-valued – so the projected entries hold arrays and the welded filter is unusable across the whole collection. Measured on a real index: creator.role:=fotograaf answers 46 works in 0.0 s, creator.creator_id:=<iri> answers 22 works in 0.0 s, and creator.{role:=fotograaf && creator_id:=<iri>} never returns. Through the GraphQL API that surfaces as Unexpected error, the 5 s Typesense timeout retried once.
So the data is fine – roles, agents and the resolved lookup are all indexed and readable, and either condition alone filters – but the weld itself, the one thing a nested edge buys over two flat fields, cannot be asked. Unwelded, role: fotograaf && creator: X still matches a work where X is the publisher and somebody else is the photographer.
Suggestion
The fix looks like Typesense’s: the syntax is documented and the input valid. What seems worth doing here is a note in #771’s documentation on which value shapes the capability can serve today, and – if it is worth dodging until Typesense fixes it – deciding whether a single-valued edge field should project a scalar rather than a one-element array.
Downstream context: https://codeberg.org/limburg/lol/issues/162.
The co-element filter #771 was built for – this agent in this role – hangs Typesense 30.2 indefinitely whenever the nested fields hold arrays, which is every document a qualified relation produces in practice.
Reproducer
One document,
typesense/typesense:30.2, no LDE code in the path:The search never returns. The identical document with scalar nested values –
{"role": "etser", "agent_id": "p1"}– answers 1 hit instantly, same schema and same filter.What we narrowed it to
inflight_searches: 0in the log, no response in 300 s. Other queries keep being served normally.credits.role:=etser && credits.agent_id:=p1); twostring[]conditions welded over scalar-valued objects; and Typesense’s own documented example,ingredients.{name:=cheese && concentration:<50}, whose objects also hold scalars.The docs’ example holding scalars is presumably why the design work behind #771 cleared the query side against a live 30.2 container.
Why it reaches every real document
A qualified relation declares its edge fields
array: truefor good reasons – a role may be stated once per language, and the endpoint is multi-valued – so the projected entries hold arrays and the welded filter is unusable across the whole collection. Measured on a real index:creator.role:=fotograafanswers 46 works in 0.0 s,creator.creator_id:=<iri>answers 22 works in 0.0 s, andcreator.{role:=fotograaf && creator_id:=<iri>}never returns. Through the GraphQL API that surfaces asUnexpected error, the 5 s Typesense timeout retried once.So the data is fine – roles, agents and the resolved lookup are all indexed and readable, and either condition alone filters – but the weld itself, the one thing a nested edge buys over two flat fields, cannot be asked. Unwelded,
role: fotograaf && creator: Xstill matches a work where X is the publisher and somebody else is the photographer.Suggestion
The fix looks like Typesense’s: the syntax is documented and the input valid. What seems worth doing here is a note in #771’s documentation on which value shapes the capability can serve today, and – if it is worth dodging until Typesense fixes it – deciding whether a single-valued edge field should project a scalar rather than a one-element array.
Downstream context: https://codeberg.org/limburg/lol/issues/162.