简体   繁体   中英

Equivalent linq query in elasticsearch.net(NEST)

I have the following document type in elastic :

public class ProductDto
{
   public Int64 Id { get; set; }

   public String Title{ get; set; }

   public bool Visible { get; set; }

   public IList<ProductSupplierDto> ProductSuppliers { get; set; }
}

public class ProductSupplierDto
{
   public Int64 Id { get; set; }

   public String Title{ get; set; }

   public bool Enabled { get; set; }
}

how to write below linq query with Nest libray :

var products = db.products.where(p=>  p.Visible 
                                   && p.ProductSuppliers.Any(ps=>ps.Enabled)
                                 ).ToList();

in nest library i have the following query:

var baseQuery = Query<ProductDto>.Term(qt => qt.Field(f => 
                                      f.Visible).Value(true));

how to add productsuppliers filter to baseQuery ?

I use this method for create index :

    private async Task CreateIndexIfItDoesntExist<T>(string index) where T: class
    {
        if (!this.client.IndexExists(index).Exists)
        {
            var indexDescriptor = new CreateIndexDescriptor(index)
                            .Settings(x => x.NumberOfReplicas(0))
                            .Mappings(mappings => mappings
                                .Map<T>(m => m.AutoMap()));

            await this.client.CreateIndexAsync(index, i => indexDescriptor);

       // Max out the result window so you can have pagination for >100 pages
           await this.client.UpdateIndexSettingsAsync(index, ixs => ixs
             .IndexSettings(s => s
                 .Setting("max_result_window", int.MaxValue)));


        }
    }

and call like this :

await CreateIndexIfItDoesntExist<ProductDto>("products");

methods for index data:

    private async Task<IndexResult> IndexDocuments<T>(T[] datas, string index) where T:class
    {
        int batchSize = 1000; // magic
        int totalBatches = (int)Math.Ceiling((double)datas.Length / batchSize);

        for (int i = 0; i < totalBatches; i++)
        {
            var response = await this.client.IndexManyAsync(datas.Skip(i * batchSize).Take(batchSize), index);

            if (!response.IsValid)
            {
                return new IndexResult
                {
                    IsValid = false,
                    ErrorReason = response.ServerError?.Error?.Reason,
                    Exception = response.OriginalException
                };
            }
            else
            {
                Debug.WriteLine($"Successfully indexed batch {i + 1}");
            }
        }

        return new IndexResult
        {
            IsValid = true
        };
    }

ProductSupplierDto in ProductSuppliers will be mapped as an object type with automapping, so the following query will achieve what you're after

var client = new ElasticClient();

var searchResponse = client.Search<ProductDto>(s => s
    .Query(q => +q
        .Term(f => f.Visible, true) && +q
        .Term(f => f.ProductSuppliers[0].Enabled, true)
    )    
);  

This generates the following query

{
  "query": {
    "bool": {
      "filter": [
        {
          "term": {
            "visible": {
              "value": true
            }
          }
        },
        {
          "term": {
            "productSuppliers.enabled": {
              "value": true
            }
          }
        }
      ]
    }
  }
}

A couple of points

  1. The query uses operator overloading on queries to combine them together and generate queries that execute in a filter context (in this case, a bool query filter clause). Since a document either matches or it doesn't, a relevancy score for matching does not need to be calculated.
  2. f => f.ProductSuppliers[0].Enabled is an expression to get the path to a field. It does not mean "get the value of Enabled from the first item in ProductSuppliers " , but means "get the path to the Enabled field of all items in the ProductSuppliers property" . the indexer into the collection here is only to be able to access the properties of the ProductSupplierDto type.

You may want to consider mapping ProductSuppliers as a nested type so that you would be able to query across properties of individual items in a ProductSuppliers collection. With ProductSuppliers mapped as a nested type, the query would then be

var searchResponse = client.Search<ProductDto>(s => s
    .Query(q => +q
        .Term(f => f.Visible, true) && +q
        .Nested(n => n
            .Path(p => p.ProductSuppliers)
            .Query(nq => nq
                .Term(f => f.ProductSuppliers[0].Enabled, true)
            )
        )           
    )    
);

Something like this?

  QueryContainer baseQuery = Query<ProductDto>.Term(qt => qt.Field(f =>
                                   f.Visible).Value(true));
        baseQuery &= Query<ProductDto>.Term(qt => qt.Field(o => o.ProductSuppliers.Select(a => a.Enabled)).Value(true));

        client.Search<ProductDto>(o => o
           .From(0)
           .Size(10)
           .Query(a => baseQuery));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM