From 879e2561f54851600a80f9fb1dd5798025c1e8e2 Mon Sep 17 00:00:00 2001 From: Manh Nguyen Date: Thu, 17 Oct 2024 15:32:02 +0700 Subject: [PATCH] Support facet on IEnumerable property - Add methods for supporting facet for IEnumerable property - Add unit tests for feature --- .../Api/Querying/TypeQueryBuilder.cs | 38 ++++++--- .../Extensions/FacetExtension.cs | 23 +++++- .../ExtensionTests/FacetExtensionTests.cs | 82 +++++++++++++++++++ .../QueryTypeObjects/NestedObject.cs | 1 + 4 files changed, 133 insertions(+), 11 deletions(-) create mode 100644 APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/FacetExtensionTests.cs diff --git a/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs b/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs index def645f8..be3e4d0b 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs @@ -292,6 +292,12 @@ public TypeQueryBuilder Autocomplete(Expression> fieldSelecto } return this; } + private void SetFacetClause(string facetClause) + { + graphObject.Facets = graphObject.Facets.IsNullOrEmpty() ? + $"{facetClause}" : + $"{graphObject.Facets} {facetClause}"; + } /// /// Get facet by field /// @@ -324,9 +330,25 @@ public TypeQueryBuilder Facet(Expression> fieldSelector) public TypeQueryBuilder Facet(string propertyName) { string facet = ConvertNestedFieldToString.ConvertNestedFieldForFacet(propertyName); - graphObject.Facets = graphObject.Facets.IsNullOrEmpty() ? - $"{facet}" : - $"{graphObject.Facets} {facet}"; + SetFacetClause(facet); + return this; + } + public TypeQueryBuilder Facet(Expression>> enumSelector, Expression> fieldSelector) + { + enumSelector.ValidateNotNullArgument("enumSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + var combinePath = $"{enumSelector.GetFieldPath()}.{fieldSelector.GetFieldPath()}"; + Facet(combinePath); + return this; + } + public TypeQueryBuilder Facet(Expression>> enumSelector, Expression> fieldSelector) + { + enumSelector.ValidateNotNullArgument("enumSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + var parse = new FacetExpressionParser(); + var facetFilter = parse.GetFacetFilter(fieldSelector); + + SetFacetClause($"{enumSelector.GetFieldPath()}{{{facetFilter.FilterClause}}}"); return this; } public TypeQueryBuilder Total(bool? isAll = null) @@ -708,14 +730,12 @@ public TypeQueryBuilder Where(string fieldPath, IFilterOperator filte Where(fieldPath, filterOperator); return this; } - private TypeQueryBuilder Facet(string propertyName, IFacetOperator facetFilter) + public TypeQueryBuilder Facet(string propertyName, IFacetOperator facetFilter) { propertyName.ValidateNotNullArgument("propertyName"); facetFilter.ValidateNotNullArgument("facetFilter"); string facets = ConvertNestedFieldToString.ConvertNestedFieldForFacet(propertyName, facetFilter); - graphObject.Facets = graphObject.Facets.IsNullOrEmpty() ? - $"{facets}" : - $"{graphObject.Facets} {facets}"; + SetFacetClause(facets); return this; } public TypeQueryBuilder Facet(Expression> fieldSelector, IFacetOperator facetFilter) @@ -789,9 +809,7 @@ public TypeQueryBuilder Facet(Expression> fieldSelector, D public TypeQueryBuilder Facet(IFacetFilter facetFilter) { facetFilter.ValidateNotNullArgument("facetFilter"); - graphObject.Facets = graphObject.Facets.IsNullOrEmpty() ? - $"{facetFilter.FilterClause}" : - $"{graphObject.Facets} {facetFilter.FilterClause}"; + SetFacetClause(facetFilter.FilterClause); return this; } public TypeQueryBuilder FilterForVisitor(params IFilterForVisitor[] filterForVisitors) diff --git a/APIs/src/EpiServer.ContentGraph/Extensions/FacetExtension.cs b/APIs/src/EpiServer.ContentGraph/Extensions/FacetExtension.cs index 72c1e742..fef20c83 100644 --- a/APIs/src/EpiServer.ContentGraph/Extensions/FacetExtension.cs +++ b/APIs/src/EpiServer.ContentGraph/Extensions/FacetExtension.cs @@ -5,10 +5,31 @@ namespace EPiServer.ContentGraph.Extensions { public static class FacetExtension { - public static DelegateFacetFilterBuilder FacetLimit(this object field, int limit=5) + public static DelegateFacetFilterBuilder FacetLimit(this object field, int limit = 5) { return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); } + public static DelegateFacetFilterBuilder FacetLimit(this string field, int limit=5) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); + } + public static DelegateFacetFilterBuilder FacetLimit(this float field, int limit = 5) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); + } + public static DelegateFacetFilterBuilder FacetLimit(this double field, int limit = 5) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); + } + public static DelegateFacetFilterBuilder FacetLimit(this int field, int limit = 5) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); + } + public static DelegateFacetFilterBuilder FacetLimit(this bool field, int limit = 5) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Limit(limit))); + } + public static DelegateFacetFilterBuilder FacetFilters(this string field, params string[] values) { return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new StringFacetFilterOperators().Filters(values))); diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/FacetExtensionTests.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/FacetExtensionTests.cs new file mode 100644 index 00000000..2ef1a22c --- /dev/null +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/FacetExtensionTests.cs @@ -0,0 +1,82 @@ +using EPiServer.ContentGraph.Api.Querying; +using EpiServer.ContentGraph.UnitTests.QueryTypeObjects; +using Xunit; +using EPiServer.ContentGraph.Extensions; + +namespace EpiServer.ContentGraph.UnitTests.ExtensionTests +{ + [CollectionDefinition("Facet extension tests")] + public class FacetExtensionTests + { + TypeQueryBuilder typeQueryBuilder; + public FacetExtensionTests() + { + typeQueryBuilder = new TypeQueryBuilder(); + } + + [Fact] + public void generate_facet_filter_with_extension() + { + const string expectedFields = "items{Property1}"; + const string expectedFacet = "facets{Property1(filters: [\"test\"]){name count}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.Property1.FacetFilters("test")); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFacet, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + [Fact] + public void generate_facet_limit_with_extension() + { + const string expectedFields = "items{Property1}"; + const string expectedFacet = "facets{Property1(limit: 10){name count}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.Property1.FacetLimit(10)); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFacet, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + [Fact] + public void generate_facet_with_IEnumerable() + { + const string expectedFields = "items{Property1}"; + const string expectedFacet = "facets{Nesteds{NestedProperty{name count}}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.Nesteds, f=> f.NestedProperty); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFacet, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + [Fact] + public void generate_facet_limit_with_IEnumerable() + { + const string expectedFields = "items{Property1}"; + const string expectedFacet = "facets{Nesteds{NestedProperty(limit: 10){name count}}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.Nesteds, f => f.NestedProperty.FacetLimit(10)); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFacet, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + } +} diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/NestedObject.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/NestedObject.cs index d1d2a949..f47b7681 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/NestedObject.cs +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/NestedObject.cs @@ -3,5 +3,6 @@ internal class NestedObject { public int NestedProperty { get; set; } + public string NestedStringProperty { get; set; } } }