Skip to content

Commit fcf4771

Browse files
elit0451kjac
andauthored
Delivery API: Adding support for "Contains" filters (#14289)
* Adding support for contains filters * Making a dedicated name filter and making changes to the sort indexer * Fixed the merge from release/12.0 * Make the "name" filter support OR for comma separated filter values --------- Co-authored-by: kjac <[email protected]>
1 parent 53e0227 commit fcf4771

File tree

5 files changed

+55
-16
lines changed

5 files changed

+55
-16
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
using Umbraco.Cms.Core.DeliveryApi;
2+
using Umbraco.Cms.Core.Models;
3+
4+
namespace Umbraco.Cms.Api.Delivery.Indexing.Filters;
5+
6+
public sealed class NameFilterIndexer : IContentIndexHandler
7+
{
8+
internal const string FieldName = "name";
9+
10+
public IEnumerable<IndexFieldValue> GetFieldValues(IContent content, string? culture)
11+
=> new[] { new IndexFieldValue { FieldName = FieldName, Values = new object[] { content.GetCultureName(culture) ?? string.Empty } } };
12+
13+
public IEnumerable<IndexField> GetFields()
14+
=> new[] { new IndexField { FieldName = FieldName, FieldType = FieldType.StringAnalyzed, VariesByCulture = true } };
15+
}

src/Umbraco.Cms.Api.Delivery/Indexing/Sorts/NameSortIndexer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace Umbraco.Cms.Api.Delivery.Indexing.Sorts;
55

66
public sealed class NameSortIndexer : IContentIndexHandler
77
{
8-
internal const string FieldName = "name";
8+
internal const string FieldName = "sortName";
99

1010
public IEnumerable<IndexFieldValue> GetFieldValues(IContent content, string? culture)
1111
=> new[] { new IndexFieldValue { FieldName = FieldName, Values = new object[] { content.GetCultureName(culture) ?? string.Empty } } };
Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
using Umbraco.Cms.Api.Delivery.Indexing.Sorts;
1+
using Umbraco.Cms.Api.Delivery.Indexing.Filters;
2+
using Umbraco.Cms.Core;
23
using Umbraco.Cms.Core.DeliveryApi;
3-
using Umbraco.Extensions;
44

55
namespace Umbraco.Cms.Api.Delivery.Querying.Filters;
66

@@ -16,16 +16,16 @@ public bool CanHandle(string query)
1616
public FilterOption BuildFilterOption(string filter)
1717
{
1818
var value = filter.Substring(NameSpecifier.Length);
19+
var negate = value.StartsWith('!');
20+
var values = value.TrimStart('!').Split(Constants.CharArrays.Comma);
1921

2022
return new FilterOption
2123
{
22-
FieldName = NameSortIndexer.FieldName,
23-
Values = value.IsNullOrWhiteSpace() == false
24-
? new[] { value.TrimStart('!') }
25-
: Array.Empty<string>(),
26-
Operator = value.StartsWith('!')
27-
? FilterOperation.IsNot
28-
: FilterOperation.Is
24+
FieldName = NameFilterIndexer.FieldName,
25+
Values = values,
26+
Operator = negate
27+
? FilterOperation.DoesNotContain
28+
: FilterOperation.Contains
2929
};
3030
}
3131
}

src/Umbraco.Cms.Api.Delivery/Services/ApiContentQueryProvider.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Examine;
2+
using Examine.Lucene.Providers;
3+
using Examine.Lucene.Search;
24
using Examine.Search;
35
using Microsoft.Extensions.Logging;
46
using Umbraco.Cms.Core;
@@ -77,7 +79,14 @@ public PagedModel<Guid> ExecuteQuery(SelectorOption selectorOption, IList<Filter
7779

7880
private IBooleanOperation BuildSelectorOperation(SelectorOption selectorOption, IIndex index, string culture)
7981
{
80-
IQuery query = index.Searcher.CreateQuery();
82+
// Needed for enabling leading wildcards searches
83+
BaseLuceneSearcher searcher = index.Searcher as BaseLuceneSearcher ?? throw new InvalidOperationException($"Index searcher must be of type {nameof(BaseLuceneSearcher)}.");
84+
85+
IQuery query = searcher.CreateQuery(
86+
IndexTypes.Content,
87+
BooleanOperation.And,
88+
searcher.LuceneAnalyzer,
89+
new LuceneSearchOptions { AllowLeadingWildcard = true });
8190

8291
IBooleanOperation selectorOperation = selectorOption.Values.Length == 1
8392
? query.Field(selectorOption.FieldName, selectorOption.Values.First())
@@ -103,6 +112,23 @@ void HandleExact(IQuery query, string fieldName, string[] values)
103112
}
104113
}
105114

115+
void HandleContains(IQuery query, string fieldName, string[] values)
116+
{
117+
if (values.Length == 1)
118+
{
119+
// The trailing wildcard is added automatically
120+
query.Field(fieldName, (IExamineValue)new ExamineValue(Examineness.ComplexWildcard, $"*{values[0]}"));
121+
}
122+
else
123+
{
124+
// The trailing wildcard is added automatically
125+
IExamineValue[] examineValues = values
126+
.Select(value => (IExamineValue)new ExamineValue(Examineness.ComplexWildcard, $"*{value}"))
127+
.ToArray();
128+
query.GroupedOr(new[] { fieldName }, examineValues);
129+
}
130+
}
131+
106132
foreach (FilterOption filterOption in filterOptions)
107133
{
108134
var values = filterOption.Values.Any()
@@ -112,18 +138,16 @@ void HandleExact(IQuery query, string fieldName, string[] values)
112138
switch (filterOption.Operator)
113139
{
114140
case FilterOperation.Is:
115-
// TODO: test this for explicit word matching
116141
HandleExact(queryOperation.And(), filterOption.FieldName, values);
117142
break;
118143
case FilterOperation.IsNot:
119-
// TODO: test this for explicit word matching
120144
HandleExact(queryOperation.Not(), filterOption.FieldName, values);
121145
break;
122-
// TODO: Fix
123146
case FilterOperation.Contains:
147+
HandleContains(queryOperation.And(), filterOption.FieldName, values);
124148
break;
125-
// TODO: Fix
126149
case FilterOperation.DoesNotContain:
150+
HandleContains(queryOperation.Not(), filterOption.FieldName, values);
127151
break;
128152
default:
129153
continue;

src/Umbraco.Infrastructure/Examine/DeliveryApiContentIndexFieldDefinitionBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ private static FieldDefinition CreateFieldDefinition(IndexField field)
6868
FieldType.Number => FieldDefinitionTypes.Integer,
6969
FieldType.StringRaw => FieldDefinitionTypes.Raw,
7070
FieldType.StringAnalyzed => FieldDefinitionTypes.FullText,
71-
FieldType.StringSortable => FieldDefinitionTypes.FullTextSortable,
71+
FieldType.StringSortable => FieldDefinitionTypes.InvariantCultureIgnoreCase,
7272
_ => throw new ArgumentOutOfRangeException(nameof(field.FieldType))
7373
};
7474

0 commit comments

Comments
 (0)