Skip to content

Commit

Permalink
Search enhancement: pinned queries (#4143)
Browse files Browse the repository at this point in the history
* Search enhancement: pinned queries

Addresses elastic/elasticsearch#44345

* update docs in test

* remove params overloads on Ids that already convert to Ids

* add note to IdsQuery
  • Loading branch information
Mpdreamz committed Oct 15, 2019
1 parent d893f06 commit 2c125f3
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Nest/QueryDsl/Abstractions/Container/IQueryContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,11 @@ public interface IQueryContainer
[DataMember(Name = "distance_feature")]
IDistanceFeatureQuery DistanceFeature { get; set; }

/// <inheritdoc cref="IPinnedQuery"/>
[DataMember(Name = "pinned")]
IPinnedQuery Pinned { get; set; }


void Accept(IQueryVisitor visitor);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public partial class QueryContainer : IQueryContainer, IDescriptor
private ITermsSetQuery _termsSet;
private IWildcardQuery _wildcard;
private IRankFeatureQuery _rankFeature;
private IPinnedQuery _pinned;

[IgnoreDataMember]
private IQueryContainer Self => this;
Expand Down Expand Up @@ -360,6 +361,12 @@ IRankFeatureQuery IQueryContainer.RankFeature
get => _rankFeature;
set => _rankFeature = Set(value);
}
IPinnedQuery IQueryContainer.Pinned
{
get => _pinned;
set => _pinned = Set(value);
}


private T Set<T>(T value) where T : IQuery
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -475,5 +475,8 @@ public QueryContainer Conditionless(Func<ConditionlessQueryDescriptor<T>, ICondi
/// </summary>
public QueryContainer TermsSet(Func<TermsSetQueryDescriptor<T>, ITermsSetQuery> selector) =>
WrapInContainer(selector, (query, container) => container.TermsSet = query);

public QueryContainer Pinned(Func<PinnedQueryDescriptor<T>, IPinnedQuery> selector) =>
WrapInContainer(selector, (query, container) => container.Pinned = query);
}
}
4 changes: 4 additions & 0 deletions src/Nest/QueryDsl/Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,9 @@ public static class Query<T> where T : class

public static QueryContainer Wildcard(Func<WildcardQueryDescriptor<T>, IWildcardQuery> selector) =>
new QueryContainerDescriptor<T>().Wildcard(selector);

public static QueryContainer Pinned(Func<PinnedQueryDescriptor<T>, IPinnedQuery> selector) =>
new QueryContainerDescriptor<T>().Pinned(selector);

}
}
55 changes: 55 additions & 0 deletions src/Nest/QueryDsl/Specialized/Pinned/PinnedQuery.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using Elasticsearch.Net.Utf8Json;

namespace Nest
{
[InterfaceDataContract]
[ReadAs(typeof(PinnedQuery))]
public interface IPinnedQuery : IQuery
{
[DataMember(Name = "ids")]
IEnumerable<Id> Ids { get; set; }

[DataMember(Name = "organic")]
QueryContainer Organic { get; set; }
}

public class PinnedQuery : QueryBase, IPinnedQuery
{
public IEnumerable<Id> Ids { get; set; }

public QueryContainer Organic { get; set; }

protected override bool Conditionless => IsConditionless(this);

internal override void InternalWrapInContainer(IQueryContainer c) => c.Pinned = this;

internal static bool IsConditionless(IPinnedQuery q) => !q.Ids.HasAny() && q.Organic.IsConditionless();
}

public class PinnedQueryDescriptor<T>
: QueryDescriptorBase<PinnedQueryDescriptor<T>, IPinnedQuery>
, IPinnedQuery
where T : class
{
protected override bool Conditionless => PinnedQuery.IsConditionless(this);
IEnumerable<Id> IPinnedQuery.Ids { get; set; }
QueryContainer IPinnedQuery.Organic { get; set; }

public PinnedQueryDescriptor<T> Ids(params Id[] ids) => Assign(ids, (a, v) => a.Ids = v);

public PinnedQueryDescriptor<T> Ids(IEnumerable<Id> ids) => Ids(ids?.ToArray());

public PinnedQueryDescriptor<T> Ids(IEnumerable<string> ids) => Ids(ids.ToArray());

public PinnedQueryDescriptor<T> Ids(IEnumerable<long> ids) => Ids(ids.ToArray());

public PinnedQueryDescriptor<T> Ids(IEnumerable<Guid> ids) => Ids(ids.ToArray());

public PinnedQueryDescriptor<T> Organic(Func<QueryContainerDescriptor<T>, QueryContainer> selector) =>
Assign(selector, (a, v) => a.Organic = v?.Invoke(new QueryContainerDescriptor<T>()));
}
}
1 change: 1 addition & 0 deletions src/Nest/QueryDsl/TermLevel/Ids/IdsQuery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class IdsQueryDescriptor

