Skip to content

Commit 1194b38

Browse files
committed
Search enhancement: pinned queries
Addresses elastic/elasticsearch#44345
1 parent 28444dd commit 1194b38

File tree

9 files changed

+154
-0
lines changed

9 files changed

+154
-0
lines changed

src/Nest/QueryDsl/Abstractions/Container/IQueryContainer.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,11 @@ public interface IQueryContainer
171171
[DataMember(Name = "distance_feature")]
172172
IDistanceFeatureQuery DistanceFeature { get; set; }
173173

174+
/// <inheritdoc cref="IPinnedQuery"/>
175+
[DataMember(Name = "pinned")]
176+
IPinnedQuery Pinned { get; set; }
177+
178+
174179
void Accept(IQueryVisitor visitor);
175180
}
176181
}

src/Nest/QueryDsl/Abstractions/Container/QueryContainer-Assignments.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ public partial class QueryContainer : IQueryContainer, IDescriptor
5757
private ITermsSetQuery _termsSet;
5858
private IWildcardQuery _wildcard;
5959
private IRankFeatureQuery _rankFeature;
60+
private IPinnedQuery _pinned;
6061

6162
[IgnoreDataMember]
6263
private IQueryContainer Self => this;
@@ -360,6 +361,12 @@ IRankFeatureQuery IQueryContainer.RankFeature
360361
get => _rankFeature;
361362
set => _rankFeature = Set(value);
362363
}
364+
IPinnedQuery IQueryContainer.Pinned
365+
{
366+
get => _pinned;
367+
set => _pinned = Set(value);
368+
}
369+
363370

364371
private T Set<T>(T value) where T : IQuery
365372
{

src/Nest/QueryDsl/Abstractions/Container/QueryContainerDescriptor.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -475,5 +475,8 @@ public QueryContainer ParentId(Func<ParentIdQueryDescriptor<T>, IParentIdQuery>
475475
/// </summary>
476476
public QueryContainer TermsSet(Func<TermsSetQueryDescriptor<T>, ITermsSetQuery> selector) =>
477477
WrapInContainer(selector, (query, container) => container.TermsSet = query);
478+
479+
public QueryContainer Pinned(Func<PinnedQueryDescriptor<T>, IPinnedQuery> selector) =>
480+
WrapInContainer(selector, (query, container) => container.Pinned = query);
478481
}
479482
}

src/Nest/QueryDsl/Query.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,5 +190,9 @@ public static QueryContainer Wildcard(Field field, string value, double? boost =
190190

191191
public static QueryContainer Wildcard(Func<WildcardQueryDescriptor<T>, IWildcardQuery> selector) =>
192192
new QueryContainerDescriptor<T>().Wildcard(selector);
193+
194+
public static QueryContainer Pinned(Func<PinnedQueryDescriptor<T>, IPinnedQuery> selector) =>
195+
new QueryContainerDescriptor<T>().Pinned(selector);
196+
193197
}
194198
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.Serialization;
5+
using Elasticsearch.Net.Utf8Json;
6+
7+
namespace Nest
8+
{
9+
[InterfaceDataContract]
10+
[ReadAs(typeof(PinnedQuery))]
11+
public interface IPinnedQuery : IQuery
12+
{
13+
[DataMember(Name = "ids")]
14+
IEnumerable<Id> Ids { get; set; }
15+
16+
[DataMember(Name = "organic")]
17+
QueryContainer Organic { get; set; }
18+
}
19+
20+
public class PinnedQuery : QueryBase, IPinnedQuery
21+
{
22+
public IEnumerable<Id> Ids { get; set; }
23+
24+
public QueryContainer Organic { get; set; }
25+
26+
protected override bool Conditionless => IsConditionless(this);
27+
28+
internal override void InternalWrapInContainer(IQueryContainer c) => c.Pinned = this;
29+
30+
internal static bool IsConditionless(IPinnedQuery q) => !q.Ids.HasAny() && q.Organic.IsConditionless();
31+
}
32+
33+
public class PinnedQueryDescriptor<T>
34+
: QueryDescriptorBase<PinnedQueryDescriptor<T>, IPinnedQuery>
35+
, IPinnedQuery
36+
where T : class
37+
{
38+
protected override bool Conditionless => PinnedQuery.IsConditionless(this);
39+
IEnumerable<Id> IPinnedQuery.Ids { get; set; }
40+
QueryContainer IPinnedQuery.Organic { get; set; }
41+
42+
public PinnedQueryDescriptor<T> Ids(params Id[] ids) => Assign(ids, (a, v) => a.Ids = v);
43+
44+
public PinnedQueryDescriptor<T> Ids(IEnumerable<Id> ids) => Ids(ids?.ToArray());
45+
46+
public PinnedQueryDescriptor<T> Ids(params string[] ids) => Assign(ids?.Select(v => (Id)v), (a, v) => a.Ids = v);
47+
48+
public PinnedQueryDescriptor<T> Ids(IEnumerable<string> ids) => Ids(ids.ToArray());
49+
50+
public PinnedQueryDescriptor<T> Ids(params long[] ids) => Assign(ids?.Select(v => (Id)v), (a, v) => a.Ids = v);
51+
52+
public PinnedQueryDescriptor<T> Ids(IEnumerable<long> ids) => Ids(ids.ToArray());
53+
54+
public PinnedQueryDescriptor<T> Ids(params Guid[] ids) => Assign(ids?.Select(v => (Id)v), (a, v) => a.Ids = v);
55+
56+
public PinnedQueryDescriptor<T> Ids(IEnumerable<Guid> ids) => Ids(ids.ToArray());
57+
58+
public PinnedQueryDescriptor<T> Organic(Func<QueryContainerDescriptor<T>, QueryContainer> selector) =>
59+
Assign(selector, (a, v) => a.Organic = v?.Invoke(new QueryContainerDescriptor<T>()));
60+
}
61+
}

