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,9 @@
# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
title: Add top-level "queries" support to JsonQueryRequest in SolrJ
type: added
authors:
- name: Sonu Sharma
nick: ercsonusharma
links:
- name: SOLR-18093
url: https://issues.apache.org/jira/browse/SOLR-18093
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ private void initComponents() {
DebugComponent dbgCmp = null;
for (String c : list) {
SearchComponent comp = core.getSearchComponent(c);
if (comp == null) {
throw new SolrException(
SolrException.ErrorCode.SERVER_ERROR, "Unknown search component: " + c);
}
if (comp instanceof DebugComponent && makeDebugLast == true) {
dbgCmp = (DebugComponent) comp;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
<double name="maxWriteMBPerSecDefault">1000000</double>
<double name="maxWriteMBPerSecFlush">2000000</double>
<double name="maxWriteMBPerSecMerge">3000000</double>
<double name="maxWriteMBPerSecRead">4000000</double>
<double name="maxWriteMBPerSecRead">4000000</double>
</directoryFactory>

<schemaFactory class="ClassicIndexSchemaFactory"/>
Expand Down Expand Up @@ -380,6 +380,9 @@
</arr>
</requestHandler>

<searchComponent class="solr.CombinedQueryComponent" name="combined_query">
</searchComponent>

<requestHandler name="/mltrh" class="org.apache.solr.handler.component.SearchHandler">

</requestHandler>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private synchronized void prepareIndexDocsColocated() throws Exception {
del("*:*");
List<SolrInputDocument> docs = getSolrDocuments();
for (SolrInputDocument doc : docs) {
doc.setField("id", doc.getFieldValue("mod3_idv") + "!" + doc.getField("id").getValue());
doc.setField("id", "CO!" + doc.getField("id").getValue());
}
for (SolrInputDocument doc : docs) {
indexDoc(doc);
Expand Down Expand Up @@ -284,16 +284,16 @@ public void testQueriesWithFacetAndHighlightsCollapse() throws Exception {
"queries": {
"lexical1": {
"lucene": {
"query": "id:(2!2^2 OR 0!3^1 OR 0!6^2 OR 2!5^1)"
"query": "id:(CO!2^3 OR CO!3^1 OR CO!6^2 OR CO!5^1)"
}
},
"lexical2": {
"lucene": {
"query": "id:(2!8^1 OR 2!5^2 OR 1!7^3 OR 1!10^2)"
"query": "id:(CO!8^1 OR CO!5^2 OR CO!7^3 OR CO!10^2)"
}
}
},
"limit": 1,
"limit": 3,
"fields": [
"id",
"score",
Expand All @@ -304,7 +304,7 @@ public void testQueriesWithFacetAndHighlightsCollapse() throws Exception {
"facet": true,
"facet.field": "id",
"fq": [
"{!collapse field=mod3_idv sort='id asc'}"
"{!collapse field=mod3_idv sort='id asc, score desc'}"
],
"expand": true,
"expand.q": "*:*",
Expand All @@ -319,17 +319,17 @@ public void testQueriesWithFacetAndHighlightsCollapse() throws Exception {
}""";
handle.put("expanded", UNORDERED);
QueryResponse rsp = query(CommonParams.JSON, jsonQuery, CommonParams.QT, "/search");
assertEquals(1, rsp.getResults().size());
assertFieldValues(rsp.getResults(), id, "2!2");
assertEquals(3, rsp.getResults().size());
Comment thread
ercsonusharma marked this conversation as resolved.
assertFieldValues(rsp.getResults(), id, "CO!2", "CO!10", "CO!3");
assertEquals("id", rsp.getFacetFields().getFirst().getName());
assertEquals(
"[0!3 (1), 1!10 (1), 2!2 (1), 0!6 (0), 0!9 (0), 1!1 (0), 1!4 (0), 1!7 (0), 2!5 (0), 2!8 (0)]",
"[CO!10 (1), CO!2 (1), CO!3 (1), CO!1 (0), CO!4 (0), CO!5 (0), CO!6 (0), CO!7 (0), CO!8 (0), CO!9 (0)]",
rsp.getFacetFields().getFirst().getValues().toString());
assertEquals(1, rsp.getHighlighting().size());
assertEquals(3, rsp.getHighlighting().size());
assertEquals(
"title <em>test</em> for <em>doc</em> 2",
rsp.getHighlighting().get("2!2").get("title").getFirst());
assertEquals(1, rsp.getExpandedResults().size());
rsp.getHighlighting().get("CO!2").get("title").getFirst());
assertEquals(3, rsp.getExpandedResults().size());
}

/** To test that we can force distrib */
Expand Down
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mmmmm, not sure what I think about adding another handler to one of only 2 configSets we ship with Solr. But I suppose it's fine as this particular one, techproducts, is to show off features. So sure.

CC @epugh for 2nd opinion.

Original file line number Diff line number Diff line change
Expand Up @@ -640,7 +640,14 @@
</lst>
</requestHandler>

<initParams path="/update/**,/query,/select,/tvrh,/elevate,/spell,update">
<requestHandler name="/rrf" class="solr.CombinedQuerySearchHandler">
</requestHandler>

<searchComponent class="solr.CombinedQueryComponent" name="combined_query">
Comment thread
ercsonusharma marked this conversation as resolved.
<int name="maxCombinerQueries">2</int>
</searchComponent>

<initParams path="/update/**,/query,/search,/select,/tvrh,/elevate,/spell,update">
<lst name="defaults">
<str name="df">text</str>
</lst>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,76 @@ public void testSimpleJsonQuery() throws Exception {
assertResponseFoundNumDocs(queryResponse, expectedResults);
}

/**
* Test json query behaviour in case of multiple query to be executed using Combined Query.
*
* @throws Exception the exception
*/
@Test
public void testSimpleJsonQueryWithQueriesParams() throws Exception {
SolrClient solrClient = cluster.getSolrClient();
final int expectedResults = 2;
final Map<String, Object> queriesMap = new HashMap<>();
queriesMap.put(
"query1",
Map.of(
"lucene",
Map.of(
"query", "apache",
"df", "manu")));
queriesMap.put(
"query2",
Map.of(
"edismax",
Map.of(
"query", "solr",
"df", "name")));
final JsonQueryRequest query =
new JsonQueryRequest()
.setQueries(queriesMap)
.withFilter("inStock:true")
.withParam("fl", "name")
.withParam("combiner", "true")
.withParam("combiner.query", List.of("query1", "query2"));
query.setPath("/rrf");
QueryResponse queryResponse = query.process(solrClient, COLLECTION_NAME);
assertResponseFoundNumDocs(queryResponse, expectedResults);
}

/**
* Test json query behaviour in case of multiple query to be executed using Additional Queries.
*
* @throws Exception the exception
*/
@Test
public void testAdditionalJsonQueries() throws Exception {
SolrClient solrClient = cluster.getSolrClient();
final int expectedResults = 12;
// tag::solrj-json-query-with-queries[]
final Map<String, Object> queriesMap = new HashMap<>();
queriesMap.put(
"electronic",
Map.of(
"field",
Map.of(
"query", "electronics",
"f", "cat")));
queriesMap.put(
"manufacturers",
List.of(
"manu: apple",
Map.of(
"field",
Map.of(
"query", "belkin",
"f", "manu"))));
final JsonQueryRequest query =
new JsonQueryRequest().setQueries(queriesMap).setQuery(Map.of("param", "electronic"));
QueryResponse queryResponse = query.process(solrClient, COLLECTION_NAME);
// end::solrj-json-query-with-queries[]
assertEquals(expectedResults, queryResponse.getResults().getNumFound());
}

@Test
public void testJsonQueryWithJsonQueryParamOverrides() throws Exception {
SolrClient solrClient = cluster.getSolrClient();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,21 @@ The query structure is similar to JSON Query DSL except for how multiple queries

=== Example

Below is a sample JSON query payload:
Below is an example with sample JSON query payload:

```
[tabs#json-query-with-queries]
======
curl::
+
====
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
{
"queries": {
"lexical1": {
"lucene": {
"query": "title:sales"
"query": "name:apache"
}
},
"vector": {
Expand All @@ -97,15 +104,37 @@ Below is a sample JSON query payload:
}
},
"limit": 5,
"fields": ["id", "score", "title"],
"fields": ["id", "score", "name"],
"params": {
"combiner": true,
"combiner.query": ["lexical1", "vector"],
"combiner.algorithm": "rrf",
"combiner.rrf.k": "15"
}
}
```
}'
----
====
SolrJ::
+
====
[source,java,indent=0]
----
final Map<String, Object> queriesMap = new HashMap<>();
queriesMap.put("lexical1", Map.of("lucene", Map.of("query", "apache", "df", "name")));
queriesMap.put("vector", Map.of("knn", Map.of("query", [0.1,-0.34,0.89,0.02], "f", "vector", "topK", 5)));
final JsonQueryRequest query =
new JsonQueryRequest()
.setQueries(queriesMap)
.withParam("id", "score", "name")
.setLimit(5)
.withParam("combiner", "true")
.withParam("combiner.query", List.of("lexical1", "vector"))
.withParam("combiner.algorithm", "rrf")
.withParam("combiner.rrf.k", "15");
QueryResponse queryResponse = query.process(solrClient, COLLECTION_NAME);
----
====
======

== Combiner Algorithm Plugin

Expand Down
15 changes: 15 additions & 0 deletions solr/solr-ref-guide/modules/query-guide/pages/json-query-dsl.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,11 @@ Beware of arity for these references.
Depending on the context, a reference might be resolved into the first element in the array ignoring the later elements, e.g., if one changes the reference below from `{"param":"electronic"}` to `{"param":"manufacturers"}`, it's equivalent to querying for `manu:apple`, ignoring the later query.
These queries don't impact the query result until explicit referencing.

[tabs#json-additional-queries]
======
curl::
+
====
[source,bash]
----
curl -X POST http://localhost:8983/solr/techproducts/query -d '
Expand All @@ -402,6 +407,16 @@ curl -X POST http://localhost:8983/solr/techproducts/query -d '
"query":{"param":"electronic"}
}'
----
====
SolrJ::
+
====
[source,java,indent=0]
----
include::example$JsonRequestApiTest.java[tag=solrj-json-query-with-queries]
----
====
======

Overall this example doesn't make much sense, but just demonstrates the syntax.
This feature is useful in xref:json-faceting-domain-changes.adoc#adding-domain-filters[filtering domain] in JSON Facet API xref:json-facet-api.adoc#changing-the-domain[domain changes].
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,26 @@ public JsonQueryRequest setQuery(Map<String, Object> queryJson) {
return this;
}

/**
* Specify the queries parameter sent as a part of JSON request.
*
* <p>This method would be helpful in setting the queries parameter specially for Combined Query
* Component {@code CombinedQueryComponent} use case.
*
* <p><b>Example:</b> You wish to send the JSON request:
*
* <pre>{@code {'limit': 5, 'queries': {'query1': {'lucene':
* {'df':'genre_s', 'query': 'scifi'}}}, 'query2': {'knn': {'f': 'vector', 'query': [0.1, 0.43]}}}}
* </pre>
*
* @param queriesJson a Map of values representing the query subtree of the JSON request you wish
* to send.
*/
public JsonQueryRequest setQueries(Map<String, Object> queriesJson) {
jsonRequestMap.put("queries", queriesJson);
return this;
}

/**
* Specify the query sent as a part of this JSON request.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.client.solrj.request.AbstractUpdateRequest;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
Expand All @@ -45,6 +46,7 @@ public class JsonQueryRequestIntegrationTest extends SolrCloudTestCase {
private static final int NUM_SCIFI_BOOKS = 2;
private static final int NUM_IN_STOCK = 8;
private static final int NUM_IN_STOCK_AND_FIRST_IN_SERIES = 5;
private static final int NUM_MARTIN_BOOKS = 3;

@BeforeClass
public static void setupCluster() throws Exception {
Expand Down Expand Up @@ -88,6 +90,29 @@ public void testQueriesCanUseLocalParamsSyntax() throws Exception {
assertEquals(NUM_SCIFI_BOOKS, queryResponse.getResults().getNumFound());
}

/**
* Test multiple queries can use local params syntax with Combined Query Component.
*
* @throws Exception the exception
*/
@Test
public void testMultipleQueriesCanUseLocalParamsSyntax() throws Exception {
final Map<String, Object> queriesMap = new HashMap<>();
queriesMap.put("query1", "{!lucene df=genre_s v='scifi'}");
queriesMap.put("query2", "{!edismax df=author_t v='martin'}");
final JsonQueryRequest query =
new JsonQueryRequest()
.setQueries(queriesMap)
.withFilter("inStock:true")
.withParam("fl", "name")
.withParam("combiner", "true")
.withParam("combiner.query", List.of("query1", "query2"));
query.setPath("/rrf");
QueryResponse queryResponse = query.process(cluster.getSolrClient(), COLLECTION_NAME);
assertEquals(0, queryResponse.getStatus());
assertEquals(NUM_SCIFI_BOOKS + NUM_MARTIN_BOOKS, queryResponse.getResults().size());
}

@Test
public void testQueriesCanUseExpandedSyntax() throws Exception {
// Construct a tree representing the JSON: {lucene: {df:'genre_s', 'query': 'scifi'}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,29 @@ public void testWritesProvidedQueryMapToJsonCorrectly() {
requestBody, containsString("\"query\":{\"lucene\":{\"q\":\"*:*\",\"df\":\"text\"}}"));
}

@Test
public void testWritesProvidedQueriesMapToJsonCorrectly() {
final Map<String, Object> queriesMap = new HashMap<>();
queriesMap.put(
"query1",
Map.of(
"lucene",
Map.of(
"query", "*:*",
"df", "text")));
queriesMap.put(
"query2",
Map.of(
"edismax",
Map.of(
"query", "solr",
"df", "text")));
final JsonQueryRequest request = new JsonQueryRequest().setQueries(queriesMap);
final String requestBody = writeRequestToJson(request);
assertThat(requestBody, containsString("\"query1\":{\"lucene\":"));
assertThat(requestBody, containsString("\"query2\":{\"edismax\":"));
}

@Test
public void testWritesProvidedQueryMapWriterToJsonCorrectly() {
final MapWriter queryWriter =
Expand Down
Loading