Investigate a shared attribute-match constructor per Query Language
- Dominant language
- TypeScript
- Stars
- 481
- Forks
- 108
- Avg merge
- 8d 9h
- Merged PRs (30d)
- 7
Description
## Goal
Attribute filtering and Keyword Search each build "does this attribute value match this user string" independently in every Query Language, so the same idea is implemented six times:
- `connector/gremlin/fetchNeighbors/oneHopTemplate.ts` — `attributeFilterTemplate`
- `connector/gremlin/keywordSearch/keywordSearchTemplate.ts` — inline in the `.or(…)` map
- `connector/openCypher/fetchNeighbors/oneHopTemplate.ts` — `attributeFilterTemplate`
- `connector/openCypher/keywordSearch/keywordSearchTemplate.ts` — inline in the `OR` map
- `connector/sparql/fetchNeighbors/oneHopNeighborsTemplate.ts` — `createFilterTemplate`
- `connector/sparql/keywordSearch/keywordSearchTemplate.ts` — `getFilterObject`
Within SPARQL the same `regex(str(…), …, "i")` expression is written out twice, in `createFilterTemplate` and `getFilterObject`. That duplication is why the semantics drifted apart in the first place (see the divergence table in #2038).
Note the two SPARQL call sites are not structurally parallel to the others: keyword search pairs one search term against a set of predicates (`FILTER (?pValue IN (…))` plus a single value filter), whereas expansion pairs each predicate with its own value. Any shared seam has to accommodate both shapes or deliberately serve only one.
Investigate introducing one attribute-match constructor per Query Language that both features call, alongside the existing `fragment` modules from #2020.
## Expected Outcome
A recommendation covering:
- Whether the seam belongs at the *predicate* (one attribute compared to one value) or higher at the whole where-clause. Note that Keyword Search carries per-language special cases that neighbor expansion has no use for — the node-ID pseudo-attribute, missing-type handling, and the all-attributes fan-out — which argues for the narrower seam.
- How the node-ID case is modelled. Today it travels as a sentinel string (`SEARCH_TOKENS.NODE_ID`, value `"__id"`) inside `searchByAttributes: string[]`, so "which kind of string is this" is re-derived in every template. A discriminated union — an attribute name versus the ID — would make it explicit. SPARQL cannot search by ID at all, and today the only thing preventing a malformed query is that the UI withholds the option (`useKeywordSearch.ts`); the constructor could reject it at the boundary via the existing `InvalidFragmentValueError` instead.
- Whether exact-vs-partial becomes an explicit parameter shared by both features. Note the two features currently disagree on default: Keyword Search defaults to exact, neighbor expansion is always partial. Whether to surface a partial/exact control in the node-expand filter UI is a product decision this spike should flag, not settle.
- Where the combinator stays. Keyword Search ORs attributes (one term, many attributes); expansion ANDs filters (many name/value pairs, each narrowing). Expansion's intended combinator is AND, and in either case the combinator belongs in the feature layer, not the shared constructor.
- Whether missing-type handling belongs in the same seam or a separate one — it is a type predicate rather than an attribute-value predicate.
## Related Issues
- Originated from #2031
- Related to #2020
- Related to #2038
> [!IMPORTANT]
> Internal only — this issue is maintained by the core team and is not accepting external contributions.
Contributor guide
Research direction
Start by comparing the six named templates and read the divergence table in #2038, with the fragment modules from #2020 as context. Produce a recommendation covering the shared predicate seam, node-ID modelling, exact-versus-partial matching, feature-level combinators, and missing-type handling; done means the expected outcome is addressed without implementing the refactor.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- backend-api-design
- Issue type
- Refactor
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 20/100