public IdsQueryDescriptor Values(IEnumerable<Id> values) => Values(values?.ToArray());

// TODO 8.x remove params on Values already implicitly converting to Id
public IdsQueryDescriptor Values(params string[] values) => Assign(values?.Select(v => (Id)v), (a, v) => a.Values = v);

public IdsQueryDescriptor Values(IEnumerable<string> values) => Values(values.ToArray());
Expand Down
2 changes: 2 additions & 0 deletions src/Nest/QueryDsl/Visitor/DslPrettyPrintVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,8 @@ public virtual void Visit(IGeoShapeQuery query)

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

public virtual void Visit(IPinnedQuery query) => Write("pinned");

private void Write(string queryType, Dictionary<string, string> properties)
{
properties = properties ?? new Dictionary<string, string>();
Expand Down
4 changes: 4 additions & 0 deletions src/Nest/QueryDsl/Visitor/QueryVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@ public interface IQueryVisitor
void Visit(IParentIdQuery query);

void Visit(ITermsSetQuery query);

void Visit(IPinnedQuery query);
}

public class QueryVisitor : IQueryVisitor
Expand Down Expand Up @@ -271,6 +273,8 @@ public class QueryVisitor : IQueryVisitor

public virtual void Visit(ITermsSetQuery query) { }

public virtual void Visit(IPinnedQuery query) { }

public virtual void Visit(IQueryVisitor visitor) { }
}
}
1 change: 1 addition & 0 deletions src/Nest/QueryDsl/Visitor/QueryWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public void Walk(IQueryContainer qd, IQueryVisitor visitor)
VisitQuery(qd.Percolate, visitor, (v, d) => v.Visit(d));
VisitQuery(qd.ParentId, visitor, (v, d) => v.Visit(d));
VisitQuery(qd.TermsSet, visitor, (v, d) => v.Visit(d));
VisitQuery(qd.Pinned, visitor, (v, d) => v.Visit(d));

VisitQuery(qd.Bool, visitor, (v, d) =>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System;
using Nest;
using Tests.Core.ManagedElasticsearch.Clusters;
using Tests.Domain;
using Tests.Framework.EndpointTests.TestState;

#pragma warning disable 618 //Testing an obsolete method

namespace Tests.QueryDsl.Specialized.Pinned
{
/**
* Promotes selected documents to rank higher than those matching a given query. This feature is typically used to
* guide searchers to curated documents that are promoted over and above any "organic" matches for a search. The promoted or "pinned"
* documents are identified using the document IDs stored in the _id field.
* See the Elasticsearch documentation on {ref_current}/query-dsl-pinned-query.html[pinned query] for more details.
*/
public class PinnedQueryUsageTests : QueryDslUsageTestsBase
{
public PinnedQueryUsageTests(ReadOnlyCluster i, EndpointUsage usage) : base(i, usage) { }

protected override ConditionlessWhen ConditionlessWhen => new ConditionlessWhen<IPinnedQuery>(a => a.Pinned)
{
q =>
{
q.Ids = null;
q.Organic = null;
},
q =>
{
q.Ids = Array.Empty<Id>();
q.Organic = ConditionlessQuery;
},
};

protected override NotConditionlessWhen NotConditionlessWhen => new NotConditionlessWhen<IPinnedQuery>(a => a.Pinned)
{
q => q.Organic = VerbatimQuery,
};

protected override QueryContainer QueryInitializer => new PinnedQuery()
{
Name = "named_query",
Boost = 1.1,
Organic = new MatchAllQuery { Name = "organic_query" },
Ids = new Id[] { 1,11,22 },
};

protected override object QueryJson => new
{
pinned = new
{
_name = "named_query",
boost = 1.1,
organic = new
{
match_all = new { _name = "organic_query" }
},
ids = new [] { 1, 11, 22},
}
};

protected override QueryContainer QueryFluent(QueryContainerDescriptor<Project> q) => q
.Pinned(c => c
.Name("named_query")
.Boost(1.1)
.Organic(qq => qq.MatchAll(m => m.Name("organic_query")))
.Ids(1, 11, 22)
);
}
}

0 comments on commit 2c125f3

Please sign in to comment.