Using the Elasticsearch NEST library for .NET I am trying to create a query that will search for products. The model's structure is the following:
[ElasticsearchType(Name = "product", IdProperty = "ProductId")]
public class ProductIndex
{
[Number(NumberType.Integer)]
public int ProductId { get; set; }
[Text]
public string Name { get; set; }
[Text]
public string Description { get; set; }
[Number(NumberType.Integer)]
public int BranchId { get; set; }
[Number(NumberType.Integer)]
public int SubbranchId { get; set; }
[Number(NumberType.Float)]
public decimal Rating { get; set; }
[Boolean]
public bool IsActive { get; set; }
[Date]
public DateTime PublishedAt { get; set; }
[Nested]
public ICollection<VariantIndex> Variants { get; set; }
}
[ElasticsearchType(Name = "variant", IdProperty = "VariantId")]
public class VariantIndex
{
public int VariantId { get; set; }
public decimal Price { get; set; }
public IEnumerable<string> Values { get; set; }
}
I need to filter the products according to the following parameters:
- branchId (int): the product BranchId equals to the branchId parameter,
- subbranches (int[]): the product SubbranchId is contained in the subbranches array
- rating (int): the product Rating is greater than or equal to the rating parameter
- minPrice (int): the product variants Price is greater than minPrice parameter
- maxPrice (int): the product variants Price is less than maxPrice parameter
- searchTerm (string): the product Name or Description contain the string searchTerm
- tags (string[]): the product variants Values contains one or more or all tags.
NOTE: The branchId, subbranches, rating and searchTerm should filter out the top level ProductIndex objects. Then, the minPrice and maxPrice parameters should filter out any nested Variants objects if their Price is not in the specified range and finally the tags parameter should filter out any nested Variants object that do not contain any of the tags in their Values array or score them appropriately according to the number of tags that matched in the Values array.
The end result should be a list of scored ProductIndex objects, each containing a list of filtered out and scored VariantIndex objects.
Here is how i tried to create the query but this gives me totally wrong search results:
Func<SearchDescriptor<ProductIndex>, ISearchRequest> search = s => s
.From(page * 10)
.Size(10)
.Query(q => q
.Bool(b => b
.Must(mu => mu
.Match(m => m
.Field(f => f.Name)
.Query(searchTerm)
), mu => mu
.Match(m => m
.Field(f => f.Description)
.Query(product)
), mu => mu
.Terms(t => t
.Field(f => f.Variants.Select(v => v.Values))
.Terms(tags)
)
)
.Filter(bf => bf
.Term(t => t
.Field(p => p.BranchId)
.Value(mainbranch))
)
.Filter(bf => bf
.Terms(t => t
.Field(p => p.SubbranchId)
.Terms(subbranches)
)
)
.Filter(bf => bf
.Range(r => r
.Field(f => f.Variants.Select(v => v.Price))
.GreaterThanOrEquals(minPrice)
.LessThanOrEquals(maxPrice)
)
)
)
);
var response = await client.SearchAsync<ProductIndex>(search);
EDIT 1: Based on the comments my own investigation I finally managed to create a query which works correctly. The only thing left to do is to filter out the list of nested VariantIndex objects so that we only get the variants which comply with the price and tags filters. What code should I add to my latest working version of the query in order to achieve this?
Func<SearchDescriptor<ProductIndex>, ISearchRequest> search = s => s.Type<ProductIndex>()
.Query(q => q
.Bool(b => b
.Must(m => m
.MultiMatch(mm => mm
.Query(product)
.Fields(f =>
f.Fields(f1 => f1.Name, f2 => f2.Description)
)
)
)
.Should(ss => ss
.Match(m => m
.Field(f => f.Variants.Select(v => v.Values))
.Query(tagsJoined)
)
)
.Filter(m => m
.Bool(bl => bl
.Must(ms => ms
.Range(r => r
.Field(f => f.Rating)
.GreaterThanOrEquals(rating)
),
ms => ms
.Range(r => r
.Field(f => f.Variants.Select(v => v.Price))
.GreaterThanOrEquals(minPrice)
.LessThanOrEquals(maxPrice)
)
)
)
)
)
)