mirror of https://github.com/apache/lucene.git
SOLR-12490: Introducing json.queries to define many named queries in Query DSL.
This commit is contained in:
parent
479e736469
commit
8fba8eba13
|
@ -153,12 +153,14 @@ Upgrade Notes
|
||||||
If you prefer to keep the old (but insecure) serialization strategy, you can start your nodes using the
|
If you prefer to keep the old (but insecure) serialization strategy, you can start your nodes using the
|
||||||
property: `-Dsolr.useUnsafeOverseerResponse=true`. Keep in mind that this will be removed in future version of Solr.
|
property: `-Dsolr.useUnsafeOverseerResponse=true`. Keep in mind that this will be removed in future version of Solr.
|
||||||
|
|
||||||
* SOLR-13808: add cache=false into uderneath BoolQParser's filter clause or {"bool":{"filter":..}} to avoid caching in
|
* SOLR-13808: add cache=false into underneath BoolQParser's filter clause or {"bool":{"filter":..}} to avoid caching in
|
||||||
filterCache. (Mikhail Khludnev)
|
filterCache. (Mikhail Khludnev)
|
||||||
|
|
||||||
New Features
|
New Features
|
||||||
---------------------
|
---------------------
|
||||||
(No changes)
|
* SOLR-12490: Introducing json.queries in JSON Request API. Every property of this object holds one or many named
|
||||||
|
Query DSL queries. It's optional and doesn't impact response without explicit referencing these queries by names
|
||||||
|
(Anatolii Siuniaev via Mikhail Khludnev)
|
||||||
|
|
||||||
Improvements
|
Improvements
|
||||||
---------------------
|
---------------------
|
||||||
|
|
|
@ -192,6 +192,7 @@ public class RequestUtil {
|
||||||
|
|
||||||
// implement compat for existing components...
|
// implement compat for existing components...
|
||||||
JsonQueryConverter jsonQueryConverter = new JsonQueryConverter();
|
JsonQueryConverter jsonQueryConverter = new JsonQueryConverter();
|
||||||
|
|
||||||
if (json != null && !isShard) {
|
if (json != null && !isShard) {
|
||||||
for (Map.Entry<String,Object> entry : json.entrySet()) {
|
for (Map.Entry<String,Object> entry : json.entrySet()) {
|
||||||
String key = entry.getKey();
|
String key = entry.getKey();
|
||||||
|
@ -214,48 +215,62 @@ public class RequestUtil {
|
||||||
out = "rows";
|
out = "rows";
|
||||||
} else if (SORT.equals(key)) {
|
} else if (SORT.equals(key)) {
|
||||||
out = SORT;
|
out = SORT;
|
||||||
|
} else if ("queries".equals(key)) {
|
||||||
|
Object queriesJsonObj = entry.getValue();
|
||||||
|
if (queriesJsonObj instanceof Map && queriesJsonObj != null) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final Map<String,Object> queriesAsMap = (Map<String,Object>) queriesJsonObj;
|
||||||
|
for (Map.Entry<String,Object> queryJsonProperty : queriesAsMap.entrySet()) {
|
||||||
|
out = queryJsonProperty.getKey();
|
||||||
|
arr = true;
|
||||||
|
isQuery = true;
|
||||||
|
convertJsonPropertyToLocalParams(newMap, jsonQueryConverter, queryJsonProperty, out, isQuery, arr);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
|
||||||
|
"Expected Map for 'queries', received " + queriesJsonObj);
|
||||||
|
}
|
||||||
} else if ("params".equals(key) || "facet".equals(key) ) {
|
} else if ("params".equals(key) || "facet".equals(key) ) {
|
||||||
// handled elsewhere
|
// handled elsewhere
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown top-level key in JSON request : " + key);
|
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown top-level key in JSON request : " + key);
|
||||||
}
|
}
|
||||||
|
convertJsonPropertyToLocalParams(newMap, jsonQueryConverter, entry, out, isQuery, arr);
|
||||||
Object val = entry.getValue();
|
|
||||||
|
|
||||||
if (arr) {
|
|
||||||
String[] existing = newMap.get(out);
|
|
||||||
List lst = val instanceof List ? (List)val : null;
|
|
||||||
int existingSize = existing==null ? 0 : existing.length;
|
|
||||||
int jsonSize = lst==null ? 1 : lst.size();
|
|
||||||
String[] newval = new String[ existingSize + jsonSize ];
|
|
||||||
for (int i=0; i<existingSize; i++) {
|
|
||||||
newval[i] = existing[i];
|
|
||||||
}
|
|
||||||
if (lst != null) {
|
|
||||||
for (int i = 0; i < jsonSize; i++) {
|
|
||||||
Object v = lst.get(i);
|
|
||||||
newval[existingSize + i] = isQuery ? jsonQueryConverter.toLocalParams(v, newMap) : v.toString();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newval[newval.length-1] = isQuery ? jsonQueryConverter.toLocalParams(val, newMap) : val.toString();
|
|
||||||
}
|
|
||||||
newMap.put(out, newval);
|
|
||||||
} else {
|
|
||||||
newMap.put(out, new String[]{isQuery ? jsonQueryConverter.toLocalParams(val, newMap) : val.toString()});
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (json != null) {
|
if (json != null) {
|
||||||
req.setJSON(json);
|
req.setJSON(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void convertJsonPropertyToLocalParams(Map<String, String[]> outMap, JsonQueryConverter jsonQueryConverter, Map.Entry<String, Object> jsonProperty, String outKey, boolean isQuery, boolean arr) {
|
||||||
|
Object val = jsonProperty.getValue();
|
||||||
|
|
||||||
|
if (arr) {
|
||||||
|
String[] existing = outMap.get(outKey);
|
||||||
|
List<?> lst = val instanceof List ? (List<?>)val : null;
|
||||||
|
int existingSize = existing==null ? 0 : existing.length;
|
||||||
|
int jsonSize = lst==null ? 1 : lst.size();
|
||||||
|
String[] newval = new String[ existingSize + jsonSize ];
|
||||||
|
for (int i=0; i<existingSize; i++) {
|
||||||
|
newval[i] = existing[i];
|
||||||
|
}
|
||||||
|
if (lst != null) {
|
||||||
|
for (int i = 0; i < jsonSize; i++) {
|
||||||
|
Object v = lst.get(i);
|
||||||
|
newval[existingSize + i] = isQuery ? jsonQueryConverter.toLocalParams(v, outMap) : v.toString();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newval[newval.length-1] = isQuery ? jsonQueryConverter.toLocalParams(val, outMap) : val.toString();
|
||||||
|
}
|
||||||
|
outMap.put(outKey, newval);
|
||||||
|
} else {
|
||||||
|
outMap.put(outKey, new String[]{isQuery ? jsonQueryConverter.toLocalParams(val, outMap) : val.toString()});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// queryParamName is something like json.facet or json.query, or just json...
|
// queryParamName is something like json.facet or json.query, or just json...
|
||||||
|
@ -295,6 +310,7 @@ public class RequestUtil {
|
||||||
|
|
||||||
Object o = ObjectBuilder.getVal(parser);
|
Object o = ObjectBuilder.getVal(parser);
|
||||||
if (!(o instanceof Map)) return;
|
if (!(o instanceof Map)) return;
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
Map<String,Object> map = (Map<String,Object>)o;
|
Map<String,Object> map = (Map<String,Object>)o;
|
||||||
// To make consistent with json.param handling, we should make query params come after json params (i.e. query params should
|
// To make consistent with json.param handling, we should make query params come after json params (i.e. query params should
|
||||||
// appear to overwrite json params.
|
// appear to overwrite json params.
|
||||||
|
@ -310,7 +326,7 @@ public class RequestUtil {
|
||||||
if (val == null) {
|
if (val == null) {
|
||||||
params.remove(key);
|
params.remove(key);
|
||||||
} else if (val instanceof List) {
|
} else if (val instanceof List) {
|
||||||
List lst = (List) val;
|
List<?> lst = (List<?>) val;
|
||||||
String[] vals = new String[lst.size()];
|
String[] vals = new String[lst.size()];
|
||||||
for (int i = 0; i < vals.length; i++) {
|
for (int i = 0; i < vals.length; i++) {
|
||||||
vals[i] = lst.get(i).toString();
|
vals[i] = lst.get(i).toString();
|
||||||
|
|
|
@ -56,6 +56,7 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
private static int origTableSize;
|
private static int origTableSize;
|
||||||
private static FacetField.FacetMethod origDefaultFacetMethod;
|
private static FacetField.FacetMethod origDefaultFacetMethod;
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeTests() throws Exception {
|
public static void beforeTests() throws Exception {
|
||||||
systemSetPropertySolrDisableShardsWhitelist("true");
|
systemSetPropertySolrDisableShardsWhitelist("true");
|
||||||
|
@ -83,6 +84,7 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public static void afterTests() throws Exception {
|
public static void afterTests() throws Exception {
|
||||||
systemClearPropertySolrDisableShardsWhitelist();
|
systemClearPropertySolrDisableShardsWhitelist();
|
||||||
|
@ -1063,7 +1065,6 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void doStatsTemplated(Client client, ModifiableSolrParams p) throws Exception {
|
public static void doStatsTemplated(Client client, ModifiableSolrParams p) throws Exception {
|
||||||
int numShards = client.local() ? 1 : client.getClientProvider().all().size();
|
|
||||||
p.set("Z_num_i", "Z_" + p.get("num_i") );
|
p.set("Z_num_i", "Z_" + p.get("num_i") );
|
||||||
p.set("Z_num_l", "Z_" + p.get("num_l") );
|
p.set("Z_num_l", "Z_" + p.get("num_l") );
|
||||||
p.set("sparse_num_d", "sparse_" + p.get("num_d") );
|
p.set("sparse_num_d", "sparse_" + p.get("num_d") );
|
||||||
|
@ -2290,6 +2291,19 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
"}"
|
"}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
//test filter using queries from json.queries
|
||||||
|
client.testJQ(params(p, "q", "*:*"
|
||||||
|
, "json.queries", "{catS:{'#cat_sA': '${cat_s}:A'}, ff:[{'#id_1':'-id:1'},{'#id_2':'-id:2'}]}"
|
||||||
|
, "json.facet", "{" +
|
||||||
|
",t_filt1:{${terms} type:terms, field:${cat_s}, domain:{filter:{param:catS} } }" + // test filter via "param" type from .queries
|
||||||
|
",t_filt2:{${terms} type:terms, field:${cat_s}, domain:{filter:{param:ff}} }" + // test multi-valued query parameter from .queries
|
||||||
|
"}"
|
||||||
|
)
|
||||||
|
, "facets=={ count:6, " +
|
||||||
|
",t_filt1:{ buckets:[ {val:A, count:2}] } " +
|
||||||
|
",t_filt2:{ buckets:[ {val:B, count:2}, {val:A, count:1}] } " +
|
||||||
|
"}"
|
||||||
|
);
|
||||||
|
|
||||||
// test acc reuse (i.e. reset() method). This is normally used for stats that are not calculated in the first phase,
|
// test acc reuse (i.e. reset() method). This is normally used for stats that are not calculated in the first phase,
|
||||||
// currently non-sorting stats.
|
// currently non-sorting stats.
|
||||||
|
@ -2907,7 +2921,7 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
int commitPercent = 10;
|
int commitPercent = 10;
|
||||||
int ndocs=1000;
|
int ndocs=1000;
|
||||||
|
|
||||||
Map<Integer, Map<Integer, List<Integer>>> model = new HashMap(); // cat->where->list<ids>
|
Map<Integer, Map<Integer, List<Integer>>> model = new HashMap<>(); // cat->where->list<ids>
|
||||||
for (int i=0; i<ndocs; i++) {
|
for (int i=0; i<ndocs; i++) {
|
||||||
Integer cat = r.nextInt(numCat);
|
Integer cat = r.nextInt(numCat);
|
||||||
Integer where = r.nextInt(numWhere);
|
Integer where = r.nextInt(numWhere);
|
||||||
|
@ -3328,7 +3342,6 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void doTestErrors(Client client) throws Exception {
|
public void doTestErrors(Client client) throws Exception {
|
||||||
ModifiableSolrParams p = params("rows", "0");
|
|
||||||
client.deleteByQuery("*:*", null);
|
client.deleteByQuery("*:*", null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -3646,11 +3659,18 @@ public class TestJsonFacets extends SolrTestCaseHS {
|
||||||
req("q", "*:*", "rows", "0", "json.facet", "{cat_s:{type:terms,field:cat_s,sort:[\"count desc\"]}}"),
|
req("q", "*:*", "rows", "0", "json.facet", "{cat_s:{type:terms,field:cat_s,sort:[\"count desc\"]}}"),
|
||||||
SolrException.ErrorCode.BAD_REQUEST);
|
SolrException.ErrorCode.BAD_REQUEST);
|
||||||
|
|
||||||
|
|
||||||
assertQEx("Should fail as facet is not of type map",
|
assertQEx("Should fail as facet is not of type map",
|
||||||
"Expected Map for 'facet', received ArrayList=[{}]",
|
"Expected Map for 'facet', received ArrayList=[{}]",
|
||||||
req("q", "*:*", "rows", "0", "json.facet", "[{}]"), SolrException.ErrorCode.BAD_REQUEST);
|
req("q", "*:*", "rows", "0", "json.facet", "[{}]"), SolrException.ErrorCode.BAD_REQUEST);
|
||||||
|
|
||||||
|
assertQEx("Should fail as queries is not of type map",
|
||||||
|
"Expected Map for 'queries', received [{}]",
|
||||||
|
req("q", "*:*", "rows", "0", "json.queries", "[{}]"), SolrException.ErrorCode.BAD_REQUEST);
|
||||||
|
|
||||||
|
assertQEx("Should fail as queries are null in JSON",
|
||||||
|
"Expected Map for 'queries', received null",
|
||||||
|
req("json", "{query:\"*:*\", queries:null}"), SolrException.ErrorCode.BAD_REQUEST);
|
||||||
|
|
||||||
// range facets
|
// range facets
|
||||||
assertQEx("Should fail as 'other' is of type Map",
|
assertQEx("Should fail as 'other' is of type Map",
|
||||||
"Expected list of string or comma separated string values for 'other', " +
|
"Expected list of string or comma separated string values for 'other', " +
|
||||||
|
|
Loading…
Reference in New Issue