Skip to content

[DO NOT MERGE] fix: treat missing properties as null in FilterPredicate (#70)#71

Draft
McNultyyy wants to merge 10 commits into
mainfrom
mcnultyyy/fix-filter-predicate-missing-property-nu
Draft

[DO NOT MERGE] fix: treat missing properties as null in FilterPredicate (#70)#71
McNultyyy wants to merge 10 commits into
mainfrom
mcnultyyy/fix-filter-predicate-missing-property-nu

Conversation

@McNultyyy
Copy link
Copy Markdown
Collaborator

Motivation

When using NullValueHandling.Ignore with Newtonsoft.Json, properties set to null are omitted from the serialized document entirely. In real Cosmos DB, FilterPredicate on patch operations treats these missing properties as null -- so a condition like FROM c WHERE c.prop = null correctly matches documents where the property is absent. The in-memory emulator was returning PreconditionFailed instead, because its EvaluateComparison method short-circuits to false when either operand is UndefinedValue.

Fixes: #70

Approach

Added a treatUndefinedAsNull parameter through the WHERE expression evaluation chain (EvaluateWhereExpression -> EvaluateComparison -> EvaluateWhereExpressionIncludesUndefined -> ComparisonIncludesUndefined). This parameter is passed as true only from the two FilterPredicate call sites in PatchItemCore and its stream-based counterpart. When enabled, UndefinedValue is converted to null before comparison.

This preserves existing query semantics (where undefined != null per Cosmos DB spec) while fixing FilterPredicate behavior.

Tests

  • Integration tests (Issue70FilterPredicateMissingPropertyTests.cs): 4 tests exercising the full SDK HTTP pipeline with a custom NullValueHandling.Ignore serializer
  • Unit tests (PatchDeepDiveTests.cs): 5 tests using InMemoryContainer directly to verify missing-property-equals-null behavior in filter predicates

All tests tagged [Trait(TestTraits.Target, TestTraits.InMemoryOnly)] since the Windows Cosmos DB Emulator v2.14.0 doesn't support FilterPredicate syntax (tracked in #53).

Notes

McNultyyy and others added 10 commits May 18, 2026 23:03
… round-trip (#64)

ExprToString did not wrap TernaryExpression or CoalesceExpression in
parentheses when they appeared as operands of higher-precedence binary,
unary, BETWEEN, IN, or LIKE operators. When the SDK's transformed query
was round-tripped through SimplifySdkQuery → re-parse, the missing
parentheses produced a different AST — causing COUNT(expr) to evaluate
the wrong condition and miscount documents.

Added WrapIfLowPrecedence helper that wraps ternary/coalesce nodes in
parens when they appear in operator positions that would otherwise
re-parse with different associativity.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The EmulatorFlaky trait was blanket-excluding 12 tests from all emulator
runs, creating a regression gap. The root cause was a single test
(ConcurrentReadsOfNonExistent) overwhelming the emulator with 50 parallel
requests.

Instead of excluding the entire class, make the concurrency adaptive:
- 50 concurrent reads for in-memory (fast, no resource constraints)
- 10 concurrent reads for emulator targets (same assertion, lower volume)

Changes:
- Remove [Trait(TestTraits.Target, TestTraits.EmulatorFlaky)] from
  Issue18EdgeCaseIntegrationTests
- Remove EmulatorFlaky constant from TestTraits.cs
- Remove Target!=EmulatorFlaky filter from scripts/run-tests.ps1

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The Windows emulator can return 404 with substatus 0 during an
intermediate startup state (between 403/1008 and fully ready). This was
causing the EmulatorWarmup tool and EmulatorRetry to treat it as fatal,
failing the parity workflow before tests even ran.

Add 404/0 to the transient error set in both:
- tests/EmulatorWarmup/Program.cs (warmup tool)
- tests/.../EmulatorSession.cs (shared retry helper)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Documents how to publish beta/prerelease and stable packages via the
existing release.yml workflow (tag push convention) for AI/LLM agent
discoverability.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
String literal aliases (e.g. SELECT 'Settlement' AS Label, COUNT(1) ...)
returned null on Linux because ProjectAggregateFields treated them as
property paths via SelectToken. On Windows the bug was masked by
ServiceInterop's native aggregate pipeline.

The fix checks for non-identifier SqlExpr nodes in the else branch and
evaluates them via EvaluateSqlExpression, matching the existing pattern
used in the GROUP BY aggregate path.

Covers string, numeric, boolean, and null literal aliases with
integration tests.

Bumps version to 4.0.19.

Closes #67

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…gences

ServiceInterop.dll is only available on Windows, causing fundamentally
different code paths to execute. Adding a windows-latest integration
test job ensures both paths are tested on every PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ases-linux' into mcnultyyy/merge-prs-65-66-68
)

In real Cosmos DB, FilterPredicate on patch operations treats missing
properties as null, so 'FROM c WHERE c.prop = null' matches documents
where the property is absent (e.g. due to NullValueHandling.Ignore).

Add treatUndefinedAsNull parameter to the WHERE evaluation chain,
passed as true only from the two FilterPredicate call sites. This
preserves existing query semantics (undefined != null) while fixing
the FilterPredicate behavior.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@McNultyyy McNultyyy changed the title fix: treat missing properties as null in FilterPredicate (#70) [DO NOT MERGE] fix: treat missing properties as null in FilterPredicate (#70) May 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FilterPredicate does not treat missing properties as null when NullValueHandling.Ignore is used

1 participant