diff --git a/APIs/src/EpiServer.ContentGraph/Api/Filters/AndFilter.cs b/APIs/src/EpiServer.ContentGraph/Api/Filters/AndFilter.cs index f859c852..3ec4a826 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Filters/AndFilter.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Filters/AndFilter.cs @@ -37,6 +37,7 @@ public AndFilter(Expression> fieldSelector, NumericFilterOperators And(fieldSelector, filterOperators); } #endregion + #region Base type public AndFilter And(Expression> fieldSelector, IFilterOperator filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -61,6 +62,12 @@ public AndFilter And(Expression> fieldSelector, IFilterOpera And(fieldSelector.GetFieldPath(), filterOperator); return this; } + public AndFilter And(Expression> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } public AndFilter And(Expression> fieldSelector, IFilterOperator filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -73,6 +80,64 @@ public AndFilter And(Expression> fieldSelector, IFilterOperator And(fieldSelector.GetFieldPath(), filterOperator); return this; } + #endregion + #region Enumerable + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter(Expression>> fieldSelector, StringFilterOperators filterOperators) + { + And(fieldSelector, filterOperators); + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public AndFilter And(Expression>> rootSelector, + Expression> fieldSelector, + IFilterOperator filterOperator) + { + rootSelector.ValidateNotNullArgument("rootSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + And($"{rootSelector.GetFieldPath()}.{fieldSelector.GetFieldPath()}", filterOperator); + return this; + } + #endregion private AndFilter And(Expression> fieldSelector) { fieldSelector.ValidateNotNullArgument("fieldSelector"); diff --git a/APIs/src/EpiServer.ContentGraph/Api/Filters/NotFilter.cs b/APIs/src/EpiServer.ContentGraph/Api/Filters/NotFilter.cs index 8549b1ed..daa83dbb 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Filters/NotFilter.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Filters/NotFilter.cs @@ -54,6 +54,12 @@ public NotFilter Not(Expression> fieldSelector, IFilterOpera Not(fieldSelector.GetFieldPath(), filterOperator); return this; } + public NotFilter Not(Expression> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } public NotFilter Not(Expression> fieldSelector, IFilterOperator filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -85,6 +91,63 @@ public NotFilter Not(params Expression>[] fieldSelectors) } return this; } + #region Enumerable + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter(Expression>> fieldSelector, StringFilterOperators filterOperators) + { + Not(fieldSelector, filterOperators); + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public NotFilter Not(Expression>> rootSelector, + Expression> fieldSelector, + IFilterOperator filterOperator) + { + rootSelector.ValidateNotNullArgument("rootSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Not($"{rootSelector.GetFieldPath()}.{fieldSelector.GetFieldPath()}", filterOperator); + return this; + } + #endregion } public class NotFilter : Filter diff --git a/APIs/src/EpiServer.ContentGraph/Api/Filters/NumericFilterOperators.cs b/APIs/src/EpiServer.ContentGraph/Api/Filters/NumericFilterOperators.cs index 8be5df0e..efa9955d 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Filters/NumericFilterOperators.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Filters/NumericFilterOperators.cs @@ -71,14 +71,6 @@ public NumericFilterOperators InRange(int? from, int? to) } return this; } - public NumericFilterOperators InRanges(params (int? from, int? to)[] ranges) - { - foreach (var range in ranges) - { - InRange(range.from, range.to); - } - return this; - } public NumericFilterOperators Eq(float value) { @@ -164,21 +156,5 @@ public NumericFilterOperators InRange(double? from, double? to) } return this; } - public NumericFilterOperators InRanges(params (float? from, float? to)[] ranges) - { - foreach (var range in ranges) - { - InRange(range.from, range.to); - } - return this; - } - public NumericFilterOperators InRanges(params (double? from, double? to)[] ranges) - { - foreach (var range in ranges) - { - InRange(range.from, range.to); - } - return this; - } } } diff --git a/APIs/src/EpiServer.ContentGraph/Api/Filters/OrFilter.cs b/APIs/src/EpiServer.ContentGraph/Api/Filters/OrFilter.cs index f8196973..2a54e1a9 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Filters/OrFilter.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Filters/OrFilter.cs @@ -54,6 +54,12 @@ public OrFilter Or(Expression> fieldSelector, IFilterOperator Or(fieldSelector.GetFieldPath(), filterOperator); return this; } + public OrFilter Not(Expression> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } public OrFilter Or(Expression> fieldSelector, IFilterOperator filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -92,6 +98,63 @@ public OrFilter Or(params Expression>[] fieldSelectors) } return this; } + #region Enumerable + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter(Expression>> fieldSelector, StringFilterOperators filterOperators) + { + Or(fieldSelector, filterOperators); + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> fieldSelector, IFilterOperator filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or(fieldSelector.GetFieldPath(), filterOperator); + return this; + } + public OrFilter Or(Expression>> rootSelector, + Expression> fieldSelector, + IFilterOperator filterOperator) + { + rootSelector.ValidateNotNullArgument("rootSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + Or($"{rootSelector.GetFieldPath()}.{fieldSelector.GetFieldPath()}", filterOperator); + return this; + } + #endregion } public class OrFilter : Filter diff --git a/APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs b/APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs index a65c7f7f..30777835 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Querying/FragmentBuilder.cs @@ -67,7 +67,7 @@ public class FragmentBuilder : FragmentBuilder public FragmentBuilder() : base() { } public FragmentBuilder(string name) { - _query.OperationName = name; + base.OperationName(name); } private FragmentBuilder Field(Expression> fieldSelector) { diff --git a/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs b/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs index 7df0e5f1..be3e4d0b 100644 --- a/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs +++ b/APIs/src/EpiServer.ContentGraph/Api/Querying/TypeQueryBuilder.cs @@ -115,6 +115,27 @@ public TypeQueryBuilder Fields(params Expression>[] fieldSele } return this; } + /// + /// Select properties of an IEnumerable of + /// + /// + /// IEnumerable property of + /// Fields of type + /// + public TypeQueryBuilder NestedFields(Expression>> enumSelector, params Expression>[] fieldSelectors ) + { + enumSelector.ValidateNotNullArgument("fieldSelector"); + fieldSelectors.ValidateNotNullArgument("fields"); + var enumPath = enumSelector.GetFieldPath(); + string fields = string.Empty; + foreach (var fieldSelector in fieldSelectors) + { + fields += fields.IsNullOrEmpty() ? fieldSelector.GetFieldPath(): $" {fieldSelector.GetFieldPath()}"; + } + var combinedPath = ConvertNestedFieldToString.ConvertNestedFieldForQuery($"{enumPath}.{fields}"); + Field(combinedPath); + return this; + } [Obsolete("Obsoleted. Use InlineFragment instead")] /// /// Select fields in a subtype. The response of your query may need to convert data type. If then consider to use GetContent method. @@ -271,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 /// @@ -303,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) @@ -467,6 +510,17 @@ private string MergeFilters(string exsting, string additional) } return exsting; } + private void SetWhereClause(string clause) + { + if (graphObject.WhereClause.IsNullOrEmpty()) + { + graphObject.WhereClause = clause; + } + else + { + graphObject.WhereClause += $",{clause}"; + } + } public TypeQueryBuilder Where(string fieldName, IFilterOperator filterOperator) { filterOperator.ValidateNotNullArgument("filterOperator"); @@ -488,14 +542,7 @@ public TypeQueryBuilder Where(string fieldName, IFilterOperator filterOperato } } - if (graphObject.WhereClause.IsNullOrEmpty()) - { - graphObject.WhereClause = $"{combinedQuery}"; - } - else - { - graphObject.WhereClause += $",{combinedQuery}"; - } + SetWhereClause(combinedQuery); return this; } @@ -518,6 +565,14 @@ public TypeQueryBuilder Where(Expression> fieldSelector, Stri return this; } + public TypeQueryBuilder Where(Expression>> fieldSelector, StringFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } public TypeQueryBuilder Where(Expression> fieldSelector, NumericFilterOperators filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -526,6 +581,62 @@ public TypeQueryBuilder Where(Expression> fieldSelector, Numer return this; } + public TypeQueryBuilder Where(Expression>> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression>> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression>> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } + public TypeQueryBuilder Where(Expression>> fieldSelector, NumericFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } public TypeQueryBuilder Where(Expression> fieldSelector, DateFilterOperators filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -534,6 +645,14 @@ public TypeQueryBuilder Where(Expression> fieldSelector, Date return this; } + public TypeQueryBuilder Where(Expression>> fieldSelector, DateFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } public TypeQueryBuilder Where(Expression> fieldSelector, BooleanFilterOperators filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -542,6 +661,14 @@ public TypeQueryBuilder Where(Expression> fieldSelector, Boolea return this; } + public TypeQueryBuilder Where(Expression>> fieldSelector, BooleanFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } public TypeQueryBuilder Where(Expression> fieldSelector, DateFilterOperators filterOperator) { fieldSelector.ValidateNotNullArgument("fieldSelector"); @@ -550,28 +677,65 @@ public TypeQueryBuilder Where(Expression> fieldSelector, D return this; } + public TypeQueryBuilder Where(Expression>> fieldSelector, DateFilterOperators filterOperator) + { + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + Where(fieldSelector.GetFieldPath(), filterOperator); + + return this; + } public TypeQueryBuilder Where(IFilter filter) { filter.ValidateNotNullArgument("filter"); - if (graphObject.WhereClause.IsNullOrEmpty()) - { - graphObject.WhereClause = $"{filter.FilterClause}"; - } - else + SetWhereClause(filter.FilterClause); + return this; + } + //Support filter property of IEnumerable with filter extension + public TypeQueryBuilder Where(Expression>> enumSelector, Expression> fieldSelector) + { + enumSelector.ValidateNotNullArgument("enumSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + + var enumPath = enumSelector.GetFieldPath(); + var paser = new FilterExpressionParser(); + var filter = paser.GetFilter(fieldSelector); + if (filter.IsNotNull()) { - graphObject.WhereClause += $",{filter.FilterClause}"; + var fieldFilter = filter.FilterClause; + var combineFilter = $"{enumPath}:{{{fieldFilter}}}"; + SetWhereClause(combineFilter); } + return this; + } + //Support filter property of IEnumerable with filter operator + public TypeQueryBuilder Where(Expression>> enumSelector, + Expression> fieldSelector, IFilterOperator filterOperator) + { + enumSelector.ValidateNotNullArgument("enumSelector"); + fieldSelector.ValidateNotNullArgument("fieldSelector"); + filterOperator.ValidateNotNullArgument("filterOperator"); + + var enumPath = enumSelector.GetFieldPath(); + var fullFieldPath = $"{enumPath}.{fieldSelector.GetFieldPath()}"; + Where(fullFieldPath, filterOperator); + return this; + } + //Support raw string field path + public TypeQueryBuilder Where(string fieldPath, IFilterOperator filterOperator) + { + fieldPath.ValidateNotNullArgument("fieldPath"); + filterOperator.ValidateNotNullArgument("filterOperator"); + 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) @@ -645,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..6f1b80df 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))); @@ -66,5 +87,37 @@ public static DelegateFacetFilterBuilder FacetInRange(this IEnumerable fi { return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges((from, to)))); } + public static DelegateFacetFilterBuilder FacetInRanges(this float field, params (float? from, float? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this IEnumerable field, params (float? from, float? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this double field, params (double? from, double? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this IEnumerable field, params (double? from, double? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this int field, params (int? from, int? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this IEnumerable field, params (int? from, int? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this long field, params (long? from, long? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } + public static DelegateFacetFilterBuilder FacetInRanges(this IEnumerable field, params (long? from, long? to)[] ranges) + { + return new DelegateFacetFilterBuilder(field => new TermFacetFilter(field, new NumericFacetFilterOperators().Ranges(ranges))); + } } } diff --git a/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtension.cs b/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtension.cs index 9cc9d9d6..3cb6641d 100644 --- a/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtension.cs +++ b/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtension.cs @@ -188,16 +188,6 @@ public static DelegateFilterBuilder InRange(this int field, int from, int to) { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from,to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this int field, params (int? from, int? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Long type @@ -248,16 +238,6 @@ public static DelegateFilterBuilder InRange(this long field, int from, int to) { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this long field, params (int? from, int? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Float type @@ -308,16 +288,6 @@ public static DelegateFilterBuilder InRange(this float field, int from, int to) { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this float field, params (float? from, float? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Double type @@ -368,16 +338,6 @@ public static DelegateFilterBuilder InRange(this double field, int from, int to) { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this double field, params (float? from, float? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #endregion diff --git a/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtensionIEnumerable.cs b/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtensionIEnumerable.cs index 4416c8be..0b3a55a7 100644 --- a/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtensionIEnumerable.cs +++ b/APIs/src/EpiServer.ContentGraph/Extensions/FilterExtensionIEnumerable.cs @@ -142,16 +142,6 @@ public static DelegateFilterBuilder InRange(this IEnumerable field, int fro { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this IEnumerable field, params (int? from, int? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Long type @@ -202,16 +192,6 @@ public static DelegateFilterBuilder InRange(this IEnumerable field, int fr { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this IEnumerable field, params (int? from, int? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Float type @@ -262,16 +242,6 @@ public static DelegateFilterBuilder InRange(this IEnumerable field, int f { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this IEnumerable field, params (float? from, float? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #region Double type @@ -322,16 +292,6 @@ public static DelegateFilterBuilder InRange(this IEnumerable field, int { return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRange(from, to))); } - /// - /// Multiple ranges for InRange filter. - /// - /// - /// Array of tuples (from,to) - /// - public static DelegateFilterBuilder InRanges(this IEnumerable field, params (float? from, float? to)[] ranges) - { - return new DelegateFilterBuilder(field => new TermFilter(field, new NumericFilterOperators().InRanges(ranges))); - } #endregion #endregion diff --git a/APIs/src/Templates/Alloy/Controllers/SearchPageController.cs b/APIs/src/Templates/Alloy/Controllers/SearchPageController.cs index 65e3b087..27cb1c2e 100644 --- a/APIs/src/Templates/Alloy/Controllers/SearchPageController.cs +++ b/APIs/src/Templates/Alloy/Controllers/SearchPageController.cs @@ -25,8 +25,9 @@ public ViewResult Index(SearchPage currentPage, string q, string t, string p = " .Skip((int.Parse(p) -1) * 10) .Limit(10) .Fields(x=>x.Name, x=> x.Url) + .NestedFields(x=> x.ExistingLanguages, x=> x.Name) .Total() - .AsType(x=>x.MetaDescription, x=> x.MetaTitle) + .InlineFragment(x=>x.MetaDescription, x=> x.MetaTitle) .Search(q) .FilterForVisitor() .Facet(x=>x.ContentType.FacetFilters(t)) diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/EpiServer.ContentGraph.UnitTests.csproj b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/EpiServer.ContentGraph.UnitTests.csproj index 88601790..2e5980b5 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/EpiServer.ContentGraph.UnitTests.csproj +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/EpiServer.ContentGraph.UnitTests.csproj @@ -7,12 +7,15 @@ + + + all runtime; build; native; contentfiles; analyzers; buildtransitive 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..c497b14b --- /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{NestedObjects{NestedProperty{name count}}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.NestedObjects, 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{NestedObjects{NestedProperty(limit: 10){name count}}}"; + const string expectedFullQuery = $"RequestTypeObject{{{expectedFields} {expectedFacet}}}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Facet(x => x.NestedObjects, 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/ExtensionTests/QueryExtensionTests.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/QueryExtensionTests.cs index b1d4eaf4..47aeb92f 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/QueryExtensionTests.cs +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/ExtensionTests/QueryExtensionTests.cs @@ -66,6 +66,23 @@ public void GetScore_should_build_query_with_score_field() typeQueryBuilder.GetScore(); var query = typeQueryBuilder.ToQuery().GetQuery(); + Assert.NotNull(query); + Assert.Contains(expectedFields, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + [Fact] + public void generate_where_with_IEnumerable() + { + const string expectedFields = "{items{Property1 _score}}"; + const string expectedFilter = "where:{NestedObjects:{NestedProperty:{eq: 100}}}"; + const string expectedFullQuery = $"RequestTypeObject({expectedFilter}){expectedFields}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.GetScore(); + typeQueryBuilder.Where(x => x.NestedObjects, f => f.NestedProperty.Eq(100)); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + Assert.NotNull(query); Assert.Contains(expectedFields, query.Query); Assert.Equal(query.Query, expectedFullQuery); diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateFilterTests.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateFilterTests.cs index 56d850dd..f9e14179 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateFilterTests.cs +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateFilterTests.cs @@ -103,5 +103,37 @@ public void SingleFieldWithNotFiltersTest() Assert.Contains(expectedFilters, query); Assert.Equal($"{type}{expectedFilters}{items}", query); } + [Fact] + public void generate_where_with_raw_string() + { + const string expectedFields = "{items{Property1}}"; + const string expectedFilter = "where:{Nesteds:{NestedProperty:{eq: 100}}}"; + const string expectedFullQuery = $"RequestTypeObject({expectedFilter}){expectedFields}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Where("Nesteds.NestedProperty", new NumericFilterOperators().Eq(100)); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFields, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } + [Fact] + public void generate_where_with_IFilterOperator() + { + const string expectedFields = "{items{Property1}}"; + const string expectedFilter = "where:{NestedObjects:{NestedProperty:{eq: 100}}}"; + const string expectedFullQuery = $"RequestTypeObject({expectedFilter}){expectedFields}"; + + typeQueryBuilder.Field(x => x.Property1); + typeQueryBuilder.Where(x => x.NestedObjects, f => f.NestedProperty, new NumericFilterOperators().Eq(100)); + + var query = typeQueryBuilder.ToQuery().GetQuery(); + + Assert.NotNull(query); + Assert.Contains(expectedFields, query.Query); + Assert.Equal(query.Query, expectedFullQuery); + } } } diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateQueryTests.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateQueryTests.cs index 2f50767d..db841d7a 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateQueryTests.cs +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/GenerateQueryTests.cs @@ -26,6 +26,7 @@ public void SelectNoneOfFieldsShouldThrowException() } [Fact] + [Category("Select Fields")] public void SelectFields() { string expectedFields = @"{items{Property1 Property2}}"; @@ -35,10 +36,24 @@ public void SelectFields() Assert.NotNull(query.GetQuery()); //check selected fields Assert.Contains(expectedFields, query.GetQuery().Query); - query = query.BuildQueries(); } [Fact] + [Category("Select Fields")] + public void SelectFieldsOfEnumerable() + { + string expectedFields = @"{items{Property1 Property2 NestedObjects{NestedProperty}}}"; + typeQueryBuilder.Fields(x => x.Property1, x => x.Property2); + typeQueryBuilder.NestedFields(x => x.NestedObjects, f => f.NestedProperty); + GraphQueryBuilder query = typeQueryBuilder.ToQuery(); + + Assert.NotNull(query.GetQuery()); + //check selected fields + Assert.Contains(expectedFields, query.GetQuery().Query); + } + + [Fact] + [Category("Select Fields")] public void SelectFieldWithAlias() { string expectedFields = @"{items{property1:Property1 property2:Property2}}"; @@ -52,6 +67,7 @@ public void SelectFieldWithAlias() } [Fact] + [Category("Select Fields")] public void SelectNestedFields() { string expectedFields = @"{items{Property1 Property2 Property3{NestedProperty}}}"; 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; } } } diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/RequestTypeObject.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/RequestTypeObject.cs index a96dca3e..585c3965 100644 --- a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/RequestTypeObject.cs +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/QueryTypeObjects/RequestTypeObject.cs @@ -11,5 +11,6 @@ internal class RequestTypeObject public string Property1 { get; set; } public int Property2 { get; set; } public NestedObject Property3 { get; set; } + public IEnumerable NestedObjects { get; set; } } } diff --git a/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/Startup.cs b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/Startup.cs new file mode 100644 index 00000000..f010e67a --- /dev/null +++ b/APIs/src/Testing/EpiServer.ContentGraph.UnitTests/Startup.cs @@ -0,0 +1,17 @@ +using EPiServer.ContentGraph.Connection; +using EPiServer.ServiceLocation; +using Microsoft.Extensions.DependencyInjection; + +namespace EpiServer.ContentGraph.UnitTests +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddMemoryCache(); + services.AddSingleton(); + var provider = services.BuildServiceProvider(); + CacheAccessor.Cache = provider.GetService(); + } + } +} diff --git a/msbuild/version.props b/msbuild/version.props index 3b9cac61..0c0bd6d5 100644 --- a/msbuild/version.props +++ b/msbuild/version.props @@ -1,7 +1,7 @@ - 1.2.0 + 1.3.0 \ No newline at end of file