Convert DSL Query To NEST Query - elasticsearch

My DSL query is below :
{
"from": 0,
"size": 200,
"query": {
"filtered": {
"filter": {
"bool": {
"must": {
"query": {
"match": {
"contactId": {
"query": "e84aca88-7b82-43d9-8788-4cc25af0c43a",
"type": "phrase"
}
}
}
}
}
}
}
}
}
I tried to convert dsl query to NEST query.
var descriptor =
new SearchDescriptor<dynamic>().From(0)
.Size(200)
.Query(
a =>
a.Filtered(
b =>
b.Filter(
c =>
c.Bool(
d =>
d.Must(
e =>
e.Query(
f =>
f.Match(
g =>
g.OnField("contactId")
.Query(
"e84aca88-7b82-43d9-8788-4cc25af0c43a"))))))));
string result = System.Text.Encoding.Default.GetString(client.Serializer.Serialize(descriptor));
I examined the json results with the help of SearchDescriptor. There were differences :
{
"from": 0,
"size": 200,
"query":
{
"filtered":
{
"filter": {
"bool":
{
"must": [
{
"fquery":
{
"query":
{
"match":
{
"contactId":
{
"query": "e84aca88-7b82-43d9-8788-4cc25af0c43a"
}
}
}
}
}]
}
}
}
}
}
As you can see fquery added but i didnt fquery my Nest query. Fquery why added? First DSL query is returning result but second DSL query is not returning any result

My updated code :
var searchResults =
client.Search<dynamic>(u=>u.From(0)
.Size(200)
.Query(
a =>
a.Filtered(
b =>
b.Filter(
c =>
c.Bool(
d =>
d.Must(
e =>
e.Query(
f =>
f.MatchPhrase(
g =>
g.OnField("contactId")
.Query(
"e84aca88-7b82-43d9-8788-4cc25af0c43a")))))))));

Related

Complex nested Elastic NEST Query

How would I convert this to the equivalent Nest query?
{
"query": {
"nested": {
"path": "boundedContexts",
"query": {
"nested": {
"path": "boundedContexts.aggregateRoots",
"query": {
"nested": {
"path": "boundedContexts.aggregateRoots.modelMetaData",
"query": {
"bool": {
"must": [
{ "match": { "boundedContexts.aggregateRoots.modelMetaData.modelReferenceId": "4e7c5c0e-93a7-4bf6-9705-cf1327760e21" } },
{ "match": { "boundedContexts.aggregateRoots.modelMetaData.modelType.name": "AggregateRoot" } }
]
}
}
}
}
}
}
}
},
"size": 1,
"sort": [
{
"generatedDate": {
"order": "desc"
}
}
]
}
I am trying to generate an equivalent Nest query similar to this ..
ISearchResponse<ViewModels.DomainModel> response = null;
response = await _elasticClient.SearchAsync<ViewModels.DomainModel>(s => s.Index(_modelMetadataProvider.CurrentIndexName)
.Query(q => q.Nested(n1 => n1.Path("boundedContexts")
.Query(q2 => q2.Nested(n2 => n2.Path("boundedContexts.aggregateRoots")
.Query(q3 => q3.Nested(n3 => n3.Path("boundedContexts.aggregateRoots.modelMetaData").
Query(q4 =>q4.Bool(b => b.Must(bs =>
bs.Match(p => p.Field("boundedContexts.aggregateRoots.modelMetaData.modelReferenceId")))))))))))
.Size(1).Sort(s=>s.Descending("generatedDate")));
I am stuck on how to compare to a variable against at the p.Field("boundedContexts.aggregateRoots.modelMetaData.modelReferenceId") level of query.

Filter Creation

