Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: Support for using Query Elevation with CombinedQueryComponent (RRF)
type: added
authors:
- name: Sonu Sharma
nick: ercsonusharma
links:
- name: SOLR-18271
url: https://issues.apache.org/jira/browse/SOLR-18271
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ public void prepare(ResponseBuilder rb) throws IOException {
final var unparsedQuery = params.get(queryKey);
ResponseBuilder rbNew = new ResponseBuilder(rb.req, new SolrQueryResponse(), rb.components);
rbNew.setQueryString(unparsedQuery);
rbNew.setDebug(rb.isDebug());
super.prepare(rbNew);
crb.setFilters(rbNew.getFilters());
crb.responseBuilders.add(rbNew);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore
@VisibleForTesting static final String FIELD_TYPE = "queryFieldType";
@VisibleForTesting static final String CONFIG_FILE = "config-file";
private static final String EXCLUDE = "exclude";
private static final String DEBUG_QUERY_BOOSTING = "queryBoosting";

/**
* @see #getBoostDocs(SolrIndexSearcher, Set, Map)
Expand Down Expand Up @@ -488,6 +489,45 @@ public void prepare(ResponseBuilder rb) throws IOException {
return;
}

if (rb instanceof CombinedQueryResponseBuilder crb) {
prepareCombined(crb);
} else {
prepareElevationComponent(rb);
}
}

/**
* Elevates each subquery and mirrors the resulting SortSpec/filters onto the parent crb so {@link
* CombinedQueryComponent#mergeIds} can read {@code _elevate_} from each shard's {@code
* sort_values_i} during distributed merge.
*/
private void prepareCombined(CombinedQueryResponseBuilder crb) throws IOException {
if (crb.responseBuilders.isEmpty()) {
return;
}
for (ResponseBuilder thisRb : crb.responseBuilders) {
prepareElevationComponent(thisRb);
}
// Subqueries get identical elevation treatment, so any one is representative.
ResponseBuilder representative = crb.responseBuilders.getFirst();
crb.setSortSpec(representative.getSortSpec());
crb.setFilters(representative.getFilters());

if (crb.isDebug() && crb.isDebugQuery()) {
List<Object> debugPerSubquery = new ArrayList<>(crb.responseBuilders.size());
for (ResponseBuilder thisRb : crb.responseBuilders) {
Object queryBoosting = thisRb.getDebugInfo().get(DEBUG_QUERY_BOOSTING);
if (queryBoosting != null) {
debugPerSubquery.add(queryBoosting);
}
}
if (!debugPerSubquery.isEmpty()) {
crb.addDebugInfo(DEBUG_QUERY_BOOSTING, debugPerSubquery);
}
}
}

private void prepareElevationComponent(ResponseBuilder rb) throws IOException {
Elevation elevation = getElevation(rb);
if (elevation != null) {
setQuery(rb, elevation);
Expand Down Expand Up @@ -767,7 +807,7 @@ private void addDebugInfo(ResponseBuilder rb, Elevation elevation) {
SimpleOrderedMap<Object> dbg = new SimpleOrderedMap<>();
dbg.add("q", rb.getQueryString());
dbg.add("match", match);
rb.addDebugInfo("queryBoosting", dbg);
rb.addDebugInfo(DEBUG_QUERY_BOOSTING, dbg);
}

// ---------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,18 @@
<requestHandler name="/search" class="solr.CombinedQuerySearchHandler">
</requestHandler>

<searchComponent class="solr.CombinedQueryComponent" name="combined_query">
<requestHandler name="/search-elevate" class="solr.CombinedQuerySearchHandler">
<arr name="last-components">
<str>elevator</str>
</arr>
</requestHandler>

<searchComponent name="elevator" class="solr.QueryElevationComponent">>
<str name="queryFieldType">string</str>
<str name="config-file">elevate.xml</str>
</searchComponent>

<searchComponent class="solr.CombinedQueryComponent" name="combined_query">
<int name="maxCombinerQueries">2</int>
<lst name="combiners">
<lst name="test">
Expand Down Expand Up @@ -159,7 +170,7 @@
</highlighting>
</searchComponent>

<initParams path="/select,/search">
<initParams path="/select,/search,/search-elevate">
<lst name="defaults">
<str name="df">text</str>
</lst>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@
*/
package org.apache.solr.handler.component;

import static org.apache.solr.cloud.AbstractZkTestCase.SOLRHOME;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.cloud.AbstractFullDistribZkTestBase;
import org.apache.solr.cloud.ZkTestServer;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ShardParams;
Expand Down Expand Up @@ -49,6 +52,12 @@ public CombinedQuerySolrCloudTest() {
configString = "solrconfig-combined-query.xml";
}

@Override
public void distribSetUp() throws Exception {
super.distribSetUp();
ZkTestServer.putConfig("conf1", zkServer.getZkClient(), SOLRHOME, "elevate.xml");
}

@Override
protected String getCloudSchemaFile() {
return "schema-vector-catchall.xml";
Expand Down Expand Up @@ -121,7 +130,7 @@ public void testSingleLexicalQuery() throws Exception {
query(
CommonParams.JSON,
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:2^10\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:2^=10\"}}},"
+ "\"limit\":5,"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\"]}}",
Expand All @@ -141,8 +150,8 @@ public void testMultipleLexicalQuery() throws Exception {
prepareIndexDocs();
String jsonQuery =
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^2)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"limit\":5,"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\",\"lexical2\"]}}";
Expand All @@ -161,8 +170,8 @@ public void testMultipleQueryWithSort() throws Exception {
prepareIndexDocs();
String jsonQuery =
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^1)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"limit\":5,\"sort\":\"mod3_idv desc, score desc\""
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\",\"lexical2\"]}}";
Expand All @@ -183,8 +192,8 @@ public void testHybridQueryWithPagination() throws Exception {
query(
CommonParams.JSON,
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^2)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\",\"lexical2\"]}}",
CommonParams.QT,
Expand All @@ -194,8 +203,8 @@ public void testHybridQueryWithPagination() throws Exception {
query(
CommonParams.JSON,
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^2)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"limit\":4,"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\",\"lexical2\"]}}",
Expand All @@ -206,8 +215,8 @@ public void testHybridQueryWithPagination() throws Exception {
query(
CommonParams.JSON,
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^2)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"limit\":4,\"offset\":3,"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"combiner.query\":[\"lexical1\",\"lexical2\"]}}",
Expand All @@ -227,7 +236,7 @@ public void testQueryWithFaceting() throws Exception {
prepareIndexDocs();
String jsonQuery =
"{\"queries\":"
+ "{\"lexical\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}}},"
+ "{\"lexical\":{\"lucene\":{\"query\":\"id:(2^=2 OR 3^=1 OR 6^=2 OR 5^=1)\"}}},"
+ "\"limit\":3,\"offset\":1"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"facet\":true,\"facet.field\":\"mod3_idv\",\"facet.mincount\":1,"
Expand All @@ -248,8 +257,8 @@ public void testQueriesWithFacetAndHighlights() throws Exception {
prepareIndexDocs();
String jsonQuery =
"{\"queries\":"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^2 OR 3^1 OR 6^2 OR 5^1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^1 OR 5^2 OR 7^3 OR 10^2)\"}}},"
+ "{\"lexical1\":{\"lucene\":{\"query\":\"id:(2^=4 OR 3^=2 OR 6^=3 OR 5^=1)\"}},"
+ "\"lexical2\":{\"lucene\":{\"query\":\"id:(8^=1 OR 5^=3 OR 7^=4 OR 10^=2)\"}}},"
+ "\"limit\":4,"
+ "\"fields\":[\"id\",\"score\",\"title\"],"
+ "\"params\":{\"combiner\":true,\"facet\":true,\"facet.field\":\"mod3_idv\","
Expand All @@ -269,6 +278,48 @@ public void testQueriesWithFacetAndHighlights() throws Exception {
rsp.getHighlighting().get("5").get("title").getFirst());
}

/**
* Tests the combined query feature with faceting, highlighting and elevation.
*
* @throws Exception if any unexpected error occurs during the test execution.
*/
@Test
public void testElevatedQueriesWithFacetAndHighlights() throws Exception {
prepareIndexDocs();
String jsonQuery =
"""
{
"queries": {
"lexical1": {"lucene": {"query": "id:(2^=3 OR 3^=1 OR 6^=2 OR 5^=2)"}},
"lexical2": {"lucene": {"query": "id:(4^=1 OR 5^=2 OR 7^=3 OR 10^=2)"}}
},
"limit": 4,
"fields": ["id", "score", "title"],
"params": {
"combiner": true,
"elevateIds": "6,10",
"combiner.query": ["lexical1", "lexical2"],
"facet": true,
"facet.field": "mod3_idv",
"hl": true,
"hl.fl": "title",
"hl.q": "test doc"
}
}""";
QueryResponse rsp = query(CommonParams.JSON, jsonQuery, CommonParams.QT, "/search-elevate");
assertEquals(4, rsp.getResults().size());
assertFieldValues(rsp.getResults(), id, "6", "10", "5", "7");
assertEquals("mod3_idv", rsp.getFacetFields().getFirst().getName());
assertEquals("[1 (3), 0 (2), 2 (2)]", rsp.getFacetFields().getFirst().getValues().toString());
assertEquals(4, rsp.getHighlighting().size());
assertEquals(
"title <em>test</em> for <em>doc</em> 10",
rsp.getHighlighting().get("10").get("title").getFirst());
assertEquals(
"title <em>test</em> for <em>doc</em> 5",
rsp.getHighlighting().get("5").get("title").getFirst());
}

/**
* Tests the combined query feature with faceting, highlighting and collapse.
*
Expand All @@ -284,12 +335,12 @@ public void testQueriesWithFacetAndHighlightsCollapse() throws Exception {
"queries": {
"lexical1": {
"lucene": {
"query": "id:(CO!2^3 OR CO!3^1 OR CO!6^2 OR CO!5^1)"
"query": "id:(CO!2^=3 OR CO!3^=1 OR CO!6^=2 OR CO!5^=1)"
}
},
"lexical2": {
"lucene": {
"query": "id:(CO!8^1 OR CO!5^2 OR CO!7^3 OR CO!10^2)"
"query": "id:(CO!8^=1 OR CO!5^=2 OR CO!7^=3 OR CO!10^=2)"
}
}
},
Expand Down
Loading