Skip to content

Commit 4ee1d1a

Browse files
pdevito3claude
andcommitted
fix: add support for NotEqual operator in derived property expressions
Derived properties containing the != (NotEqual) operator would previously fail with "Operator for expression type 'NotEqual' is not supported" error. This was due to the GetOperator method missing the NotEqual case. - Add ExpressionType.NotEqual => "!=" to GetOperator switch statement - Add integration tests for derived properties with != operators - Support complex conditional expressions like x.Patient != null ? ... : ... - Support multiple != checks in boolean expressions Fixes issue where derived properties like: config.DerivedProperty<T>(x => x.Field != null ? x.Field : "default") would throw NotSupportedException. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 88c44e7 commit 4ee1d1a

2 files changed

Lines changed: 91 additions & 0 deletions

File tree

QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3529,4 +3529,94 @@ public async Task can_filter_with_deeply_nested_logical_expressions()
35293529
people.Count.Should().Be(1);
35303530
people[0].Id.Should().Be(targetPerson.Id);
35313531
}
3532+
3533+
[Fact]
3534+
public async Task can_filter_with_derived_property_containing_not_equal_operator()
3535+
{
3536+
// Arrange
3537+
var testingServiceScope = new TestingServiceScope();
3538+
var uniqueId = Guid.NewGuid().ToString()[..8];
3539+
3540+
// Create a person with FirstName and LastName (both not null)
3541+
var personWithFullName = new FakeTestingPersonBuilder()
3542+
.WithFirstName($"John_{uniqueId}")
3543+
.WithLastName($"Doe_{uniqueId}")
3544+
.WithTitle($"Person1_{uniqueId}")
3545+
.Build();
3546+
3547+
// Create a person with only FirstName (LastName is null)
3548+
var personWithPartialName = new FakeTestingPersonBuilder()
3549+
.WithFirstName($"Jane_{uniqueId}")
3550+
.WithLastName(null) // Explicitly null
3551+
.WithTitle($"Person2_{uniqueId}")
3552+
.Build();
3553+
3554+
await testingServiceScope.InsertAsync(personWithFullName, personWithPartialName);
3555+
3556+
// Test derived property that uses != operator like the original user issue:
3557+
// x.Patient != null ? x.Patient.FirstName + " " + x.Patient.LastName : null
3558+
var input = $"""patient_name == "John_{uniqueId} Doe_{uniqueId}" """;
3559+
var config = new QueryKitConfiguration(config =>
3560+
{
3561+
config.DerivedProperty<TestingPerson>(x =>
3562+
x.LastName != null
3563+
? x.FirstName + " " + x.LastName
3564+
: x.FirstName ?? "")
3565+
.HasQueryName("patient_name");
3566+
});
3567+
3568+
// Act
3569+
var queryablePeople = testingServiceScope.DbContext().People;
3570+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
3571+
var people = await appliedQueryable.ToListAsync();
3572+
3573+
// Assert
3574+
people.Count.Should().Be(1);
3575+
people[0].Id.Should().Be(personWithFullName.Id);
3576+
}
3577+
3578+
[Fact]
3579+
public async Task can_filter_with_derived_property_containing_multiple_not_equal_operators()
3580+
{
3581+
// Arrange
3582+
var testingServiceScope = new TestingServiceScope();
3583+
var uniqueId = Guid.NewGuid().ToString()[..8];
3584+
3585+
// Create test data
3586+
var validPerson = new FakeTestingPersonBuilder()
3587+
.WithFirstName($"Valid_{uniqueId}")
3588+
.WithLastName($"Person_{uniqueId}")
3589+
.WithTitle($"Mr_{uniqueId}") // Not null and not empty and unique
3590+
.WithAge(25) // Not null
3591+
.Build();
3592+
3593+
var invalidPerson = new FakeTestingPersonBuilder()
3594+
.WithFirstName($"Invalid_{uniqueId}")
3595+
.WithLastName(null) // null LastName
3596+
.WithTitle("") // empty Title
3597+
// Age left as default (null)
3598+
.Build();
3599+
3600+
await testingServiceScope.InsertAsync(validPerson, invalidPerson);
3601+
3602+
// Test derived property with multiple != checks and additional filter to isolate our test data
3603+
var input = $"""is_complete == true && Title == "Mr_{uniqueId}" """;
3604+
var config = new QueryKitConfiguration(config =>
3605+
{
3606+
config.DerivedProperty<TestingPerson>(x =>
3607+
x.FirstName != null && x.LastName != null &&
3608+
x.Title != null && x.Title != "" &&
3609+
x.Age != null)
3610+
.HasQueryName("is_complete");
3611+
});
3612+
3613+
// Act
3614+
var queryablePeople = testingServiceScope.DbContext().People;
3615+
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
3616+
var people = await appliedQueryable.ToListAsync();
3617+
3618+
// Assert
3619+
people.Count.Should().Be(1);
3620+
people[0].Id.Should().Be(validPerson.Id);
3621+
}
35323622
}

QueryKit/QueryKitPropertyMappings.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ private static string GetOperator(ExpressionType nodeType)
357357
ExpressionType.GreaterThanOrEqual => ">=",
358358
ExpressionType.LessThanOrEqual => "<=",
359359
ExpressionType.Equal => "==",
360+
ExpressionType.NotEqual => "!=",
360361
_ => throw new NotSupportedException($"Operator for expression type '{nodeType}' is not supported.")
361362
};
362363
}

0 commit comments

Comments
 (0)