So after much experimentation I have found that the syntax I needed to filter products based on their specs is as follows
{
"post_filter": {
"bool": {
"filter": [{
"nested": {
"path": "productSpecification",
"query": {
"bool": {
"filter": [{
"term": {
"productSpecification.name": "Brand"
}
},
{
"terms": {
"productSpecification.value": [
"Brand1"
]
}
}
]
}
}
}
},
{
"nested": {
"path": "productSpecification",
"query": {
"bool": {
"filter": [{
"term": {
"productSpecification.name": "Guarantee"
}
},
{
"terms": {
"productSpecification.value": [
"3 years"
]
}
}
]
}
}
}
}
]
}
}
}
I'm now experimenting with creating a QueryContainer function to build this based on the selected name/values, If anyone can give me a point in the right direction on this that would be much appreciated.
Thanks
Ok I am now building a query from nest that works but is a bit messier than the DSL query above (not sure if someone can spot what Im doing below that makes it have some unnecessary bool filters
Here is my nest syntax
.PostFilter(pf => FilterTest(elasticParams,pf))
private static QueryContainer FilterTest(ElasticParams elasticParams, QueryContainerDescriptor<Product>q) => q
.Bool(b => b
.Filter(fi => FilterSelected(elasticParams, fi)));
private static QueryContainer FilterSelected2(ElasticParams elasticParams, QueryContainerDescriptor<Product> q) =>
elasticParams.Filters.Aggregate(new QueryContainer(), (c, s) => c && +q.Nested(n => n
.Path(p => p.ProductSpecification)
.Query(qq => qq
.Bool(bo => bo
.Filter(fi => fi
.Term(tt => tt
.Field(a => a.ProductSpecification.Suffix("name"))
.Value(s.name)) && fi
.Terms(ttt => ttt
.Field(fff => fff.ProductSpecification.Suffix("value"))
.Terms(s.Values))
)))));
As mentioned before this is filters as I would want however it DSL that it outputs is rather fugly as can be seen below
{
"post_filter": {
"bool": {
"filter": [{
"bool": {
"filter": [{
"nested": {
"query": {
"bool": {
"filter": [{
"bool": {
"must": [{
"term": {
"productSpecification.name": {
"value": "Brand"
}
}
}, {
"terms": {
"productSpecification.value": ["Brand1", "Brand2"]
}
}]
}
}]
}
},
"path": "productSpecification"
}
}, {
"nested": {
"query": {
"bool": {
"filter": [{
"bool": {
"must": [{
"term": {
"productSpecification.name": {
"value": "Guarantee"
}
}
}, {
"terms": {
"productSpecification.value": ["3 years"]
}
}]
}
}]
}
},
"path": "productSpecification"
}
}]
}
}]
}
}
}

Multiple should queries with must query

I am building a query to Elastic 5 (using nest in .net), i am trying to achive this result:
Must have value1 and value 2
Should have value3 or value 4
and should have value5 or value6
Here is my query:
{
"query": {
"bool": {
"must": [
{
"match": {
"code": {
"query": "value1"
}
}
},
{
"match": {
"code": {
"query": "value2"
}
}
}
],
"should": [
{
"match": {
"code": {
"query": "value3"
}
}
},
{
"match": {
"code": {
"query": "value4"
}
}
}
],
"should": [
{
"match": {
"code": {
"query": "value5"
}
}
},
{
"match": {
"code": {
"query": "value6"
}
}
}
],
"minimum_should_match": 1
}
}
}
I dont get the desired answer (for example i dont have anywhere value 5 and value 6 but still getting results)
Thank you
Then you need something like this:
{
"query": {
"bool": {
"must": [
{
"match": {
"code": {
"query": "value1"
}
}
},
{
"match": {
"code": {
"query": "value2"
}
}
},
{
"bool": {
"minimum_should_match": 1,
"should": [
{
"match": {
"code": {
"query": "value3"
}
}
},
{
"match": {
"code": {
"query": "value4"
}
}
}
]
}
},
{
"bool": {
"minimum_should_match": 1,
"should": [
{
"match": {
"code": {
"query": "value5"
}
}
},
{
"match": {
"code": {
"query": "value6"
}
}
}
]
}
}
]
}
}
}
Here's the NEST equivalent of Val's answer
void Main()
{
var client = new ElasticClient();
client.Search<MyDocument>(s => s
.Query(q => q
.Bool(b => b
.Must(mu => mu
.Match(m => m
.Field(f => f.Code)
.Query("value1")
), mu => mu
.Match(m => m
.Field(f => f.Code)
.Query("value2")
), mu => mu
.Bool(bb => bb
.MinimumShouldMatch(1)
.Should(sh => sh
.Match(m => m
.Field(f => f.Code)
.Query("value3")
), sh => sh
.Match(m => m
.Field(f => f.Code)
.Query("value4")
)
)
), mu => mu
.Bool(bb => bb
.MinimumShouldMatch(1)
.Should(sh => sh
.Match(m => m
.Field(f => f.Code)
.Query("value5")
), sh => sh
.Match(m => m
.Field(f => f.Code)
.Query("value6")
)
)
)
)
)
)
);
}
public class MyDocument
{
public string Code { get; set; }
}
Here's another example using more concrete sample data:
{
"query": {
"bool": {
"must": [
{
"match": {
"author": {
"query": "Anita Author"
}
}
},
{
"match": {
"author": {
"query": "Bertha Booster"
}
}
}
],
"should":
[
{
"match": {
"title": {
"query": "A Fantastic Book"
}
}
},
{
"match": {
"title": {
"query": "A Fantastic Book, Volume 2"
}
}
},
{
"match": {
"title": {
"query": "Yet Another Fantastic Book"
}
}
}
],
"minimum_should_match": 1
}
}
}

Nest query to write must and should in bool

I want to write query that will produce result like below. Bool query takes only a boolQueryDescriptior. I want to use two boolQueryDescriptors instead.
{
"query": {
"bool": {
"must": [
{
"term": {
"id": "idValue"
}
}
],
"should": [
{
"nested": {
"path": "nestedType",
"query": {
"match": {
"item2": "item2Value"
}
},
"inner_hits": {}
}
}
]
}
}
}
var results = client.Search<object>(sd => sd
.Query(q => q
.Bool(b => b
.Must(m => m
.Term("id", "idValue"))
.Should(s => s
.Nested(nq => nq
.Path("nestedType")
.Query(qd => qd
.Match(m => m
.OnField("item2")
.Query("item2Value")))
.InnerHits()))));

in NEST, how do I dynamically build a query from a list of terms?

Say my user provides a list of search terms which I've collected into an array/list, and now I want to combine those OR-wise into a NEST query using MatchPhrase. How would I do that? The code for a (single) search term would look something like this:
var search = client.Search<ElasticRequirement>(s => s
.Query(q =>
q.MatchPhrase(m => m.OnField(f => f.Title).Query(term.ToLower()).Slop(slop))
|| q.MatchPhrase(m => m.OnField(f => f.Description).Query(text).Slop(slop))
)
.LowercaseExpandedTerms()
.Explain()
.Query(q => q.Fuzzy(f => f.PrefixLength(1).OnField(c => c.Title).OnField(c => c.Description)))
);
This is fine, but I need to apply that same MatchPhrase filter once for each provided search term. Any help much appreciated.
You can use bool should expressions to build your query dynamically. I'll provide the complete solution below. Call BuildQuery() method with appropriate parameters.
ISearchResponse<ElasticRequirement> BuildQuery(IElasticClient client, IEnumerable<string> terms, int slop)
{
return client.Search<ElasticRequirement>(s => s
.Query(q => q
.Bool(b => b
.Should(terms.Select(t => BuildPhraseQueryContainer(q, t, slop)).ToArray())))
.LowercaseExpandedTerms()
.Explain()
.Query(q => q.Fuzzy(f => f.PrefixLength(1).OnField(c => c.Title).OnField(c => c.Description))));
}
QueryContainer BuildPhraseQueryContainer(QueryDescriptor<ElasticRequirement> qd, string term, int slop)
{
return qd.MatchPhrase(m => m.OnField(f => f.Title).Query(term.ToLower()).Slop(slop)) ||
qd.MatchPhrase(m => m.OnField(f => f.Description).Query(term.ToLower()).Slop(slop));
}
For terms = {"term1", "term2", "term3"} and slop = 0, the Elasticsearch search JSON command that will get built by my code is as under:
{
"explain": true,
"query": {
"bool": {
"should": [
{
"bool": {
"should": [
{
"match": {
"title": {
"type": "phrase",
"query": "term1",
"slop": 0
}
}
},
{
"match": {
"description": {
"type": "phrase",
"query": "term1",
"slop": 0
}
}
}
]
}
},
{
"bool": {
"should": [
{
"match": {
"title": {
"type": "phrase",
"query": "term2",
"slop": 0
}
}
},
{
"match": {
"description": {
"type": "phrase",
"query": "term2",
"slop": 0
}
}
}
]
}
},
{
"bool": {
"should": [
{
"match": {
"title": {
"type": "phrase",
"query": "term3",
"slop": 0
}
}
},
{
"match": {
"description": {
"type": "phrase",
"query": "term3",
"slop": 0
}
}
}
]
}
}
]
}
}
}
You can tweak this code such that all the match commands are under the same should node. I'll leave that up to you to figure out :)

Resources