blob: c51e0d3f5a716a992a2b3638783f034154ee2d41 [file] [log] [blame]
/*
* 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.request.json;
import java.util.List;
import java.util.Map;
import org.apache.solr.common.SolrException;
/**
* Convert json query object to local params.
*
* @lucene.internal
*/
class JsonQueryConverter {
private int numParams = 0;
String toLocalParams(Object jsonQueryObject, Map<String, String[]> additionalParams) {
if (jsonQueryObject instanceof String) return jsonQueryObject.toString();
StringBuilder builder = new StringBuilder();
buildLocalParams(builder, jsonQueryObject, true, additionalParams);
return builder.toString();
}
private String putParam(String val, Map<String, String[]> additionalParams) {
String name = "_tt"+(numParams++);
additionalParams.put(name, new String[]{val});
return name;
}
// when isQParser==true, "val" is a query object of the form {query_type:{param1:val1, param2:val2}}
// when isQParser==false, "val" is a parameter on an existing qparser (which could be a simple parameter like 42, or a sub-query)
@SuppressWarnings({"unchecked"})
private void buildLocalParams(StringBuilder builder, Object val, boolean isQParser, Map<String, String[]> additionalParams) {
if (!isQParser && !(val instanceof Map)) {
// val is value of a query parser, and it is not a map
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Error when parsing json query, expect a json object here, but found : "+val);
// NOTE: a top-level query *can* be a String, so we should really allow it here. This currently only works because
// we special-case String in toLocalParams() and don't call this method.
}
// We don't want to introduce unnecessary variable at root level
boolean useSubBuilder = builder.length() > 0;
if (val instanceof String) {
if (!useSubBuilder) {
// Top level, so just use the value. NOTE: this case is also short-circuited in toLocalParams() for performance.
builder.append(val.toString());
} else {
// val is a parameter in a qparser, so use param deref and skip escaping: ...=$param1}&param1=<val>
builder.append('$').append(putParam(val.toString(), additionalParams));
}
return;
}
if (val instanceof Number) {
builder.append(val);
return;
}
if (!(val instanceof Map)) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Error when parsing json query, expect a json object here, but found : "+val);
}
Map<String,Object> map = (Map<String, Object>) val;
if (isQParser) {
if (map.size() != 1) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Error when parsing json query, expect only one query parser here, but found : "+map.keySet());
}
String qtype = map.keySet().iterator().next();
String tagName = null;
if (qtype.startsWith("#")) {
Object taggedQueryObject = map.get(qtype);
tagName = qtype.substring(1);
if (taggedQueryObject instanceof String) {
StringBuilder sb = new StringBuilder();
sb.append("{!tag=").append(tagName).append("}");
sb.append(taggedQueryObject.toString());
buildLocalParams(builder, sb.toString(), true, additionalParams);
return;
} else if (taggedQueryObject instanceof Map) {
map = (Map<String, Object>) taggedQueryObject;
qtype = map.keySet().iterator().next();
// FUTURE: might want to recurse here instead to handle nested tags (and add tagName as a parameter?)
}
} else {
if (qtype.equals("param")) {
boolean toplevel;
if (toplevel=(builder.length() == 0)) {
builder.append("{!v=");
}
builder.append("$").append(map.get("param"));
if (toplevel) {
builder.append("}");
}
return;
}
}
StringBuilder subBuilder = useSubBuilder ? new StringBuilder() : builder;
Object subVal = map.get(qtype);
subBuilder = subBuilder.append("{!").append(qtype).append(' ');
if (tagName != null) {
subBuilder.append("tag=").append(tagName).append(' ');
}
buildLocalParams(subBuilder, subVal, false, additionalParams);
subBuilder.append("}");
if (useSubBuilder) {
builder.append('$').append(putParam(subBuilder.toString(), additionalParams));
}
} else {
if(map.size()==1 && map.keySet().iterator().next().equals("param")) {
builder.append("v").append("=$").append(map.get("param")).append(" ");
} else {
for (Map.Entry<String, Object> entry : map.entrySet()) {
String key = entry.getKey();
if (entry.getValue() instanceof List) {
if (key.equals("query")) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Error when parsing json query, value of query field should not be a list, found : " + entry.getValue());
}
@SuppressWarnings({"rawtypes"})
List l = (List) entry.getValue();
for (Object subVal : l) {
builder.append(key).append("=");
buildLocalParams(builder, subVal, true, additionalParams);
builder.append(" ");
}
} else {
if (key.equals("query")) {
key = "v";
}
builder.append(key).append("=");
buildLocalParams(builder, entry.getValue(), true, additionalParams);
builder.append(" ");
}
}
}
}
}
}