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
bool
query filter
clause). Since a document either matches or it doesn't, a relevancy score for matching does not need to be calculated. 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.