mirror of https://github.com/apache/lucene.git
SOLR-7216: JSON request API
git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1668168 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
e2feb7a2e5
commit
29053fd238
|
@ -186,6 +186,15 @@ New Features
|
|||
* SOLR-6892: Update processors can now be top-level components and they can be
|
||||
specified in request to create a new custom update chain (Noble Paul)
|
||||
|
||||
* SOLR-7216: Solr JSON Request API:
|
||||
- HTTP search requests can have a JSON body.
|
||||
- JSON request can also be passed via the "json" parameter.
|
||||
- Smart merging of multiple JSON parameters: ruery parameters starting with "json."
|
||||
will be merged into the JSON request.
|
||||
- Legacy query parameters can also be passed in the "params" block of
|
||||
the JSON request.
|
||||
(yonik)
|
||||
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
|
|
@ -105,7 +105,11 @@ public class DebugComponent extends SearchComponent
|
|||
else {
|
||||
info.addAll( stdinfo );
|
||||
}
|
||||
|
||||
|
||||
if (rb.req.getJSON() != null) {
|
||||
info.add("json", rb.req.getJSON());
|
||||
}
|
||||
|
||||
if (rb.isDebugQuery() && rb.getQparser() != null) {
|
||||
rb.getQparser().addDebugInfo(rb.getDebugInfo());
|
||||
}
|
||||
|
@ -350,6 +354,9 @@ public class DebugComponent extends SearchComponent
|
|||
return dl;
|
||||
}
|
||||
|
||||
// only add to list if JSON is different
|
||||
if (source.equals(dest)) return source;
|
||||
|
||||
// merge unlike elements in a list
|
||||
List<Object> t = new ArrayList<>();
|
||||
t.add(dest);
|
||||
|
|
|
@ -174,12 +174,6 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware ,
|
|||
@Override
|
||||
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws Exception
|
||||
{
|
||||
// int sleep = req.getParams().getInt("sleep",0);
|
||||
// if (sleep > 0) {log.error("SLEEPING for " + sleep); Thread.sleep(sleep);}
|
||||
if (req.getContentStreams() != null && req.getContentStreams().iterator().hasNext()) {
|
||||
throw new SolrException(ErrorCode.BAD_REQUEST, "Search requests cannot accept content streams");
|
||||
}
|
||||
|
||||
ResponseBuilder rb = new ResponseBuilder(req, rsp, components);
|
||||
if (rb.requestInfo != null) {
|
||||
rb.requestInfo.setResponseBuilder(rb);
|
||||
|
|
|
@ -0,0 +1,217 @@
|
|||
package org.apache.solr.search.json;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.solr.JSONTestUtil;
|
||||
import org.apache.solr.SolrTestCaseHS;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
@LuceneTestCase.SuppressCodecs({"Lucene3x","Lucene40","Lucene41","Lucene42","Lucene45","Appending"})
|
||||
public class TestJsonRequest extends SolrTestCaseHS {
|
||||
|
||||
private static SolrInstances servers; // for distributed testing
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeTests() throws Exception {
|
||||
JSONTestUtil.failRepeatedKeys = true;
|
||||
initCore("solrconfig-tlog.xml","schema_latest.xml");
|
||||
}
|
||||
|
||||
public static void initServers() throws Exception {
|
||||
if (servers == null) {
|
||||
servers = new SolrInstances(3, "solrconfig-tlog.xml","schema_latest.xml");
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void afterTests() throws Exception {
|
||||
JSONTestUtil.failRepeatedKeys = false;
|
||||
if (servers != null) {
|
||||
servers.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalJsonRequest() throws Exception {
|
||||
doJsonRequest(Client.localClient);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDistribJsonRequest() throws Exception {
|
||||
initServers();
|
||||
initServers();
|
||||
Client client = servers.getClient( random().nextInt() );
|
||||
client.queryDefaults().set( "shards", servers.getShards() );
|
||||
doJsonRequest(client);
|
||||
}
|
||||
|
||||
|
||||
public void doJsonRequest(Client client) throws Exception {
|
||||
client.deleteByQuery("*:*", null);
|
||||
client.add(sdoc("id", "1", "cat_s", "A", "where_s", "NY"), null);
|
||||
client.add(sdoc("id", "2", "cat_s", "B", "where_s", "NJ"), null);
|
||||
client.add(sdoc("id", "3"), null);
|
||||
client.commit();
|
||||
client.add(sdoc("id", "4", "cat_s", "A", "where_s", "NJ"), null);
|
||||
client.add(sdoc("id", "5", "cat_s", "B", "where_s", "NJ"), null);
|
||||
client.commit();
|
||||
client.add(sdoc("id", "6", "cat_s", "B", "where_s", "NY"), null);
|
||||
client.commit();
|
||||
|
||||
|
||||
// test json param
|
||||
client.testJQ( params("json","{query:'cat_s:A'}")
|
||||
, "response/numFound==2"
|
||||
);
|
||||
|
||||
// test multiple json params
|
||||
client.testJQ( params("json","{query:'cat_s:A'}", "json","{filter:'where_s:NY'}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test merging multi-valued params into list
|
||||
client.testJQ( params("json","{query:'*:*'}", "json","{filter:'where_s:NY'}", "json","{filter:'cat_s:A'}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test merging multi-valued params into list, second value is already list
|
||||
client.testJQ( params("json","{query:'*:*'}", "json","{filter:'where_s:NY'}", "json","{filter:['cat_s:A']}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test merging multi-valued params into list, first value is already list
|
||||
client.testJQ( params("json","{query:'*:*'}", "json","{filter:['where_s:NY']}", "json","{filter:'cat_s:A'}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test merging multi-valued params into list, both values are already list
|
||||
client.testJQ( params("json","{query:'*:*'}", "json","{filter:['where_s:NY']}", "json","{filter:['cat_s:A']}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test inserting and merging with paths
|
||||
client.testJQ( params("json.query","'*:*'", "json.filter","'where_s:NY'", "json.filter","'cat_s:A'")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test overwriting of non-multivalued params
|
||||
client.testJQ( params("json.query","'foo_s:NONE'", "json.filter","'where_s:NY'", "json.filter","'cat_s:A'", "json.query","'*:*'")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// normal parameter specified in the params block, including numeric params cast back to string
|
||||
client.testJQ( params("json","{params:{q:'*:*', fq:['cat_s:A','where_s:NY'], start:0, rows:5, fl:id}}")
|
||||
, "response/docs==[{id:'1'}]"
|
||||
);
|
||||
client.testJQ( params("json","{params:{q:'*:*', fq:['cat_s:A','where_s:(NY OR NJ)'], start:0, rows:1, fl:id, sort:'where_s asc'}}")
|
||||
, "response/numFound==2"
|
||||
, "response/docs==[{id:'4'}]"
|
||||
);
|
||||
client.testJQ( params("json","{params:{q:'*:*', fq:['cat_s:A','where_s:(NY OR NJ)'], start:0, rows:1, fl:[id,'x:5.5'], sort:'where_s asc'}}")
|
||||
, "response/numFound==2"
|
||||
, "response/docs==[{id:'4', x:5.5}]"
|
||||
);
|
||||
// test merge params
|
||||
client.testJQ( params("json","{params:{q:'*:*'}}", "json","{params:{fq:['cat_s:A','where_s:(NY OR NJ)'], start:0, rows:1, fl:[id,'x:5.5']}}", "json","{params:{sort:'where_s asc'}}")
|
||||
, "response/numFound==2"
|
||||
, "response/docs==[{id:'4', x:5.5}]"
|
||||
);
|
||||
|
||||
|
||||
// test offset/limit/sort/fields
|
||||
client.testJQ( params("json.query","'*:*'", "json.offset","1", "json.limit","2", "json.sort","'id desc'", "json.fields","'id'")
|
||||
, "response/docs==[{id:'5'},{id:'4'}]"
|
||||
);
|
||||
// test offset/limit/sort/fields, multi-valued json.fields
|
||||
client.testJQ( params("json.query","'*:*'", "json.offset","1", "json.limit","2", "json.sort","'id desc'", "json.fields","'id'", "json.fields","'x:5.5'")
|
||||
, "response/docs==[{id:'5', x:5.5},{id:'4', x:5.5}]"
|
||||
);
|
||||
// test offset/limit/sort/fields, overwriting non-multivalued params
|
||||
client.testJQ( params("json.query","'*:*'", "json.offset","17", "json.offset","1", "json.limit","42", "json.limit","2", "json.sort","'id asc'", "json.sort","'id desc'", "json.fields","'id'", "json.fields","'x:5.5'")
|
||||
, "response/docs==[{id:'5', x:5.5},{id:'4', x:5.5}]"
|
||||
);
|
||||
|
||||
|
||||
|
||||
// test templating before parsing JSON
|
||||
client.testJQ( params("json","${OPENBRACE} query:'cat_s:A' ${CLOSEBRACE}", "json","${OPENBRACE} filter:'where_s:NY'${CLOSEBRACE}", "OPENBRACE","{", "CLOSEBRACE","}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test templating with params defined in the JSON itself! Do we want to keep this functionality?
|
||||
client.testJQ( params("json","{params:{V1:A,V2:NY}, query:'cat_s:${V1}'}", "json","{filter:'where_s:${V2}'}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
|
||||
//
|
||||
// with body
|
||||
//
|
||||
client.testJQ(params("stream.body", "{query:'cat_s:A'}", "stream.contentType", "application/json")
|
||||
, "response/numFound==2"
|
||||
);
|
||||
|
||||
// test body in conjunction with query params
|
||||
client.testJQ(params("stream.body", "{query:'cat_s:A'}", "stream.contentType", "application/json", "json.filter", "'where_s:NY'")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test that json body in params come "after" (will overwrite)
|
||||
client.testJQ(params("stream.body", "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json","{query:'cat_s:A'}")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
// test that json.x params come after body
|
||||
client.testJQ(params("stream.body", "{query:'*:*', filter:'where_s:NY'}", "stream.contentType", "application/json", "json.query","'cat_s:A'")
|
||||
, "response/numFound==1"
|
||||
);
|
||||
|
||||
|
||||
// test facet with json body
|
||||
client.testJQ(params("stream.body", "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json")
|
||||
, "facets=={count:6,x:2}"
|
||||
);
|
||||
|
||||
// test facet with json body, insert additional facets via query parameter
|
||||
client.testJQ(params("stream.body", "{query:'*:*', facet:{x:'unique(where_s)'}}", "stream.contentType", "application/json", "json.facet.y","{terms:{field:where_s}}", "json.facet.z","'unique(where_s)'")
|
||||
, "facets=={count:6,x:2, y:{buckets:[{val:NJ,count:3},{val:NY,count:2}]}, z:2}"
|
||||
);
|
||||
|
||||
// test debug
|
||||
client.testJQ( params("json","{query:'cat_s:A'}", "json.filter","'where_s:NY'", "debug","true")
|
||||
, "debug/json=={query:'cat_s:A', filter:'where_s:NY'}"
|
||||
);
|
||||
|
||||
|
||||
try {
|
||||
// test failure on unknown parameter
|
||||
client.testJQ(params("json", "{query:'cat_s:A', foobar_ignore_exception:5}")
|
||||
, "response/numFound==2"
|
||||
);
|
||||
fail();
|
||||
} catch (Exception e) {
|
||||
assertTrue(e.getMessage().contains("foobar"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue