-
Notifications
You must be signed in to change notification settings - Fork 87
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add filter function for NeuralQueryBuilder and HybridQueryBuilder and… #1206
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.List; | ||
import java.util.ListIterator; | ||
import java.util.Locale; | ||
import java.util.Objects; | ||
import java.util.stream.Collectors; | ||
|
@@ -51,6 +52,7 @@ public final class HybridQueryBuilder extends AbstractQueryBuilder<HybridQueryBu | |
public static final String NAME = "hybrid"; | ||
|
||
private static final ParseField QUERIES_FIELD = new ParseField("queries"); | ||
private static final ParseField FILTER_FIELD = new ParseField("filter"); | ||
private static final ParseField PAGINATION_DEPTH_FIELD = new ParseField("pagination_depth"); | ||
|
||
private final List<QueryBuilder> queries = new ArrayList<>(); | ||
|
@@ -94,6 +96,26 @@ public HybridQueryBuilder add(QueryBuilder queryBuilder) { | |
return this; | ||
} | ||
|
||
/** | ||
* Function to support filter on HybridQueryBuilder filter. | ||
* If the filter is null, then we do nothing and return. | ||
* Otherwise, we push down the filter to queries list. | ||
* @param filter the filter parameter | ||
* @return HybridQueryBuilder itself | ||
*/ | ||
public QueryBuilder filter(QueryBuilder filter) { | ||
if (validateFilterParams(filter) == false) { | ||
return this; | ||
} | ||
ListIterator<QueryBuilder> iterator = queries.listIterator(); | ||
while (iterator.hasNext()) { | ||
QueryBuilder query = iterator.next(); | ||
// set the query again because query.filter(filter) can return new query. | ||
iterator.set(query.filter(filter)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we can save few CPU cycles here if we check for a queryWithFilter ref, if that's same as query then there is no need in calling There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @martin-gaievski, If you're okay with it, let's keep this as is. The difference between There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok, let's keep the |
||
} | ||
return this; | ||
} | ||
|
||
/** | ||
* Create builder object with a content of this hybrid query | ||
* @param builder | ||
|
@@ -155,6 +177,10 @@ protected Query doToQuery(QueryShardContext queryShardContext) throws IOExceptio | |
* } | ||
* } | ||
* ] | ||
* "filter": | ||
* "term": { | ||
* "text": "keyword" | ||
* } | ||
* } | ||
* } | ||
* } | ||
|
@@ -168,6 +194,7 @@ public static HybridQueryBuilder fromXContent(XContentParser parser) throws IOEx | |
|
||
Integer paginationDepth = null; | ||
final List<QueryBuilder> queries = new ArrayList<>(); | ||
QueryBuilder filter = null; | ||
String queryName = null; | ||
|
||
String currentFieldName = null; | ||
|
@@ -178,6 +205,8 @@ public static HybridQueryBuilder fromXContent(XContentParser parser) throws IOEx | |
} else if (token == XContentParser.Token.START_OBJECT) { | ||
if (QUERIES_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { | ||
queries.add(parseInnerQueryBuilder(parser)); | ||
} else if (FILTER_FIELD.match(currentFieldName, parser.getDeprecationHandler())) { | ||
filter = parseInnerQueryBuilder(parser); | ||
} else { | ||
log.error(String.format(Locale.ROOT, "[%s] query does not support [%s]", NAME, currentFieldName)); | ||
throw new ParsingException( | ||
|
@@ -240,7 +269,11 @@ public static HybridQueryBuilder fromXContent(XContentParser parser) throws IOEx | |
compoundQueryBuilder.paginationDepth(paginationDepth); | ||
} | ||
for (QueryBuilder query : queries) { | ||
compoundQueryBuilder.add(query); | ||
if (filter == null) { | ||
compoundQueryBuilder.add(query); | ||
} else { | ||
compoundQueryBuilder.add(query.filter(filter)); | ||
} | ||
} | ||
return compoundQueryBuilder; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new field should also be added to HybridQueryBuilder(StreamInput in) and doXContent(XContentBuilder builder, Params params) so that we will lose the filter info when we pass it across nodes. Even though the filter info is already included in the sub queries I think we still should follow the best practice to include it in those two functions.
Besides we also want to add it to the doEquals and doHashCode function in case they are used to compare two hybrid queries.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like we are missing some test case here unless this filter setting is happening in coordination node.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Bo, quick question while I am trying to follow your comments.
What do you mean by we will lose the filter info? Could you please give an example?
The field I added here is not the filter itself but the filter parser field name. I believe the filter itself is pushed down to queries. So we are automatically comparing the queries and its filter in doEquals and doHashCode. What do you mean by adding it to the doEquals and doHashCode function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For your first question when we pass the query across nodes we will convert it to a stream and later in another node build it based on the stream. If we don't clearly write the filter to the stream we will lose it.
For the second question we override the doEquals and doHashCode function so if we don't clearly check it those two functions will ignore it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Synced offline. Since currently we always want to push down the filter to sub-queries and we always do this work in the fromXContent function there is no need to persist the filter in the HybridQuery. In this case there is not need to write it to the stream since it's already included in the sub-queries. Same for the doEqual and doHash functions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this should be fine, one concern I do have is some edge case in context of cluster upgrade. Can you add a bwc test, it may be skipped for now for 2.x -> 3.x migration, but it will run in future for next 3.x, e.g. 3.0 -> 3.1. You can check how it's done in of the recent PR for stats API