Alex Gurskiy
Alex Gurskiy

Reputation: 376

Apply different filters after main MultiMatch query in Elastic Search NEST

So, this is my query:

_elasticClient.Search<SearchItem>(x =>
                x.Sort(sort).Size(itemsPerPage)
                .Query(q =>
                    q.MultiMatch(m => m
                    .Fields(fs => fs
                        .Field(p => p.Field1)
                        .Field(p => p.Field2)
                        .Field(p => p.Field3)
                        .Field(p => p.Field4)
                        .Field(p => p.Field5)
                        .Field(p => p.Field6)                            
                    )
                    .Operator(Operator.And)
                    .Query(pattern)
                )));

I have to apply different filters. Range filters (for price), filter result set where field1 = "Audi" and field2 = "Sale Car". I tried to do something like that:

.Query(q =>
                    q.MultiMatch(m => m
                    .Fields(fs => fs
                        Field(p => p.Field1)
                            .Field(p => p.Field2)
                            .Field(p => p.Field3)
                            .Field(p => p.Field4)
                            .Field(p => p.Field5)
                            .Field(p => p.Field6)  
                    )
                    .Operator(Operator.And)
                    .Query(pattern)))
.Query(q=>q.Range(ra=>ra.Field(ff=>ff.SalePrice).GreaterThan(1000))));

But this is not working. I've all results from the index with price greater then 1000, but need only searched results. Could anyone help me?

Upvotes: 0

Views: 5588

Answers (1)

Russ Cam
Russ Cam

Reputation: 125518

You can use a bool query to combine queries and NEST makes this slightly easier to work with by overloading operators to combine QueryContainers (the root query type). Here's an example for NEST 2.x

void Main()
{
    var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
    var settings = new ConnectionSettings(connectionPool);

    var client = new ElasticClient(settings);

    var itemsPerPage = 20;
    var pattern = "match query";

    client.Search<SearchItem>(x => x
        .Sort(so => so
            .Descending("_score")
        )
        .Size(itemsPerPage)
        .Query(q => q
            .MultiMatch(m => m
                .Fields(fs => fs
                    .Field(p => p.Field1)
                    .Field(p => p.Field2)
                    .Field(p => p.Field3)
                    .Field(p => p.Field4)
                    .Field(p => p.Field5)
                    .Field(p => p.Field6)
                )
                .Operator(Operator.And)
                .Query(pattern)
            ) && q
            .Range(ra => ra
                .Field(ff=>ff.SalePrice)
                .GreaterThan(1000)
            )
        )
    );
}

public class SearchItem
{
    public int SalePrice { get; set; }

    public string Field1 { get; set; }

    public string Field2 { get; set; }

    public string Field3 { get; set; }

    public string Field4 { get; set; }

    public string Field5 { get; set; }

    public string Field6 { get; set; }
}

which yields

{
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "match query",
            "operator": "and",
            "fields": [
              "field1",
              "field2",
              "field3",
              "field4",
              "field5",
              "field6"
            ]
          }
        },
        {
          "range": {
            "salePrice": {
              "gt": 1000.0
            }
          }
        }
      ]
    }
  }
}

This will find documents that are a match for the multi_match query and also have a salePrice greater than 1000. Since we don't need a score calculated for the range query (a document either has a salePrice greater than 1000 or it doesn't), the range query can run in a filter context. A slightly refined version

client.Search<SearchItem>(x => x
    .Sort(so => so
        .Descending("_score")
    )
    .Size(itemsPerPage)
    .Query(q => q
        .MultiMatch(m => m
            .Fields(fs => fs
                .Field(p => p.Field1)
                .Field(p => p.Field2)
                .Field(p => p.Field3)
                .Field(p => p.Field4)
                .Field(p => p.Field5)
                .Field(p => p.Field6)
            )
            .Operator(Operator.And)
            .Query(pattern)
        ) && +q
        .Range(ra => ra
            .Field(ff=>ff.SalePrice)
            .GreaterThan(1000)
        )
    )
);

appending the unary + operator to the range query is a shorthand for a bool query filter. The query json now looks like

{
  "size": 20,
  "sort": [
    {
      "_score": {
        "order": "desc"
      }
    }
  ],
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "query": "match query",
            "operator": "and",
            "fields": [
              "field1",
              "field2",
              "field3",
              "field4",
              "field5",
              "field6"
            ]
          }
        }
      ],
      "filter": [
        {
          "range": {
            "salePrice": {
              "gt": 1000.0
            }
          }
        }
      ]
    }
  }
}

Upvotes: 3

Related Questions