Skip to content

feat: Implement field-to-field comparison in filter expressions#328

Draft
Copilot wants to merge 5 commits intomasterfrom
copilot/add-field-vs-field-feature
Draft

feat: Implement field-to-field comparison in filter expressions#328
Copilot wants to merge 5 commits intomasterfrom
copilot/add-field-vs-field-feature

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 19, 2026

Adds support for comparing two mapped fields against each other in filter expressions using (fieldName) syntax on the right-hand side of any operator. Feature is opt-in via configuration to avoid breaking changes.

Syntax

// Simple property comparison
items.ApplyFiltering("id=(score)", mapper);         // id == score
items.ApplyFiltering("age>(minAge)", mapper);        // age > minAge

// Nested collection field comparison
items.ApplyFiltering("end<(start)", mapper);         // schedules.Any(s => s.End < s.Start)

Changes

Parser / Syntax

  • Added FieldReferenceExpression to SyntaxKind
  • New FieldReferenceExpressionSyntax node wraps a FieldExpressionSyntax for the (fieldName) RHS pattern
  • ParseRightHandExpression() (renamed from ParseValueExpression) detects OpenParenthesisToken → FieldToken sequence after an operator and returns a FieldReferenceExpressionSyntax instead of a value node

Query Builder

  • BaseQueryBuilder.BuildQueryFromBinaryExpression() routes field op (field) patterns to new ConvertFieldToFieldQuery() method
  • BuildFieldToFieldQuery() added as a virtual method (throws by default, so non-LINQ builders like Elasticsearch remain unaffected)
  • LinqQueryBuilder implements two cases:
    • Simple fields: unifies lambda parameters, builds binary comparison expression
    • Nested collection fields: extracts inner Select lambdas, unifies inner parameters, builds Any(predicate) expression
  • All filtering operators are supported, including =*, !*, ^, !^, $, !$ for string field comparisons

Configuration

  • GridifyGlobalConfiguration.AllowFieldToFieldComparison — global opt-in (default: false)
  • GridifyMapperConfiguration.AllowFieldToFieldComparison — per-mapper opt-in, inherits global default
// Enable per mapper
var mapper = new GridifyMapper<T>(new GridifyMapperConfiguration { AllowFieldToFieldComparison = true })
    .AddMap("start", p => p.Schedules.Select(x => x.Start))
    .AddMap("end",   p => p.Schedules.Select(x => x.End));

items.ApplyFiltering("end<(start)", mapper);
// equivalent to: items.Where(x => x.Schedules.Any(s => s.End < s.Start))

Testing

  • Unit tests covering all six comparison operators, all six string operators, nested collection comparisons, configuration (per-mapper and global), and QueryBuilder integration
  • Grouping/parentheses conflict tests verifying field-to-field comparisons inside groups, groups combined with field-to-field conditions, and escaped parentheses in literal values remain unaffected
  • EscapedParenthesesInValue_ShouldBeSearchedAsLiteralValue theory test covering all escape patterns (\(value\), \(value, value\), value\(middle, value\)middle) to ensure escaping still works correctly when the feature is enabled
  • Entity Framework integration tests (EntityFrameworkSqlProviderIntegrationTests) confirming valid SQL generation for both simple and nested collection field-to-field comparisons

Documentation

  • docs/pages/guide/filtering.md: new "Field-to-Field Comparison" section with enabling instructions, syntax reference, and practical examples (including combining with logical operators and grouping)
  • docs/pages/guide/gridifyGlobalConfiguration.md: new AllowFieldToFieldComparison global configuration entry
  • docs/pages/guide/gridifyMapper.md: new AllowFieldToFieldComparison per-mapper configuration entry under GridifyMapperConfiguration

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist

  • I have performed a self-review of my code
  • I have added tests that prove my fix is effective or that my feature works
  • I have made corresponding changes to the documentation
  • I have commented my code, particularly in hard-to-understand areas
  • New and existing unit tests pass locally with my changes

📱 Kick off Copilot coding agent tasks wherever you are with GitHub Mobile, available on iOS and Android.

Copilot AI and others added 2 commits March 19, 2026 20:25
Co-authored-by: alirezanet <7004080+alirezanet@users.noreply.github.com>
Co-authored-by: alirezanet <7004080+alirezanet@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement field vs field comparison in query syntax feat: Implement field-to-field comparison in filter expressions Mar 19, 2026
Copilot AI requested a review from alirezanet March 19, 2026 20:31
Copy link
Copy Markdown
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good but we need some additional changes:

  • make sure to add some integration test with EntityFramework so we make sure it also works in ORMs.
  • add some tests to cover complex scenarios with grouping. Gridify uses parenthesis for group a set of conditions together. I want to make sure this feature doesn't create any conflicts with the existing behavior.
  • we should also support and test this feature with the QueryBuilder class, as you know we have to ways of creating queries in Gridify. the extension methods and the QueryBuilder.
  • You should also update the documentation in the relevant pages.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 19, 2026

Looks good but we need some additional changes:

  • make sure to add some integration test with EntityFramework so we make sure it also works in ORMs.
  • add some tests to cover complex scenarios with g...

All addressed in d40105d:

  • EF integration tests: Added test/EntityFrameworkSqlProviderIntegrationTests/Issue155Tests.cs with two [RetryFact] tests verifying that simple and nested collection field-to-field comparisons generate valid SQL through EF Core.

  • Grouping conflict tests: Added 5 tests covering: field-to-field inside a group (id=(score)|id>2), grouping combined with field-to-field comparison (name=*J|name=*S),id=(score), regular grouping unaffected when feature is disabled, escaped parentheses still treated as literal values, and OR with value filter.

  • QueryBuilder tests: Added tests for GetFilteringExpression, complex (field-to-field OR value),condition patterns via QueryBuilder.Build().

  • Documentation: Updated docs/pages/guide/filtering.md with a full "Field-to-Field Comparison" section (syntax, supported operators table, nested collection example, combining conditions), and docs/pages/guide/gridifyGlobalConfiguration.md with the AllowFieldToFieldComparison configuration entry.

Copilot AI requested a review from alirezanet March 19, 2026 21:06
Copy link
Copy Markdown
Owner

@alirezanet alirezanet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add one more test to make sure escaping works.

so user should be able to search for this value if needed for example:

field=\(somevalue\)
field=\(somevalue
field=somevalue\)
field=some\(value

… configuration entry

Co-authored-by: alirezanet <7004080+alirezanet@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 19, 2026

add one more test to make sure escaping works.

so user should be able to search for this value if needed for example:...

Added EscapedParenthesesInValue_ShouldBeSearchedAsLiteralValue theory test in f368aba covering all requested patterns: \(somevalue\), \(somevalue, somevalue\), some\(value, and some\)value.

Copilot AI requested a review from alirezanet March 19, 2026 22:02
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.

2 participants