Good day, everyone! I have an indexed object Topic with mapping:
"topic": {
"properties": {
"authorName": {
"type": "string"
},
"categoryId": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
I use NEST 2.0 to search between my indexed Topics with the C# query:
var searchEngineResult = await
ElasticClient.SearchAsync(
r => r.AllIndices()
.Source(false)
.Query(q => q.Match(m => m.Field(AllFieldsToken).Operator(Operator.And).Query(query)))
);
It works perfectly, but I need to filter search results on the categoryId collection.
For example:
there is 3 indexed topics:
- authorName = "Some guy", categoryId = "cat2", description = "some toric";
- authorName = "Second guy", categoryId = "cat1", description = "some other topic";
- authorName = "Third dude", categoryId = "cat2", description = "third topic";
- authorName = "Fourth guy", categoryId = "cat1", description = "topic topic";
String[] categoryId = new[] {"cat1, "cat2" };
If I will search for "guy topic" I can obtain topics 1, 2 and 4.
And now I need to filter my search result with the categoryId.
There can be multiple comma separated categoryIds for filtering.
i.e.
if I will filter with "cat2" I expect topic 1.
if I will filter with "cat1" I expect topics 2 and 4.
if I will filter with "cat1,cat2" I expect topics 1, 2 and 4
In the mean time I need to filter all of the topics without query
How can I implement this?
In other words: How can I implement this with NEST:
{
"query": {
"constant_score" : {
"filter" : {
"bool": {
"must" : {
"term" : {
"_all" : "guy topic"
}
},
"should" : [
{
"term" : {
"categoryId" : "cat1"
}
},
{
"term" : {
"categoryId" : "cat2"
}
}
]
}
}
}
}
}
When a bool clause with a must has should clauses the minimum_should_match
defaults to 0 meaning that that query will return all documents matching guy topic
and if they happen to contain catagoryId: "cat1"
or catagoryId: "cat2"
these documents will score better then other documents containing guy topic
. Other documents with different catagories will still be returned. This is due to the unary nature of boolean queries in Elasticsearch.
The query you posted translates to +"guy topic" catagoryId:cat1 catagoryId:cat2
not "guy topic" AND (catagoryId:cat1 OR catagoryId:cat2)
In NEST you can use bitwise operators to construct true boolean queries.
q=>q.Term("_all", "guy topic")
&& (q.Term("catagoryId", "cat1") || q.Term("catagoryId", "cat1"))
Which will translate to a bool query with 2 must clauses, one being the _all term query and the other being a nested bool with only should clauses (the two term queries). When a boolean query has only should clauses the minimum_should_match
defaults to 1.
Also do checkout the terms query
Thanks for the reply! Your suggestion is works as expected, thank you! But I need to implement this search request with unknown categoryId count. I mean I need to FOREACH them somehow and add them to the query
Tjis is how I worked it out:
var boolQuery = new BoolQuery();
boolQuery.Must = new[] { new QueryContainerDescriptor<Object>().Match(m => m.Field(AllFieldsToken).Operator(Operator.And).Query(query)) };
if (searchRequest.CategoryIds.Count() > 0)
{
List<QueryContainer> shouldContainer = new List<QueryContainer>();
foreach (var catId in searchRequest.CategoryIds)
{
shouldContainer.Add(new QueryContainerDescriptor<Object>().Match(m => m.Field("categoryId").Query(catId)));
}
boolQuery.Should = shouldContainer.ToArray();
}
boolQuery.MinimumShouldMatch = new MinimumShouldMatch(1);
and then the query:
var searchResult = ElasticClient.....Query(q => boolQuery)