src/Nest/QueryDsl/Visitor/DslPrettyPrintVisitor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ public virtual void Visit(IGeoShapeQuery query)
203203

204204
public virtual void Visit(ITermsSetQuery query) => Write("terms_set");
205205

206+
public virtual void Visit(IPinnedQuery query) => Write("pinned");
207+
206208
private void Write(string queryType, Dictionary<string, string> properties)
207209
{
208210
properties = properties ?? new Dictionary<string, string>();

src/Nest/QueryDsl/Visitor/QueryVisitor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,8 @@ public interface IQueryVisitor
141141
void Visit(IParentIdQuery query);
142142

143143
void Visit(ITermsSetQuery query);
144+
145+
void Visit(IPinnedQuery query);
144146
}
145147

146148
public class QueryVisitor : IQueryVisitor
@@ -271,6 +273,8 @@ public virtual void Visit(IParentIdQuery query) { }
271273

272274
public virtual void Visit(ITermsSetQuery query) { }
273275

276+
public virtual void Visit(IPinnedQuery query) { }
277+
274278
public virtual void Visit(IQueryVisitor visitor) { }
275279
}
276280
}

src/Nest/QueryDsl/Visitor/QueryWalker.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ public void Walk(IQueryContainer qd, IQueryVisitor visitor)
5454
VisitQuery(qd.Percolate, visitor, (v, d) => v.Visit(d));
5555
VisitQuery(qd.ParentId, visitor, (v, d) => v.Visit(d));
5656
VisitQuery(qd.TermsSet, visitor, (v, d) => v.Visit(d));
57+
VisitQuery(qd.Pinned, visitor, (v, d) => v.Visit(d));
5758

5859
VisitQuery(qd.Bool, visitor, (v, d) =>
5960
{
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System;
2+
using Nest;
3+
using Tests.Core.ManagedElasticsearch.Clusters;
4+
using Tests.Domain;
5+
using Tests.Framework.EndpointTests.TestState;
6+
7+
#pragma warning disable 618 //Testing an obsolete method
8+
9+
namespace Tests.QueryDsl.Specialized.Pinned
10+
{
11+
/**
12+
* See the Elasticsearch documentation on {ref_current}/query-dsl-rank-feature-query.html[rank feature query] for more details.
13+
*/
14+
public class PinnedQueryUsageTests : QueryDslUsageTestsBase
15+
{
16+
public PinnedQueryUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }
17+
18+
protected override ConditionlessWhen ConditionlessWhen => new ConditionlessWhen<IPinnedQuery>(a => a.Pinned)
19+
{
20+
q =>
21+
{
22+
q.Ids = null;
23+
q.Organic = null;
24+
},
25+
q =>
26+
{
27+
q.Ids = Array.Empty<Id>();
28+
q.Organic = ConditionlessQuery;
29+
},
30+
};
31+
32+
protected override NotConditionlessWhen NotConditionlessWhen => new NotConditionlessWhen<IPinnedQuery>(a => a.Pinned)
33+
{
34+
q => q.Organic = VerbatimQuery,
35+
};
36+
37+
protected override QueryContainer QueryInitializer => new PinnedQuery()
38+
{
39+
Name = "named_query",
40+
Boost = 1.1,
41+
Organic = new MatchAllQuery { Name = "organic_query" },
42+
Ids = new Id[] { 1,11,22 },
43+
};
44+
45+
protected override object QueryJson => new
46+
{
47+
pinned = new
48+
{
49+
_name = "named_query",
50+
boost = 1.1,
51+
organic = new
52+
{
53+
match_all = new { _name = "organic_query" }
54+
},
55+
ids = new [] { 1, 11, 22},
56+
}
57+
};
58+
59+
protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
60+
.Pinned(c => c
61+
.Name("named_query")
62+
.Boost(1.1)
63+
.Organic(qq => qq.MatchAll(m => m.Name("organic_query")))
64+
.Ids(1, 11, 22)
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)