I have an interesting challenge, which I think there is an easy answer to.
I know that NEST filters work correctly when syntactically you do something like this:
var andFilter = FilterFactory.AndFilter(
FilterFactory.TermFilter("name.first", "shay1"),
FilterFactory.TermFilter("name.first", "shay4")
);
My base services should allow an the caller to pass in some sort of enumerable list of items to filter.
I'd basically like to be able programmatically achieve something like this (filters is passed into the method):
var andFilter = new FilterDescriptor();
foreach (var filter in filters)
{
andFilter = filter concatenated to andFilter
}
In other words if I passed in an array of { {"first.name", "joe"}, {"first.name", "jim"}, {"first.name", "frank"}} I would like to produce the equivalent of
var andFilter = FilterFactory.AndFilter(
FilterFactory.TermFilter("name.first", "joe"),
FilterFactory.TermFilter("name.first", "joe"),
FilterFactory.TermFilter("name.first", "frank")
);
Using the lambda based DSL you can do the following:
var termsFilters = from tp in termParameters
let field = ToCamelCaseNestedNames(tp.SearchField)
let terms = tp.SearchValues
select Filter.Terms(field, terms);
var prefixFilters = from tp in prefixParameters
let field = ToCamelCaseNestedNames(tp.SearchField)
let prefix = tp.SearchValues.FirstOrDefault().ToLowerInvariant()
select Filter.Prefix(field, prefix);
var search = client.Search(s => s
.From(0)
.Size(20)
.Filter(f => f.And(termsFilters.Concat(prefixFilters).ToArray()))
);
Which i think reads a bit better :)
Nest now also supports conditionless
queries so if any tp.SearchValues
is null
, empty
or all empty strings
or tp.SearchField
is null or empty
it will skip that terms/prefix query.
You can revert this behavior easily though:
var search = client.Search(s => s
.Strict()
.From(0)
.Size(20)
.Filter(f => f.And(termsFilters.Concat(prefixFilters).ToArray()))
);
which will throw a DslException
if an empty query is generated.
As a last note client.Search()
will return a QueryResult<dynamic>
if you can strongly type your documents so can do a client.Search<MyDocument>()
.
Martijn's answer is the best but I thought I would add an example that I created that is working for me, hopefully it will be helpful to others. I built a list of BaseQuery objects and then put that in my query using the .ToArray() method.
#region build query
var query = new List<BaseQuery>
{
Query<IAuthForReporting>.Range(r => r.OnField(f => f.AuthResult.AuthEventDate)
.From(authsByDateInput.StartDate.ToEPCISFormat())
.To(authsByDateInput.EndDate.ToEPCISFormat()))
};
if (authsByDateInput.AuthResult != AuthResultEnum.SuccessOrFailure)
{
var success = authsByDateInput.AuthResult == AuthResultEnum.Success;
query.Add(Query<IAuthForReporting>.Term(t => t.AuthResult.AuthenticationSuccessful, success));
}
if (authsByDateInput.ProductID != null)
{
query.Add(Query<IAuthForReporting>.Term(t => t.AuthResult.ProductID, authsByDateInput.ProductID.Value));
}
if (!authsByDateInput.CountryIDs.IsNullOrEmpty())
{
query.Add(Query<IAuthForReporting>.Terms(t => t.AuthResult.Address.CountryID, authsByDateInput.CountryIDs.Select(x=> x.Value.ToString()).ToArray()));
}
#endregion
var result =
ElasticClient.Search<IAuthForReporting>(s =>
s.Index(IndexName)
.Type(TypeName)
.Size(0)
.Query(q =>
q.Bool(b =>
b.Must(query.ToArray())
)
)
.FacetDateHistogram(t => t.OnField(f => f.AuthResult.AuthEventDate).Interval(DateInterval.Day))
);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With