I've got list of objects called selected
which is this shape: List<{bool Selected, String Discriminator, List<int> Tags}>
.
I need to say if document.VgnItm.Discriminator
is one of selected[index].Discriminator
, AND document.VgnItm.Tags
has at least one tag from selected[index].Tags
, then select that document.
This works when selected
contains only one item:
for (var i = 0; i < selected.Count; i++)
{
QueryContainer &= Query<VgnItmEstSearchDto>.Bool(boolean => boolean.Must(booleanMust =>
booleanMust.Term(term => term.Field(termField => termField.VgnItm.Discriminator)
.Value(selected[i].Discriminator)),
booleanMust => booleanMust.Terms(terms => terms
.Field(p => p.VgnItm.Tags)
.Terms(selected[i].Tags)
))
);
}
But when selected
contains two or more items the QueryContainer &=
means that the document.VgnItm.Discriminator
must be selected[index1].Discriminator
, and also selected[index2].Discriminator
which is impossible. So it doesn't find any results.
What can I do here to make this work when selected contains multiple items? I can't just use QueryContainer |=
because the QueryContainer
has other clauses above. I tried wrapping Query<VgnItmEstSearchDto>.Bool(boolean => boolean.Must
in a Bool.Should
so it is a double Bool, but it had the same result.
I was able to do it by composing QueryContainer
s in the correct manner. As long as we compose like so: var compositeQueryContainer = childQueryA.QueryContainer &= childQueryB.QueryContainer
, then having QueryContainer |= Query<VgnItmEstSearchDto>.Bool
as I mentioned in my question, will not negate a previous query, it will combine them (AND them rather than OR them).
So I can create the custom query exactly how it is in the question but use an |=
(OR) inside my loop, which builds a larger query container from every loop cycle:
public class ChildQueryB :
BaseQuery<VgnItmEstSearchDto>
{
public ChildQueryB(
string searchTerm,
List<DiscriminatorTags> selectedDiscriminatorTags = null,
bool shouldSearchNumberFields = false) :
base(searchTerm, shouldSearchNumberFields)
{
for (var i = 0; i < selectedDiscriminatorTags.Count; i++)
{
QueryContainer |= Query<VgnItmEstSearchDto>.Bool(boolean => boolean.Must(booleanMust =>
booleanMust.Term(term => term.Field(termField => termField.VgnItm.Discriminator)
.Value(selectedDiscriminatorTags[i].Discriminator)),
booleanMust => booleanMust.Terms(terms => terms
.Field(p => p.VgnItm.Tags)
.Terms(selectedDiscriminatorTags[i].Tags)
))
);
}
}
}
Then compose it with another child query in a parent query using &=
:
public class CompositeQuery :
BaseQuery<VgnItmEstSearchDto>
{
public CompositeQuery(
string searchTerm,
List<DiscriminatorTags> selectedDiscriminatorTags = null,
bool shouldSearchNumberFields = false) :
base(searchTerm, shouldSearchNumberFields)
{
var childQueryA = new ChildQueryB(searchTerm, true);
var childQueryB = new ChildQueryA(searchTerm, selectedDiscriminatorTags, true);
base.QueryContainer = childQueryA.QueryContainer &=
childQuery.QueryContainer;
}
}
It can then be used like:
var compositeQuery = new CompositeQuery(searchTerm, discriminatorTags.Where(d => d.Selected == true).ToList(), true);
var results = searchService.Search(compositeQuery, pageNumber, pageSize);