From 6ac635a3b819145e0fec90ed724617b3819fe282 Mon Sep 17 00:00:00 2001 From: Sebastian Stehle Date: Tue, 12 Feb 2019 18:10:37 +0100 Subject: [PATCH] Empty filter. --- .../MongoDb/Queries/FilterVisitor.cs | 2 ++ .../Queries/FilterBuilder.cs | 5 +++++ .../Queries/FilterComparison.cs | 2 ++ .../Queries/FilterOperator.cs | 1 + .../Queries/OData/EdmModelExtensions.cs | 8 ++++++++ .../Queries/OData/FilterVisitor.cs | 6 ++++++ .../Contents/MongoDb/MongoDbQueryTests.cs | 20 +++++++++++++------ .../Queries/ODataConversionTests.cs | 18 +++++++++++++++++ 8 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/Squidex.Infrastructure.MongoDb/MongoDb/Queries/FilterVisitor.cs b/src/Squidex.Infrastructure.MongoDb/MongoDb/Queries/FilterVisitor.cs index 14fe29efa..54e60ec29 100644 --- a/src/Squidex.Infrastructure.MongoDb/MongoDb/Queries/FilterVisitor.cs +++ b/src/Squidex.Infrastructure.MongoDb/MongoDb/Queries/FilterVisitor.cs @@ -51,6 +51,8 @@ namespace Squidex.Infrastructure.MongoDb.Queries switch (nodeIn.Operator) { + case FilterOperator.Empty: + return Filter.Or(Filter.Exists(propertyName, false), Filter.Eq(propertyName, default(T)), Filter.Eq(propertyName, string.Empty), Filter.Eq(propertyName, new T[0])); case FilterOperator.StartsWith: return Filter.Regex(propertyName, BuildRegex(nodeIn, s => "^" + s)); case FilterOperator.Contains: diff --git a/src/Squidex.Infrastructure/Queries/FilterBuilder.cs b/src/Squidex.Infrastructure/Queries/FilterBuilder.cs index 21eb97cfa..389d2c4e1 100644 --- a/src/Squidex.Infrastructure/Queries/FilterBuilder.cs +++ b/src/Squidex.Infrastructure/Queries/FilterBuilder.cs @@ -47,6 +47,11 @@ namespace Squidex.Infrastructure.Queries return Binary(path, FilterOperator.Equals, value); } + public static FilterComparison Empty(string path) + { + return new FilterComparison(path.Split('.', '/'), FilterOperator.Empty, FilterValue.Null); + } + public static FilterComparison In(string path, params long[] value) { return new FilterComparison(path.Split('.', '/'), FilterOperator.In, new FilterValue(value.ToList())); diff --git a/src/Squidex.Infrastructure/Queries/FilterComparison.cs b/src/Squidex.Infrastructure/Queries/FilterComparison.cs index 81db90bc2..9f19a5dcd 100644 --- a/src/Squidex.Infrastructure/Queries/FilterComparison.cs +++ b/src/Squidex.Infrastructure/Queries/FilterComparison.cs @@ -42,6 +42,8 @@ namespace Squidex.Infrastructure.Queries { case FilterOperator.Contains: return $"contains({path}, {Rhs})"; + case FilterOperator.Empty: + return $"empty({path})"; case FilterOperator.EndsWith: return $"endsWith({path}, {Rhs})"; case FilterOperator.StartsWith: diff --git a/src/Squidex.Infrastructure/Queries/FilterOperator.cs b/src/Squidex.Infrastructure/Queries/FilterOperator.cs index e1de05e93..5496584ea 100644 --- a/src/Squidex.Infrastructure/Queries/FilterOperator.cs +++ b/src/Squidex.Infrastructure/Queries/FilterOperator.cs @@ -10,6 +10,7 @@ namespace Squidex.Infrastructure.Queries public enum FilterOperator { Contains, + Empty, EndsWith, Equals, GreaterThan, diff --git a/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs b/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs index 53954b64f..e59535b38 100644 --- a/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs +++ b/src/Squidex.Infrastructure/Queries/OData/EdmModelExtensions.cs @@ -14,6 +14,14 @@ namespace Squidex.Infrastructure.Queries.OData { public static class EdmModelExtensions { + static EdmModelExtensions() + { + CustomUriFunctions.AddCustomUriFunction("empty", + new FunctionSignatureWithReturnType( + EdmCoreModel.Instance.GetBoolean(false), + EdmCoreModel.Instance.GetString(true))); + } + public static ODataUriParser ParseQuery(this IEdmModel model, string query) { if (!model.EntityContainer.EntitySets().Any()) diff --git a/src/Squidex.Infrastructure/Queries/OData/FilterVisitor.cs b/src/Squidex.Infrastructure/Queries/OData/FilterVisitor.cs index dfd603535..15d4f2350 100644 --- a/src/Squidex.Infrastructure/Queries/OData/FilterVisitor.cs +++ b/src/Squidex.Infrastructure/Queries/OData/FilterVisitor.cs @@ -49,6 +49,12 @@ namespace Squidex.Infrastructure.Queries.OData public override FilterNode Visit(SingleValueFunctionCallNode nodeIn) { var fieldNode = nodeIn.Parameters.ElementAt(0); + + if (string.Equals(nodeIn.Name, "empty", StringComparison.OrdinalIgnoreCase)) + { + return new FilterComparison(PropertyPathVisitor.Visit(fieldNode), FilterOperator.Empty, FilterValue.Null); + } + var valueNode = nodeIn.Parameters.ElementAt(1); if (string.Equals(nodeIn.Name, "endswith", StringComparison.OrdinalIgnoreCase)) diff --git a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs index 1b2b8c81e..022983bea 100644 --- a/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs +++ b/tests/Squidex.Domain.Apps.Entities.Tests/Contents/MongoDb/MongoDbQueryTests.cs @@ -19,7 +19,6 @@ using Squidex.Domain.Apps.Entities.MongoDb.Contents; using Squidex.Domain.Apps.Entities.MongoDb.Contents.Visitors; using Squidex.Domain.Apps.Entities.Schemas; using Squidex.Infrastructure; -using Squidex.Infrastructure.Collections; using Squidex.Infrastructure.MongoDb; using Squidex.Infrastructure.MongoDb.Queries; using Squidex.Infrastructure.Queries; @@ -46,13 +45,13 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb schemaDef = new Schema("user") .AddString(1, "firstName", Partitioning.Language, - new StringFieldProperties { Label = "FirstName", IsRequired = true, AllowedValues = ReadOnlyCollection.Create("1", "2") }) + new StringFieldProperties()) .AddString(2, "lastName", Partitioning.Language, - new StringFieldProperties { Hints = "Last Name", Editor = StringFieldEditor.Input }) + new StringFieldProperties()) .AddBoolean(3, "isAdmin", Partitioning.Invariant, new BooleanFieldProperties()) .AddNumber(4, "age", Partitioning.Invariant, - new NumberFieldProperties { MinValue = 1, MaxValue = 10 }) + new NumberFieldProperties()) .AddDateTime(5, "birthday", Partitioning.Invariant, new DateTimeFieldProperties()) .AddAssets(6, "pictures", Partitioning.Invariant, @@ -61,7 +60,7 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb new ReferencesFieldProperties()) .AddString(8, "dashed-field", Partitioning.Invariant, new StringFieldProperties()) - .Update(new SchemaProperties { Hints = "The User" }); + .Update(new SchemaProperties()); var schema = A.Dummy(); A.CallTo(() => schema.Id).Returns(Guid.NewGuid()); @@ -144,7 +143,16 @@ namespace Squidex.Domain.Apps.Entities.Contents.MongoDb } [Fact] - public void Should_make_query_date_field_created() + public void Should_make_query_with_empty_test() + { + var i = F(FilterBuilder.Empty("data/firstName/iv"), true); + var o = C("{ '$or' : [{ 'dd.1.iv' : { '$exists' : false } }, { 'dd.1.iv' : null }, { 'dd.1.iv' : '' }, { 'dd.1.iv' : [] }] }"); + + Assert.Equal(o, i); + } + + [Fact] + public void Should_make_query_with_datetime_data() { var i = F(FilterBuilder.Eq("data/birthday/iv", InstantPattern.General.Parse("1988-01-19T12:00:00Z").Value)); var o = C("{ 'do.5.iv' : '1988-01-19T12:00:00Z' }"); diff --git a/tests/Squidex.Infrastructure.Tests/Queries/ODataConversionTests.cs b/tests/Squidex.Infrastructure.Tests/Queries/ODataConversionTests.cs index fecc35ab7..5ec8c24f1 100644 --- a/tests/Squidex.Infrastructure.Tests/Queries/ODataConversionTests.cs +++ b/tests/Squidex.Infrastructure.Tests/Queries/ODataConversionTests.cs @@ -229,6 +229,24 @@ namespace Squidex.Infrastructure.Queries Assert.Equal(o, i); } + [Fact] + public void Should_parse_filter_with_empty() + { + var i = Q("$filter=empty(lastName)"); + var o = C("Filter: empty(lastName)"); + + Assert.Equal(o, i); + } + + [Fact] + public void Should_parse_filter_with_empty_to_true() + { + var i = Q("$filter=empty(lastName) eq true"); + var o = C("Filter: empty(lastName)"); + + Assert.Equal(o, i); + } + [Fact] public void Should_parse_filter_with_contains() {