-
Notifications
You must be signed in to change notification settings - Fork 776
SOLR-17319 : Combined Query Feature for Multi Query Execution #3418
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
base: main
Are you sure you want to change the base?
Changes from 5 commits
bf3cd5d
182bec9
b884f0e
29e8aea
c113799
3600ed3
9b0c76e
a841bc7
91f8e09
cace1f7
299db43
840070e
d2feefc
89f63a9
d821abb
8041d66
397dbb3
d8b5588
ec0b9cb
d6fd190
86933bc
b164979
85f2cf9
a4a26aa
7fe997c
bcd1c3b
787a016
7e0727c
4dcbb57
8a65023
006b8c2
771089b
460e8cd
ac85d2f
7b0593c
c03c0f7
a52dd22
195f3f1
c1f5501
3649d3e
4eedbed
0990e7f
14ff5e1
bd637b7
2958599
c3e44c3
e2dfcef
d4b34fc
3fe93b8
f23cceb
6419a07
a560899
ae84ef3
1083fdd
be428f0
5b487c2
18e8518
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 |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.solr.handler.component; | ||
|
|
||
| import java.io.IOException; | ||
| import java.lang.invoke.MethodHandles; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import org.apache.solr.common.params.CombinerParams; | ||
| import org.apache.solr.common.params.CursorMarkParams; | ||
| import org.apache.solr.common.params.ShardParams; | ||
| import org.apache.solr.common.params.SolrParams; | ||
| import org.apache.solr.response.BasicResultContext; | ||
| import org.apache.solr.response.ResultContext; | ||
| import org.apache.solr.response.SolrQueryResponse; | ||
| import org.apache.solr.search.DocListAndSet; | ||
| import org.apache.solr.search.QueryResult; | ||
| import org.apache.solr.search.combine.QueryAndResponseCombiner; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** | ||
| * The CombinedQueryComponent class extends QueryComponent and provides support for executing | ||
| * multiple queries and combining their results. | ||
| */ | ||
| public class CombinedQueryComponent extends QueryComponent { | ||
|
|
||
| public static final String COMPONENT_NAME = "combined_query"; | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); | ||
dsmiley marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /** | ||
| * Overrides the prepare method to handle combined queries. | ||
| * | ||
| * @param rb the ResponseBuilder to prepare | ||
| * @throws IOException if an I/O error occurs during preparation | ||
| */ | ||
| @Override | ||
| public void prepare(ResponseBuilder rb) throws IOException { | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (rb instanceof CombinedQueryResponseBuilder crb) { | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| SolrParams params = crb.req.getParams(); | ||
| String[] queriesToCombineKeys = params.getParams(CombinerParams.COMBINER_QUERY); | ||
| for (String queryKey : queriesToCombineKeys) { | ||
| final var unparsedQuery = params.get(queryKey); | ||
| ResponseBuilder rbNew = new ResponseBuilder(rb.req, new SolrQueryResponse(), rb.components); | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| rbNew.setQueryString(unparsedQuery); | ||
| super.prepare(rbNew); | ||
|
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. wouldn't we want to manipulate the sort spec so that we get all docs up to offset (AKA "start" param) + rows since RRF/combiner is going to want to see all docs/rankings up to offset+rows? Otherwise our combiner is blind to the "offset" docs. Assuming you agree, then we need to basically apply paging at this layer (our component) instead of letting the subquery do it. 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. It anyways happening here 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. That's for distributed-search but not single-core search. 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. I think user-managed/standalone vs SolrCloud is orthogonal. This is about a single shard working correctly (in whatever Solr mode). IMO it's not optional for basic paging parameters to work correctly with one shard. I could imagine we'd prefer a mechanism for a SearchComponent to force the "shortCircuit"=false thereby ensuring there's always a distributed phase. Maybe that could be done by re-ordering SearchHandler's call to |
||
| crb.responseBuilders.add(rbNew); | ||
| } | ||
| } | ||
| super.prepare(rb); | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /** | ||
| * Overrides the process method to handle CombinedQueryResponseBuilder instances. This method | ||
| * processes the responses from multiple shards, combines them using the specified | ||
ercsonusharma marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| * QueryAndResponseCombiner strategy, and sets the appropriate results and metadata in the | ||
| * CombinedQueryResponseBuilder. | ||
| * | ||
| * @param rb the ResponseBuilder object to process | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| * @throws IOException if an I/O error occurs during processing | ||
| */ | ||
| @Override | ||
| public void process(ResponseBuilder rb) throws IOException { | ||
|
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. As this component forces distributed mode, why would process() be called? 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. With reference, In the case of distributed mode, an HTTP request is made with rb.distrib=false through ShardHandlerRequest in one of the phases. The 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. For an isShard=true request then. Okay; but then why wouldn't the logic just pass-through as normal? It's not clear why CombinedQueryComponent has any logic to accomplish at this phase. "Way 2" is only at aggregation (disbtrib=true), in my head anyway. 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. ping 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. Sorry, took longer to reply.
The However, this Searcher call per query can be further parallelised as added in comment. |
||
| if (rb instanceof CombinedQueryResponseBuilder crb) { | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| boolean partialResults = false; | ||
| boolean segmentTerminatedEarly = false; | ||
| List<QueryResult> queryResults = new ArrayList<>(); | ||
ercsonusharma marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for (ResponseBuilder rbNow : crb.responseBuilders) { | ||
ercsonusharma marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| super.process(rbNow); | ||
| DocListAndSet docListAndSet = rbNow.getResults(); | ||
| QueryResult queryResult = new QueryResult(); | ||
| queryResult.setDocListAndSet(docListAndSet); | ||
| queryResults.add(queryResult); | ||
| partialResults |= SolrQueryResponse.isPartialResults(rbNow.rsp.getResponseHeader()); | ||
| rbNow.setCursorMark(rbNow.getCursorMark()); | ||
| if (rbNow.rsp.getResponseHeader() != null) { | ||
| segmentTerminatedEarly |= | ||
| (boolean) | ||
| rbNow | ||
| .rsp | ||
| .getResponseHeader() | ||
| .getOrDefault( | ||
| SolrQueryResponse.RESPONSE_HEADER_SEGMENT_TERMINATED_EARLY_KEY, false); | ||
| } | ||
| } | ||
| QueryAndResponseCombiner combinerStrategy = | ||
| QueryAndResponseCombiner.getImplementation(rb.req.getParams()); | ||
| QueryResult combinedQueryResult = combinerStrategy.combine(queryResults); | ||
| combinedQueryResult.setPartialResults(partialResults); | ||
| combinedQueryResult.setSegmentTerminatedEarly(segmentTerminatedEarly); | ||
| crb.setResult(combinedQueryResult); | ||
| ResultContext ctx = new BasicResultContext(crb); | ||
| crb.rsp.addResponse(ctx); | ||
| crb.rsp | ||
| .getToLog() | ||
dsmiley marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| .add( | ||
| "hits", | ||
| crb.getResults() == null || crb.getResults().docList == null | ||
| ? 0 | ||
| : crb.getResults().docList.matches()); | ||
| if (!crb.req.getParams().getBool(ShardParams.IS_SHARD, false)) { | ||
| if (null != crb.getNextCursorMark()) { | ||
| crb.rsp.add( | ||
| CursorMarkParams.CURSOR_MARK_NEXT, crb.getNextCursorMark().getSerializedTotem()); | ||
| } | ||
| } | ||
|
|
||
| if (crb.mergeFieldHandler != null) { | ||
| crb.mergeFieldHandler.handleMergeFields(crb, crb.req.getSearcher()); | ||
| } else { | ||
| doFieldSortValues(rb, crb.req.getSearcher()); | ||
| } | ||
| doPrefetch(crb); | ||
| } else { | ||
| super.process(rb); | ||
| } | ||
| } | ||
|
|
||
| @Override | ||
| public String getDescription() { | ||
| return "Combined Query Component to support multiple query execution"; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one or more | ||
| * contributor license agreements. See the NOTICE file distributed with | ||
| * this work for additional information regarding copyright ownership. | ||
| * The ASF licenses this file to You under the Apache License, Version 2.0 | ||
| * (the "License"); you may not use this file except in compliance with | ||
| * the License. You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
| package org.apache.solr.handler.component; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import org.apache.solr.request.SolrQueryRequest; | ||
| import org.apache.solr.response.SolrQueryResponse; | ||
|
|
||
| /** | ||
| * The CombinedQueryResponseBuilder class extends the ResponseBuilder class and is responsible for | ||
| * building a combined response for multiple SearchComponent objects. It orchestrates the process of | ||
| * constructing the SolrQueryResponse by aggregating results from various components. | ||
| */ | ||
| public class CombinedQueryResponseBuilder extends ResponseBuilder { | ||
|
|
||
| public final List<ResponseBuilder> responseBuilders = new ArrayList<>(); | ||
|
|
||
| /** | ||
| * Constructs a CombinedQueryResponseBuilder instance. | ||
| * | ||
| * @param req the SolrQueryRequest object containing the query parameters and context. | ||
| * @param rsp the SolrQueryResponse object to which the combined results will be added. | ||
| * @param components a list of SearchComponent objects that will be used to build the response. | ||
| */ | ||
| public CombinedQueryResponseBuilder( | ||
| SolrQueryRequest req, SolrQueryResponse rsp, List<SearchComponent> components) { | ||
| super(req, rsp, components); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.