Filter Query result by collection


(Andrey Kaprov) #1

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:

  1. authorName = "Some guy", categoryId = "cat2", description = "some toric";
  2. authorName = "Second guy", categoryId = "cat1", description = "some other topic";
  3. authorName = "Third dude", categoryId = "cat2", description = "third topic";
  4. 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?


(Andrey Kaprov) #2

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"
}
}
]
}
}
}
}
}


(Martijn Laarman) #3

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


(Andrey Kaprov) #4

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


(Andrey Kaprov) #5

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)

(system) #6