Repository navigation
fix(#4296): a negated path is a FILTER, never the anchor it excludes - #4333
Merged
Merged
Conversation
`QueryParser` folded a negated `path:` into the path ANCHOR and dropped the
operator, so a query that said "everywhere EXCEPT A" was read as "only A":
* `-path:A|B` tokenises to NotIn with two values; the `In or NotIn` arm set
`Paths = Values` AND `Path = Values[0]`;
* `-path:A` tokenises to NotEqual and fell through to the plain `path = value`
else-branch.
Every consumer of ParsedQuery then saw a positive anchor: IsSufficientlySpecified()
counted the query as anchored, ResolvePinnedPartition pinned it to A's schema, and
the per-schema route applied `path IN (A, B)` — so the query returned rows from
exactly the paths it meant to exclude, or nothing, with no error in either
direction. A wrong anchor is also a wrong PARTITION, so the exclusion re-routed a
mesh-wide read onto the one partition it had been told to skip.
Negated operators now join the ordered comparisons carved out for the same reason
in #2186: they stay filter tokens, so the backend applies `n.path <> 'A'` /
`n.path NOT IN (...)` against the real, indexed path column and the query is
UNANCHORED unless something else anchors it — which is the truth about what it
asked for. A query whose only "anchor" was the exclusion is now refused by
MeshOperations.Search with the specificity message, instead of being served from
the excluded partition. `path:a|b|c` and `path:a` are untouched.
Watched failing first: the four new cases fail against the parser as it stood —
the two anchoring rows report the excluded path as Path/Paths, the two filter rows
find no path condition in the filter at all — and APositivePathStillAnchors passes
in both states as the negative control. 27/27 in ParsedQueryAnchoringTest and
712/712 in MeshWeaver.Hosting.Test after the change.
Closes #4296.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The fix, tests, and documentation are complete with no unresolved blocking issues.
Pull request overview
Fixes negated path: queries so they remain filters instead of becoming positive partition anchors.
Changes:
- Preserves
NotEqualandNotInpath conditions as filters. - Adds anchoring and filter-preservation tests.
- Documents the user-visible fix.
File summaries
| File | Description |
|---|---|
test/MeshWeaver.Hosting.Test/ParsedQueryAnchoringTest.cs |
Tests negated and positive path behavior. |
src/MeshWeaver.Mesh.Contract/Query/QueryParser.cs |
Keeps negated paths out of anchor fields. |
src/MeshWeaver.Documentation/Data/WhatsNew/2026-09-14-a-search-that-excludes-a-folder-no-longer-searches-only-that-folder.md |
Documents the corrected search behavior. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
Contributor
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4296.
Reproduced before fixing
The review finding from #4290 is live. Added the four cases to
ParsedQueryAnchoringTestand ranthem against the parser as it stood — all four fail:
APositivePathStillAnchorspasses in both states — the negative control that says the carve-out isfor negation only.
The defect
-path:A|B→NotInwith two values → theIn or NotInarm setPaths = ValuesandPath = Values[0]. The operator was dropped.-path:A→NotEqual→ fell through to the plainelse path = value. The negation was dropped.So "everywhere EXCEPT A" reached every consumer as a positive anchor at A:
IsSufficientlySpecified()counted it as anchored,ResolvePinnedPartitionpinned the query to A'sschema, and the per-schema route applied
path IN (A, B). The query returned rows from exactly thepaths it meant to exclude, or nothing — with no error in either direction. Because a path anchor
also chooses the PARTITION, the exclusion re-routed a mesh-wide read onto the one partition it had
been told to skip.
The fix
NotEqual/NotInonpathjoin the ordered comparisons that were carved out for the same reasonin #2186: they stay filter tokens. The backend then applies
n.path <> 'A'/n.path NOT IN (...)against the real, indexedpathcolumn, and the query is unanchored unless something elseanchors it — which is what it actually asked for. A query whose only "anchor" was the exclusion is
now refused by
MeshOperations.Searchwith the specificity message (#4274) instead of being servedfrom the excluded partition.
Both halves are pinned, because fixing only the first would trade a wrong anchor for a query that
quietly returns the excluded rows:
ANegatedPathIsNeverAnAnchorassertsPath/Pathsare null andthe query is not specified;
ANegatedPathSurvivesAsAFilterConditionasserts the condition survivesin the filter with its operator and value count intact.
Verification
MeshWeaver.Hosting.TestParsedQueryAnchoringTest— 27/27 (was 23/27 before the parser change)MeshWeaver.Hosting.Testwhole project — 712/712 passed, Releasedotnet build src/MeshWeaver.Mesh.Contract -c Release -warnaserror— 0 Warning(s), 0 Error(s)Reach outside this repo
The Postgres planner in MeshWeaver.Plugins reads
Path/Pathsthrough the sameParsedQuery.IsSufficientlySpecified(since #4290) — no code change there, and the behaviour itinherits is the corrected one: a negated-path-only query is now unanchored, so the planner refuses it
rather than pinning to the excluded schema. No in-repo caller writes
-path:(sweptsrc/andtest/: two documentation mentions, both hypothetical), and nothing is removed, so a mesh-storedcaller that used the form gets a correct answer where it previously got the excluded rows or none.
Pairs-with: none — no public type or member leaves
src/; the diff changes a parser branch, addstests and a What's New entry.
🤖 Generated with Claude Code