Ive now successfully got my c# Test win forms app working with aggregations and now I want to be able to filter on said aggregations if someone selects one (or more) of them.
Here is my query that is working exactly as I want and I am getting both price range buckets and buckets for each of the specification terms that it can find in the index.
.Aggregations(a => a
.Nested("specifications", n => n
.Path(p => p.ProductSpecification)
.Aggregations(aa => aa.Terms("groups", sp => sp.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.ProductSpecification.Suffix("value"))))
)
)
)
.Range("price_range", ra => ra
.Field(p => p.Price)
.Ranges(
r => r.To(50),
r => r.From(50).To(100),
r => r.From(100).To(150),
r => r.From(150).To(200),
r => r.From(200).To(250),
r => r.From(250)
)
))
.Index("myindex")
.Type("product")
.Query(q => q
.MultiMatch(m => m
.Query(searchBox.Text + "*")
.Fields(ff => ff
.Field(f => f.Name, boost: nameBoost)
.Field(f => f.Description, boost: descriptionBoost)
.Field(f => f.ProductCode)))));
Can someone point me in the direction of how I can go about filtering down results based on selecting any of these buckets.
UPDATE (30/01/18) I have now added this to my query
&& q.Nested(n => n
.Path(p => p.ProductSpecification)
.Query(q2 => q2
.Terms(t => t
.Field(f => f.ProductSpecification.Suffix("name"))
.Terms("Guarantee",)
)).Query(q3 => q3
.Terms(t2 => t2
.Field(f2 => f2.ProductSpecification.Suffix("value"))
.Terms("3 years","10 years")
)))
));
This is enabling me to pass multiple values into one Spec filter, but what im not sure on how to achieve is how to filter on multiple specs so the above filters on Guarantee with values of either 3 or 10 years
but if I also wanted to pass values of "Grey" and "Copper" to the spec of "Colour"
Adding "Colour" into My first set of terms and "Grey" and "Copper" into my second terms list breaks all filtering.
I think im close here just need a little direction
If I understand your question correctly, when the user selects a price range from the price range facets that are applicable to the query that is being executed, you'd like to apply this price range to the query?
The Post Filter serves this purpose, by applying a filter to the search results after aggregations have been calculated.
Assuming a user has selected the 50-100 price range, your query would like something like
var response = client.Search<Product>(s => s
.Aggregations(a => a
.Nested("specifications", n => n
.Path(p => p.ProductSpecification)
.Aggregations(aa => aa
.Terms("groups", sp => sp
.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt
.Field(ff => ff.ProductSpecification.Suffix("value"))
)
)
)
)
)
.Range("price_range", ra => ra
.Field(p => p.Price)
.Ranges(
r => r.To(50),
r => r.From(50).To(100),
r => r.From(100).To(150),
r => r.From(150).To(200),
r => r.From(200).To(250),
r => r.From(250)
)
)
)
.Index("myindex")
.Type("product")
.Query(q => q
.MultiMatch(m => m
.Query(searchBox.Text + "*")
.Fields(ff => ff
.Field(f => f.Name, boost: nameBoost)
.Field(f => f.Description, boost: descriptionBoost)
.Field(f => f.ProductCode)
)
)
)
.PostFilter(pf => pf
.Range(r => r
.Field(f => f.Price)
.GreaterThanOrEquals(50)
.LessThan(100)
)
)
);
Related
I have added the following snippet in a nest query which returns a ISearchResponse<T>
.Query(query => query.Match(m =>
m.Field(f =>
f => !string.IsNullOrEmpty(f.Purchase.NationalCode.Trim()))
)))
But it makes no difference to the amount of records returned (and I know there are nulls in there)
Should I do be building my query some other way?
Edited to add:
I've tried the following based on this answer here but no luck either
.Query(query => query.Bool(b=>
b.Must(m=> m.Exists(
f=>f.Field("nationalCode")))))
.Query(query => query.Bool(b=>
b.MustNot(m=> m.Term(
"nationalCode", ""))))
Try the following
.Query(query => query
.Bool(b => b
.Must(m => m
.Exists(f => f.Field("nationalCode"))
)
.MustNot(m => m
.Term("nationalCode", "")
)
)
)
Both the .Must and .MustNot need to be called on the same bool query descriptor
I'm trying to build a query that basically searches over all full text fields, boosting a few, but leaving all others at the default boost of 1.
When I don't include any fields, everything has a boost of 1 (we're on version 6.4.2 which supports default when no fields are specified):
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Query(request.Query)
)
)
);
However, as soon as I try to boost a single field, it removes the defaults on all the other fields, only searching on the explicit field:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
);
I tried adding a wildcard, but this still just matches on firstName (then again, the wildcard on its own doesn't match anything, so assuming I have the syntax wrong on that):
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f.Field("*.*^1"))
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
);
I also tried Booling them together, but this also just matches on firstName:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.Bool(b => b
.Should(m => m
.MultiMatch(mm => mm
.Query(request.Query)
)
)
.Should(m => m
.MultiMatch(mm => mm
.Fields(f => f.Field("firstName^20"))
.Query(request.Query)
)
)
)
)
);
I'm starting to think this isn't possible. For context, the reason I'm trying to do this is to be able to add other full text fields to the index without having to include every field in our queries, but still be able to boost certain fields.
Figured out my problem. I was chaining multiple .Fields() (plural) together, where I should only have a single .Fields() (plural) and then chain multiple .Field() (singular) together:
var results = await _ElasticClient.SearchAsync<dynamic>(s => s
.Query(q => q
.MultiMatch(m => m
.Fields(f => f
.Field("firstName^20")
.Field("*.*^1")
)
.Query(request.Query)
)
)
);
For the last few hours I've been trying to get the synonyms working in Elastic Search but so far failed.
When I do the search it's like the synonyms never get indexed.
Below is the part of code where I think I may be making a mistake.
Please have a look and any help will be much appreciated.
var createIndexTask = Client.CreateIndexAsync(synonymsindexname, i => i
.Settings(s => s
.Analysis(a => a
.TokenFilters(tf => tf
.Synonym("my_synonym", sf => sf
.Format(SynonymFormat.Solr)
.SynonymsPath("synonyms/solrsynonyms.txt")
)
)
.Analyzers(an => an
.Custom("my_analyzer", ca => ca
.Tokenizer("standard")
.Filters("lowercase", "stop", "my_synonym")
)
)
)
)
.Mappings(m => m
.Map<ClinicalCode>(c => c
.AutoMap()
.Properties(p => p
.Completion(comp => comp
.Contexts(ctx => ctx
.Category(csug => csug
.Name("ShortDescription")
.Path(x => x.ShortDescription)
)
)
.Name(n => n.Suggests)
.Analyzer("simple")
)
.Text(t => t
.Name(n => n.LongDescription)
.Analyzer("my_analyzer")
.CopyTo(ct => ct
.Field("allFields")
)
)
.Text(t => t
.Name(n => n.ShortDescription)
.Analyzer("my_analyzer")
.CopyTo(ct => ct
.Field("allFields")
)
)
.Text(t => t
.Name(n => n.ICDCode)
.Analyzer("my_analyzer")
.CopyTo(ct => ct
.Field("allFields")
)
)
.Keyword(k => k
.Name(n => n.ICDCode)
)
)
)
)
);
So I have manually built the following Query in NEST which works correctly,
What im trying to figure out is a way to build this dynamically dependent on what Product Specification Filters are passed in
.Query(q => q
.MultiMatch(m => m
.Query(searchBox.Text + "*")
.Fields(ff => ff
.Field(f => f.Name, boost: nameBoost)
.Field(f => f.Description, boost: descriptionBoost)
.Field(f => f.ProductCode)))
&& q.Nested(n => n
.Path(p => p.ProductSpecification)
.Query(q2 => q2
.Terms(t => t
.Field(f => f.ProductSpecification.Suffix("name"))
.Terms("Guarantee")
)).Query(q3 => q3
.Terms(t2 => t2
.Field(f2 => f2.ProductSpecification.Suffix("value"))
.Terms("3 years")
)))
&& q.Nested(n => n
.Path(p => p.ProductSpecification)
.Query(q2 => q2
.Terms(t => t
.Field(f => f.ProductSpecification.Suffix("name"))
.Terms("Brand")
)).Query(q3 => q3
.Terms(t2 => t2
.Field(f2 => f2.ProductSpecification.Suffix("value"))
.Terms("Sony")
)))
After looking at the link that Russ has shared I think the solution is to pass in my filters
public class ElasticJsonFilter
{
public string name { get; set; }
public List<string> Values { get; set; } = new List<string>();
}
Which will give me names such as "Guarantee" and values such as "2 Years, 3 Years" looking at the code that Russ linked I can get his to run through my list of filters and spit out boolean filters but I guess I need to get this to build a nested query?
elasticParams.Filters.Aggregate(new QueryContainer(), (c, s) => c && +q.Term(p => p.Name, s), c => c);
I am successfully creating Aggregations with my Elastic NEST query but I would like to pass some additional information in I have seen that I can use Meta for this, but I cannot find any documentation for doing it in NEST.
HERE is my nest Aggregations code
.Aggregations(aa => aa.Terms("groups", sp => sp.Field(p => p.ProductSpecification.Suffix("name"))
.Aggregations(aaa => aaa
.Terms("attribute", tt => tt.Field(ff => ff.ProductSpecification.Suffix("value"))))
Basically id like to be add say SpecificationId to the meta any hints?
You can add meta per aggregation, and it'll be returned on the aggregation response
var response = client.Search<User>(s => s
.Size(0)
.Query(q => +q
.Term(m => m
.Field(f => f.Badges.First().Name)
.Value("c#")
)
)
.Aggregations(a => a
.SignificantTerms("badges", st => st
.Field(f => f.Badges.First().Name)
.Meta(m => m
.Add("meta_1", "value_1")
.Add("meta_2", 2)
)
)
)
);