Bad LINQ to SQL translation for filter on non-nullable boolean property
See original GitHub issue(already reported in v2, problem is still here in v3)
Describe the bug
LINQ queries with a condition on a non-nullable boolean property are not correctly translated to SQL. The translation doesn’t account for the fact that the property could be undefined in the JSON document.
To Reproduce
Assuming an object model like this:
public class Item
{
[JsonProperty("id")]
public string Id { get; set; }
public bool IsDeleted { get; set; }
}
Make the following query:
var query = container.GetItemLinqQueryable<Item>().Where(i => !i.IsDeleted);
Expected behavior
Documents where IsDeleted
is not defined should be returned, because in the C# model the property is not nullable, so its absence really means that it’s false.
The generated SQL should be something like this:
SELECT * FROM root WHERE (NOT IS_DEFINED(root["IsDeleted"]) OR NOT root["IsDeleted"])
Or maybe
SELECT * FROM root WHERE (NOT (root["IsDeleted"] ?? false))
Actual behavior
Documents where IsDeleted
is not defined are not returned, because NOT (undefined)
doesn’t evaluate to true.
The generated SQL looks like this:
SELECT * FROM root WHERE (NOT root["IsDeleted"])
Environment summary SDK Version: .NET Core 2.1, Microsoft.Azure.Cosmos 3.1.1 OS Version (e.g. Windows, Linux, MacOSX): Windows 10
Issue Analytics
- State:
- Created 4 years ago
- Reactions:3
- Comments:10 (9 by maintainers)
Top GitHub Comments
It depends on which type you’re querying with. If the type has a non-nullable
IsDeleted
field, and the query includes something like!x.IsDeleted
, translate toNOT (root["IsDeleted"] ?? false)
. If the type is a dictionary, as in your example, don’t make any assumption and translate the query toNOT root["IsDeleted"]
.But in this case, shouldn’t you filter on the
Type
field? Relying on the presence or absence of a field in the document seems brittle.I don’t think that’s a very common scenario. If the property is not nullable in the C# model, you don’t expect the field to be missing in the document ; and if it is missing, the value would be set to 0 in the C# object on deserialization anyway. If you really need to filter documents that do have an explicit value, you can always do it in SQL.
Yes, but it doesn’t really matter, I think. What matters here is the semantics of the model type you’re using in the query. If a property is not nullable in the C# model, you shouldn’t have to worry about whether it’s present in the document or not.
Indeed, it’s a case where the
IsDeleted
property was introduced later.That’s a fair point. I didn’t think about that. But maybe it could be handled with a
[DefaultValue]
attribute? If the property is not nullable, assume the default value (possibly specified with the[DefaultValue]
attribute) when the property is missing in the document?I realize it all seems unnecessarily complex, but the scenario I described is probably quite common, and having to specify
|| !x.IsDeleted.IsDefined()
in the query isn’t intuitive at all and is likely to cause a lot of bugs.I have a related problem but this time with a nullable field. Hope it’s ok to share a link to SO: https://stackoverflow.com/questions/70062891/cosmos-db-iqueryable-on-nullable-fields