Nest Elastic - Building Dynamic Nested Query
Asked Answered
B

2

5

I have to query a nested object using Nest, however the query is built in dynamic way. Below is code that demonstrate using query on nested "books" in a static way

QueryContainer qry;
qry = new QueryStringQuery()
{
    DefaultField = "name",
    DefaultOperator = Operator.And,
    Query = "salman"
};

QueryContainer qry1 = null;

qry1 = new RangeQuery() // used to search for range ( from , to)
{
    Field = "modified",
    GreaterThanOrEqualTo = Convert.ToDateTime("21/12/2015").ToString("dd/MM/yyyy"),
};

QueryContainer all = qry && qry1;

var results = elastic.Search<Document>(s => s
   .Query(q => q
        .Bool(qb => qb
            .Must(all)))
    .Filter(f =>
            f.Nested(n => n
                 .Path("books")
                    .Filter(f3 => f3.And(
                                f1 => f1.Term("book.isbn", "122"),
                                f2 => f2.Term("book.author", "X"))

                            )
                    )
            )

    );

The problem is that i need to combine multiple queries (using And,OR operators) for "books" in dynamic fashion. For example, get the books that satisfy these set of conditions:

  1. Condition 1: Books that has Author "X" and isbn "1"
  2. Condition 2: Books that has Author "X" and isbn "2"
  3. Condition 3: Books that has Author "Z" and isbn "3"
  4. Other Condtions: .....

Now, the filter in the nested Query should retrieve books if:
Condition 1 AND Condition 2 Or Condition 3

Suppose that i have class name FilterOptions that contains the following attributes:

  1. FieldName
  2. Value
  3. Operator (which will combine the next filter)

I am going to loop on the given FilterOptions array to build the query.

Question:

What should i use to build the nested query? Is it a FilterDesciptor and how to combine them add the nested query to the Search Method?

Please, recommend any valuable link or example?

Beardsley answered 28/12, 2015 at 15:45 Comment(0)
P
13

I agree with paweloque, it seems your first two conditions are contradictory and wouldn't work if AND-ed together. Ignoring that, here's my solution. I've implemented this in such a way that allows for more than the three specific conditions you have. I too feel it would fit better in a bool statement.

QueryContainer andQuery = null;
QueryContainer orQuery = null;
foreach(var authorFilter in FilterOptions.Where(f=>f.Operator==Operator.And))
{
    andQuery &= new TermQuery
    {
        Field = authorFilter.FieldName,
        Value = authorFilter.Value
    };
}
foreach(var authorFilter in FilterOptions.Where(f=>f.Operator==Operator.Or))
{
    orQuery |= new TermQuery
    {
        Field = authorFilter.FieldName,
        Value = authorFilter.Value
    };
}

After that, in the .Nested call I would put:

.Path("books")
    .Query(q=>q
        .Bool(bq=>bq
            .Must(m=>m.MatchAll() && andQuery)
            .Should(orQuery)
    ))
Platypus answered 4/1, 2016 at 16:37 Comment(1)
I was searching for this answer , since two days... thank you..saved my dayHighams
D
1

In the specific case of the Condition 1 and Condition 2 you'd probably not get any results because these are exclusive conditions. But I assume now, that you want to get results which match either of those conditions. You've chosen nested which is definitely the way to go. With the nested type you can combine parameters for a single book.

Combining nested queries

For your use case I'd use bool query type with must or should clauses. A query to get books for either Condition 1 or Condition 2 would be:

POST /books/_search
{
   "query": {
      "bool": {
         "should": [
            {
               "nested": {
                  "path": "books",
                  "query": {
                     "bool": {
                        "must": [
                           {
                              "match": {
                                 "books.isbn": "2"
                              }
                           },
                           {
                              "match": {
                                 "books.author": "X"
                              }
                           }
                        ]
                     }
                  }
               }
            },
            {
               "nested": {
                  "path": "books",
                  "query": {
                     "bool": {
                        "must": [
                           {
                              "match": {
                                 "books.isbn": "1"
                              }
                           },
                           {
                              "match": {
                                 "books.author": "X"
                              }
                           }
                        ]
                     }
                  }
               }
            }
         ]
      }
   }
}

Can you explain, why are your books nested? Without nesting them in a top structure but indexing directly as a top level object in an index/type you could simplify your queries.

Not-Analyzed

There is another caveat that you have to remind: If you want to have an exact match on the author and the ISBN you have to make sure that the ISBN and author fields are set to not_analyzed. Otherwise they get analyzed and splitted into parts and your match would'n work very well.

E.g. if you have a ISBN Number with dashes, then it would get split into parts:

978-3-16-148410-0

would become indexed as:

978
3
16
148410
0

And a search with exactly the same ISBN number would give you all the books which have one of the sub-numbers in their ISBN number. If you want to prevent this, use the not_analyzed index-type and Multi-fields:

  "isbn": {
     "type": "string",
     "fields": {
        "raw": {
           "type": "string",
           "index": "not_analyzed"
        }
     }
  }

Then to address the not_analyzed isbn field you'd have to call it:

books.isbn.raw

Hope this helps.

Depersonalization answered 28/12, 2015 at 21:44 Comment(2)
actually i moved into nested object, when the queries were not satisfying the results intended which is executing both conditions on books. This link will clarify my problem: [#34401101Beardsley
How can i build this Query using NEST?Beardsley

© 2022 - 2024 — McMap. All rights reserved.