XContent - An abstraction on top of content (JSON inspired), closes #152.

This commit is contained in:
kimchy 2010-04-29 23:10:47 +03:00
parent f8f65c991a
commit 34d99c39a5
39 changed files with 1393 additions and 255 deletions

View File

@ -82,6 +82,7 @@
<w>versioned</w>
<w>wildcards</w>
<w>xcontent</w>
<w>xson</w>
<w>yaml</w>
</words>
</dictionary>

View File

@ -19,18 +19,27 @@
package org.elasticsearch.action.count;
import org.elasticsearch.ElasticSearchGenerationException;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.Actions;
import org.elasticsearch.action.support.broadcast.BroadcastOperationRequest;
import org.elasticsearch.action.support.broadcast.BroadcastOperationThreading;
import org.elasticsearch.client.Requests;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.util.Required;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.XContentFactory;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.builder.BinaryXContentBuilder;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
/**
* A request to count the number of documents matching a specific query. Best created with
@ -46,6 +55,8 @@ import java.util.Arrays;
*/
public class CountRequest extends BroadcastOperationRequest {
private static final XContentType contentType = Requests.CONTENT_TYPE;
public static final float DEFAULT_MIN_SCORE = -1f;
private float minScore = DEFAULT_MIN_SCORE;
@ -53,6 +64,8 @@ public class CountRequest extends BroadcastOperationRequest {
private String[] types = Strings.EMPTY_ARRAY;
@Nullable private String queryParserName;
private transient QueryBuilder queryBuilder = null;
CountRequest() {
}
@ -64,6 +77,14 @@ public class CountRequest extends BroadcastOperationRequest {
super(indices, null);
}
@Override public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = super.validate();
if (querySource == null && queryBuilder == null) {
validationException = Actions.addValidationError("query is missing", validationException);
}
return validationException;
}
/**
* Controls the operation threading model.
*/
@ -113,6 +134,10 @@ public class CountRequest extends BroadcastOperationRequest {
* The query source to execute.
*/
byte[] querySource() {
if (querySource == null && queryBuilder != null) {
// did not get serialized...
querySource = queryBuilder.buildAsBytes(contentType);
}
return querySource;
}
@ -122,7 +147,22 @@ public class CountRequest extends BroadcastOperationRequest {
* @see org.elasticsearch.index.query.xcontent.QueryBuilders
*/
@Required public CountRequest query(QueryBuilder queryBuilder) {
return query(queryBuilder.buildAsBytes());
this.queryBuilder = queryBuilder;
return this;
}
/**
* The query source to execute in the form of a map.
*/
@Required public CountRequest query(Map querySource) {
try {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
builder.map(querySource);
this.querySource = builder.copiedBytes();
} catch (IOException e) {
throw new ElasticSearchGenerationException("Failed to generate [" + querySource + "]", e);
}
return this;
}
/**
@ -191,8 +231,14 @@ public class CountRequest extends BroadcastOperationRequest {
@Override public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeFloat(minScore);
out.writeVInt(querySource.length);
out.writeBytes(querySource);
if (querySource != null) {
out.writeVInt(querySource.length);
out.writeBytes(querySource);
} else {
FastByteArrayOutputStream os = queryBuilder.buildAsUnsafeBytes(contentType);
out.writeVInt(os.size());
out.writeBytes(os.unsafeByteArray(), 0, os.size());
}
if (queryParserName == null) {
out.writeBoolean(false);
} else {

View File

@ -22,11 +22,13 @@ package org.elasticsearch.action.deletebyquery;
import org.elasticsearch.ElasticSearchGenerationException;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.support.replication.IndicesReplicationOperationRequest;
import org.elasticsearch.client.Requests;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.util.Required;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.XContentFactory;
@ -53,10 +55,14 @@ import static org.elasticsearch.action.Actions.*;
*/
public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
private static final XContentType contentType = Requests.CONTENT_TYPE;
private byte[] querySource;
private String queryParserName;
private String[] types = Strings.EMPTY_ARRAY;
private transient QueryBuilder queryBuilder;
/**
* Constructs a new delete by query request to run against the provided indices. No indices means
* it will run against all indices.
@ -78,7 +84,7 @@ public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
@Override public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = super.validate();
if (querySource == null) {
if (querySource == null && queryBuilder == null) {
validationException = addValidationError("query is missing", validationException);
}
return validationException;
@ -93,6 +99,9 @@ public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
* The query source to execute.
*/
byte[] querySource() {
if (querySource == null && queryBuilder != null) {
querySource = queryBuilder.buildAsBytes();
}
return querySource;
}
@ -102,7 +111,8 @@ public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
* @see org.elasticsearch.index.query.xcontent.QueryBuilders
*/
@Required public DeleteByQueryRequest query(QueryBuilder queryBuilder) {
return query(queryBuilder.buildAsBytes());
this.queryBuilder = queryBuilder;
return this;
}
/**
@ -118,7 +128,7 @@ public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
*/
@Required public DeleteByQueryRequest query(Map querySource) {
try {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(XContentType.JSON);
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
builder.map(querySource);
this.querySource = builder.copiedBytes();
} catch (IOException e) {
@ -184,8 +194,14 @@ public class DeleteByQueryRequest extends IndicesReplicationOperationRequest {
public void writeTo(StreamOutput out) throws IOException {
super.writeTo(out);
out.writeVInt(querySource.length);
out.writeBytes(querySource);
if (querySource != null) {
out.writeVInt(querySource.length);
out.writeBytes(querySource);
} else {
FastByteArrayOutputStream os = queryBuilder.buildAsUnsafeBytes(contentType);
out.writeVInt(os.size());
out.writeBytes(os.unsafeByteArray(), 0, os.size());
}
if (queryParserName == null) {
out.writeBoolean(false);
} else {

View File

@ -105,6 +105,8 @@ public class IndexRequest extends ShardReplicationOperationRequest {
private byte[] source;
private OpType opType = OpType.INDEX;
private transient XContentBuilder sourceBuilder;
public IndexRequest() {
}
@ -136,7 +138,7 @@ public class IndexRequest extends ShardReplicationOperationRequest {
if (type == null) {
validationException = addValidationError("type is missing", validationException);
}
if (source == null) {
if (source == null && sourceBuilder == null) {
validationException = addValidationError("source is missing", validationException);
}
return validationException;
@ -201,6 +203,13 @@ public class IndexRequest extends ShardReplicationOperationRequest {
* The source of the JSON document to index.
*/
byte[] source() {
if (source == null && sourceBuilder != null) {
try {
source = sourceBuilder.copiedBytes();
} catch (IOException e) {
throw new ElasticSearchGenerationException("Failed to build source", e);
}
}
return source;
}
@ -234,12 +243,9 @@ public class IndexRequest extends ShardReplicationOperationRequest {
/**
* Sets the content source to index.
*/
@Required public IndexRequest source(XContentBuilder jsonBuilder) {
try {
return source(jsonBuilder.copiedBytes());
} catch (IOException e) {
throw new ElasticSearchIllegalArgumentException("Failed to build json for index request", e);
}
@Required public IndexRequest source(XContentBuilder sourceBuilder) {
this.sourceBuilder = sourceBuilder;
return this;
}
/**
@ -318,8 +324,13 @@ public class IndexRequest extends ShardReplicationOperationRequest {
out.writeBoolean(true);
out.writeUTF(id);
}
out.writeVInt(source.length);
out.writeBytes(source);
if (source != null) {
out.writeVInt(source.length);
out.writeBytes(source);
} else {
out.writeVInt(sourceBuilder.unsafeBytesLength());
out.writeBytes(sourceBuilder.unsafeBytes(), 0, sourceBuilder.unsafeBytesLength());
}
out.writeByte(opType.id());
}

View File

@ -25,12 +25,14 @@ import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.action.Actions;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.Requests;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.util.Bytes;
import org.elasticsearch.util.Required;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.XContentFactory;
@ -55,6 +57,8 @@ import static org.elasticsearch.search.Scroll.*;
*/
public class MoreLikeThisRequest implements ActionRequest {
private static final XContentType contentType = Requests.CONTENT_TYPE;
private String index;
private String type;
@ -80,6 +84,7 @@ public class MoreLikeThisRequest implements ActionRequest {
private Scroll searchScroll;
private byte[] searchSource;
private transient SearchSourceBuilder searchSourceBuiler;
private boolean threadedListener = false;
@ -306,7 +311,8 @@ public class MoreLikeThisRequest implements ActionRequest {
* more like this documents.
*/
public MoreLikeThisRequest searchSource(SearchSourceBuilder sourceBuilder) {
return searchSource(sourceBuilder.build());
this.searchSourceBuiler = sourceBuilder;
return this;
}
/**
@ -342,6 +348,9 @@ public class MoreLikeThisRequest implements ActionRequest {
* more like this documents.
*/
public byte[] searchSource() {
if (searchSource == null && searchSourceBuiler != null) {
searchSource = searchSourceBuiler.buildAsBytes(contentType);
}
return this.searchSource;
}
@ -589,11 +598,17 @@ public class MoreLikeThisRequest implements ActionRequest {
out.writeBoolean(true);
searchScroll.writeTo(out);
}
if (searchSource == null) {
if (searchSource == null && searchSourceBuiler == null) {
out.writeVInt(0);
} else {
out.writeVInt(searchSource.length);
out.writeBytes(searchSource);
if (searchSource != null) {
out.writeVInt(searchSource.length);
out.writeBytes(searchSource);
} else {
FastByteArrayOutputStream os = searchSourceBuiler.buildAsUnsafeBytes(contentType);
out.writeVInt(os.size());
out.writeBytes(os.unsafeByteArray(), 0, os.size());
}
}
}
}

View File

@ -23,12 +23,14 @@ import org.elasticsearch.ElasticSearchGenerationException;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.action.ActionRequest;
import org.elasticsearch.action.ActionRequestValidationException;
import org.elasticsearch.client.Requests;
import org.elasticsearch.search.Scroll;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.util.Bytes;
import org.elasticsearch.util.Strings;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.io.stream.StreamInput;
import org.elasticsearch.util.io.stream.StreamOutput;
import org.elasticsearch.util.xcontent.XContentFactory;
@ -58,6 +60,8 @@ import static org.elasticsearch.util.TimeValue.*;
*/
public class SearchRequest implements ActionRequest {
private static final XContentType contentType = Requests.CONTENT_TYPE;
private SearchType searchType = SearchType.DEFAULT;
private String[] indices;
@ -77,6 +81,9 @@ public class SearchRequest implements ActionRequest {
private boolean listenerThreaded = false;
private SearchOperationThreading operationThreading = SearchOperationThreading.SINGLE_THREAD;
private transient SearchSourceBuilder sourceBuilder;
private transient SearchSourceBuilder extraSourceBuilder;
SearchRequest() {
}
@ -98,7 +105,7 @@ public class SearchRequest implements ActionRequest {
@Override public ActionRequestValidationException validate() {
ActionRequestValidationException validationException = null;
if (source == null && extraSource == null) {
if (source == null && sourceBuilder == null && extraSource == null && extraSourceBuilder == null) {
validationException = addValidationError("search source is missing", validationException);
}
return validationException;
@ -188,7 +195,8 @@ public class SearchRequest implements ActionRequest {
* The source of the search request.
*/
public SearchRequest source(SearchSourceBuilder sourceBuilder) {
return source(sourceBuilder.build());
this.sourceBuilder = sourceBuilder;
return this;
}
/**
@ -204,7 +212,7 @@ public class SearchRequest implements ActionRequest {
*/
public SearchRequest source(Map source) {
try {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(XContentType.JSON);
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
builder.map(source);
this.source = builder.copiedBytes();
} catch (IOException e) {
@ -225,6 +233,9 @@ public class SearchRequest implements ActionRequest {
* The search source to execute.
*/
public byte[] source() {
if (source == null && sourceBuilder != null) {
source = sourceBuilder.buildAsBytes(contentType);
}
return source;
}
@ -232,12 +243,13 @@ public class SearchRequest implements ActionRequest {
* Allows to provide additional source that will be used as well.
*/
public SearchRequest extraSource(SearchSourceBuilder sourceBuilder) {
return extraSource(sourceBuilder.build());
this.extraSourceBuilder = sourceBuilder;
return this;
}
public SearchRequest extraSource(Map extraSource) {
try {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(XContentType.JSON);
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
builder.map(extraSource);
this.extraSource = builder.copiedBytes();
} catch (IOException e) {
@ -265,6 +277,9 @@ public class SearchRequest implements ActionRequest {
* Additional search source to execute.
*/
public byte[] extraSource() {
if (extraSource == null && extraSourceBuilder != null) {
extraSource = extraSourceBuilder.buildAsBytes(contentType);
}
return this.extraSource;
}
@ -405,17 +420,29 @@ public class SearchRequest implements ActionRequest {
out.writeBoolean(true);
timeout.writeTo(out);
}
if (source == null) {
if (source == null && sourceBuilder == null) {
out.writeVInt(0);
} else {
out.writeVInt(source.length);
out.writeBytes(source);
if (source != null) {
out.writeVInt(source.length);
out.writeBytes(source);
} else {
FastByteArrayOutputStream os = sourceBuilder.buildAsUnsafeBytes(contentType);
out.writeVInt(os.size());
out.writeBytes(os.unsafeByteArray(), 0, os.size());
}
}
if (extraSource == null) {
if (extraSource == null && extraSourceBuilder == null) {
out.writeVInt(0);
} else {
out.writeVInt(extraSource.length);
out.writeBytes(extraSource);
if (extraSource != null) {
out.writeVInt(extraSource.length);
out.writeBytes(extraSource);
} else {
FastByteArrayOutputStream os = extraSourceBuilder.buildAsUnsafeBytes(contentType);
out.writeVInt(os.size());
out.writeBytes(os.unsafeByteArray(), 0, os.size());
}
}
out.writeVInt(types.length);
for (String type : types) {

View File

@ -45,14 +45,20 @@ import org.elasticsearch.action.mlt.MoreLikeThisRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchScrollRequest;
import org.elasticsearch.action.terms.TermsRequest;
import org.elasticsearch.util.xcontent.XContentType;
/**
* A handy one stop shop for creating requests (make sure to import static this class).
*
* @author kimchy (Shay Banon)
* @author kimchy (shay.banon)
*/
public class Requests {
/**
* The content type used to generate request builders (query / search).
*/
public static XContentType CONTENT_TYPE = XContentType.XSON;
public static IndexRequest indexRequest() {
return new IndexRequest();
}

View File

@ -22,11 +22,9 @@ package org.elasticsearch.http.netty;
import org.elasticsearch.http.HttpRequest;
import org.elasticsearch.rest.support.AbstractRestRequest;
import org.elasticsearch.rest.support.RestUtils;
import org.jboss.netty.buffer.ChannelBufferInputStream;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpMethod;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -90,10 +88,6 @@ public class NettyHttpRequest extends AbstractRestRequest implements HttpRequest
return request.getContent().readableBytes() > 0;
}
@Override public InputStream contentAsStream() {
return new ChannelBufferInputStream(request.getContent());
}
@Override public byte[] contentAsBytes() {
byte[] data = new byte[request.getContent().readableBytes()];
request.getContent().getBytes(request.getContent().readerIndex(), data);

View File

@ -428,11 +428,19 @@ public class XContentObjectMapper implements XContentMapper, XContentIncludeInAl
} else if (token == XContentParser.Token.VALUE_NUMBER) {
XContentParser.NumberType numberType = context.parser().numberType();
if (numberType == XContentParser.NumberType.INT) {
mapper = integerField(currentFieldName).build(builderContext);
if (context.parser().estimatedNumberType()) {
mapper = longField(currentFieldName).build(builderContext);
} else {
mapper = integerField(currentFieldName).build(builderContext);
}
} else if (numberType == XContentParser.NumberType.LONG) {
mapper = longField(currentFieldName).build(builderContext);
} else if (numberType == XContentParser.NumberType.FLOAT) {
mapper = floatField(currentFieldName).build(builderContext);
if (context.parser().estimatedNumberType()) {
mapper = doubleField(currentFieldName).build(builderContext);
} else {
mapper = floatField(currentFieldName).build(builderContext);
}
} else if (numberType == XContentParser.NumberType.DOUBLE) {
mapper = doubleField(currentFieldName).build(builderContext);
}

View File

@ -23,7 +23,6 @@ import org.elasticsearch.util.SizeValue;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.xcontent.ToXContent;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -51,8 +50,6 @@ public interface RestRequest extends ToXContent.Params {
boolean hasContent();
InputStream contentAsStream();
byte[] contentAsBytes();
String contentAsString();

View File

@ -145,7 +145,7 @@ public class RestSearchAction extends BaseRestHandler {
return searchRequest;
}
private byte[] parseSearchSource(RestRequest request) {
private SearchSourceBuilder parseSearchSource(RestRequest request) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
String queryString = request.param("q");
if (queryString != null) {
@ -227,7 +227,6 @@ public class RestSearchAction extends BaseRestHandler {
}
}
// TODO add different parameters to the source
return searchSourceBuilder.build();
return searchSourceBuilder;
}
}

View File

@ -32,7 +32,18 @@ import java.io.IOException;
public class RestXContentBuilder {
public static BinaryXContentBuilder restContentBuilder(RestRequest request) throws IOException {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(XContentType.JSON);
XContentType contentType = XContentType.fromRestContentType(request.header("Content-Type"));
if (contentType == null) {
// try and guess it from the body, if exists
if (request.hasContent()) {
contentType = XContentFactory.xContentType(request.contentAsBytes());
}
}
if (contentType == null) {
// default to JSON
contentType = XContentType.JSON;
}
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
if (request.paramAsBoolean("pretty", false)) {
builder.prettyPrint();
}

View File

@ -20,14 +20,16 @@
package org.elasticsearch.search.builder;
import org.elasticsearch.index.query.xcontent.XContentQueryBuilder;
import org.elasticsearch.search.SearchException;
import org.elasticsearch.util.gnu.trove.TObjectFloatHashMap;
import org.elasticsearch.util.gnu.trove.TObjectFloatIterator;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.xcontent.ToXContent;
import org.elasticsearch.util.xcontent.XContentFactory;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.builder.BinaryXContentBuilder;
import org.elasticsearch.util.xcontent.builder.XContentBuilder;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -40,7 +42,7 @@ import static org.elasticsearch.util.gcommon.collect.Lists.*;
* @author kimchy (shay.banon)
* @see org.elasticsearch.action.search.SearchRequest#source(SearchSourceBuilder)
*/
public class SearchSourceBuilder {
public class SearchSourceBuilder implements ToXContent {
public static enum Order {
ASC,
@ -253,87 +255,106 @@ public class SearchSourceBuilder {
return this;
}
public FastByteArrayOutputStream buildAsUnsafeBytes() throws SearchSourceBuilderException {
return buildAsUnsafeBytes(XContentType.JSON);
}
public byte[] build() throws SearchException {
public FastByteArrayOutputStream buildAsUnsafeBytes(XContentType contentType) throws SearchSourceBuilderException {
try {
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
builder.startObject();
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
toXContent(builder, ToXContent.EMPTY_PARAMS);
return builder.unsafeStream();
} catch (Exception e) {
throw new SearchSourceBuilderException("Failed to build search source", e);
}
}
ToXContent.Params params = ToXContent.EMPTY_PARAMS;
if (from != -1) {
builder.field("from", from);
}
if (size != -1) {
builder.field("size", size);
}
if (queryParserName != null) {
builder.field("query_parser_name", queryParserName);
}
if (queryBuilder != null) {
builder.field("query");
queryBuilder.toXContent(builder, params);
}
if (explain != null) {
builder.field("explain", explain);
}
if (fieldNames != null) {
if (fieldNames.size() == 1) {
builder.field("fields", fieldNames.get(0));
} else {
builder.startArray("fields");
for (String fieldName : fieldNames) {
builder.value(fieldName);
}
builder.endArray();
}
}
if (sortFields != null) {
builder.field("sort");
builder.startObject();
for (SortTuple sortTuple : sortFields) {
builder.field(sortTuple.fieldName());
builder.startObject();
if (sortTuple.reverse) {
builder.field("reverse", true);
}
if (sortTuple.type != null) {
builder.field("type", sortTuple.type());
}
builder.endObject();
}
builder.endObject();
}
if (indexBoost != null) {
builder.startObject("indices_boost");
for (TObjectFloatIterator<String> it = indexBoost.iterator(); it.hasNext();) {
it.advance();
builder.field(it.key(), it.value());
}
builder.endObject();
}
if (facetsBuilder != null) {
facetsBuilder.toXContent(builder, params);
}
if (highlightBuilder != null) {
highlightBuilder.toXContent(builder, params);
}
builder.endObject();
public byte[] buildAsBytes() throws SearchSourceBuilderException {
return buildAsBytes(XContentType.JSON);
}
public byte[] buildAsBytes(XContentType contentType) throws SearchSourceBuilderException {
try {
XContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
toXContent(builder, EMPTY_PARAMS);
return builder.copiedBytes();
} catch (Exception e) {
throw new SearchSourceBuilderException("Failed to build search source", e);
}
}
@Override public void toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
if (from != -1) {
builder.field("from", from);
}
if (size != -1) {
builder.field("size", size);
}
if (queryParserName != null) {
builder.field("query_parser_name", queryParserName);
}
if (queryBuilder != null) {
builder.field("query");
queryBuilder.toXContent(builder, params);
}
if (explain != null) {
builder.field("explain", explain);
}
if (fieldNames != null) {
if (fieldNames.size() == 1) {
builder.field("fields", fieldNames.get(0));
} else {
builder.startArray("fields");
for (String fieldName : fieldNames) {
builder.value(fieldName);
}
builder.endArray();
}
}
if (sortFields != null) {
builder.field("sort");
builder.startObject();
for (SortTuple sortTuple : sortFields) {
builder.field(sortTuple.fieldName());
builder.startObject();
if (sortTuple.reverse) {
builder.field("reverse", true);
}
if (sortTuple.type != null) {
builder.field("type", sortTuple.type());
}
builder.endObject();
}
builder.endObject();
}
if (indexBoost != null) {
builder.startObject("indices_boost");
for (TObjectFloatIterator<String> it = indexBoost.iterator(); it.hasNext();) {
it.advance();
builder.field(it.key(), it.value());
}
builder.endObject();
}
if (facetsBuilder != null) {
facetsBuilder.toXContent(builder, params);
}
if (highlightBuilder != null) {
highlightBuilder.toXContent(builder, params);
}
builder.endObject();
}
private static class SortTuple {
private final String fieldName;
private final boolean reverse;

View File

@ -25,6 +25,8 @@ import java.io.IOException;
import java.util.Map;
/**
* An interface allowing to transfer an object to "XContent" using an {@link org.elasticsearch.util.xcontent.builder.XContentBuilder}.
*
* @author kimchy (shay.banon)
*/
public interface ToXContent {

View File

@ -22,23 +22,49 @@ package org.elasticsearch.util.xcontent;
import java.io.*;
/**
* A generic abstraction on top of handling content, inspired by JSON and pull parsing.
*
* @author kimchy (shay.banon)
*/
public interface XContent {
/**
* The type this content handles and produces.
*/
XContentType type();
/**
* Creates a new generator using the provided output stream.
*/
XContentGenerator createGenerator(OutputStream os) throws IOException;
/**
* Creates a new generator using the provided writer.
*/
XContentGenerator createGenerator(Writer writer) throws IOException;
/**
* Creates a parser over the provided string content.
*/
XContentParser createParser(String content) throws IOException;
/**
* Creates a parser over the provided input stream.
*/
XContentParser createParser(InputStream is) throws IOException;
/**
* Creates a parser over the provided bytes.
*/
XContentParser createParser(byte[] data) throws IOException;
/**
* Creates a parser over the provided bytes.
*/
XContentParser createParser(byte[] data, int offset, int length) throws IOException;
/**
* Creates a parser over the provided reader.
*/
XContentParser createParser(Reader reader) throws IOException;
}

View File

@ -20,14 +20,17 @@
package org.elasticsearch.util.xcontent;
import org.elasticsearch.ElasticSearchIllegalArgumentException;
import org.elasticsearch.ElasticSearchIllegalStateException;
import org.elasticsearch.util.xcontent.builder.BinaryXContentBuilder;
import org.elasticsearch.util.xcontent.builder.TextXContentBuilder;
import org.elasticsearch.util.xcontent.json.JsonXContent;
import org.elasticsearch.util.xcontent.xson.XsonType;
import org.elasticsearch.util.xcontent.xson.XsonXContent;
import java.io.IOException;
/**
* A one stop to use {@link org.elasticsearch.util.xcontent.XContent} and {@link org.elasticsearch.util.xcontent.builder.XContentBuilder}.
*
* @author kimchy (shay.banon)
*/
public class XContentFactory {
@ -37,28 +40,52 @@ public class XContentFactory {
private static final XContent[] contents;
static {
contents = new XContent[1];
contents = new XContent[2];
contents[0] = new JsonXContent();
contents[1] = new XsonXContent();
}
/**
* Returns a binary content builder using JSON format ({@link org.elasticsearch.util.xcontent.XContentType#JSON}.
*/
public static BinaryXContentBuilder jsonBuilder() throws IOException {
return contentBinaryBuilder(XContentType.JSON);
}
/**
* Returns a binary content builder using XSON format ({@link org.elasticsearch.util.xcontent.XContentType#XSON}.
*/
public static BinaryXContentBuilder xsonBuilder() throws IOException {
return contentBinaryBuilder(XContentType.XSON);
}
/**
* Returns a binary content builder for the provided content type.
*/
public static BinaryXContentBuilder contentBuilder(XContentType type) throws IOException {
if (type == XContentType.JSON) {
return JsonXContent.contentBinaryBuilder();
} else if (type == XContentType.XSON) {
return XsonXContent.contentBinaryBuilder();
}
throw new ElasticSearchIllegalArgumentException("No matching content type for " + type);
}
/**
* Returns a binary content builder for the provided content type.
*/
public static BinaryXContentBuilder contentBinaryBuilder(XContentType type) throws IOException {
if (type == XContentType.JSON) {
return JsonXContent.contentBinaryBuilder();
} else if (type == XContentType.XSON) {
return XsonXContent.contentBinaryBuilder();
}
throw new ElasticSearchIllegalArgumentException("No matching content type for " + type);
}
/**
* Returns a textual content builder for the provided content type. Note, XSON does not support this... .
*/
public static TextXContentBuilder contentTextBuilder(XContentType type) throws IOException {
if (type == XContentType.JSON) {
return JsonXContent.contentTextBuilder();
@ -66,10 +93,16 @@ public class XContentFactory {
throw new ElasticSearchIllegalArgumentException("No matching content type for " + type);
}
/**
* Returns the {@link org.elasticsearch.util.xcontent.XContent} for the provided content type.
*/
public static XContent xContent(XContentType type) {
return contents[type.index()];
}
/**
* Guesses the content type based on the provided char sequence.
*/
public static XContentType xContentType(CharSequence content) {
int length = content.length() < GUESS_HEADER_LENGTH ? content.length() : GUESS_HEADER_LENGTH;
for (int i = 0; i < length; i++) {
@ -78,32 +111,50 @@ public class XContentFactory {
return XContentType.JSON;
}
}
throw new ElasticSearchIllegalStateException("Failed to derive xContent from byte stream");
return null;
}
/**
* Guesses the content (type) based on the provided char sequence.
*/
public static XContent xContent(CharSequence content) {
return xContent(xContentType(content));
}
/**
* Guesses the content type based on the provided bytes.
*/
public static XContent xContent(byte[] data) {
return xContent(data, 0, data.length);
}
/**
* Guesses the content type based on the provided bytes.
*/
public static XContent xContent(byte[] data, int offset, int length) {
return xContent(xContentType(data, offset, length));
}
/**
* Guesses the content type based on the provided bytes.
*/
public static XContentType xContentType(byte[] data) {
return xContentType(data, 0, data.length);
}
/**
* Guesses the content type based on the provided bytes.
*/
public static XContentType xContentType(byte[] data, int offset, int length) {
length = length < GUESS_HEADER_LENGTH ? length : GUESS_HEADER_LENGTH;
if (length > 1 && data[0] == XsonType.HEADER) {
return XContentType.XSON;
}
for (int i = offset; i < length; i++) {
if (data[i] == '{') {
return XContentType.JSON;
}
}
throw new ElasticSearchIllegalStateException("Failed to derive xContent from byte stream");
return null;
}
}

View File

@ -20,8 +20,6 @@
package org.elasticsearch.util.xcontent;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @author kimchy (shay.banon)
@ -54,14 +52,10 @@ public interface XContentGenerator {
void writeNumber(long v) throws IOException;
void writeNumber(BigInteger v) throws IOException;
void writeNumber(double d) throws IOException;
void writeNumber(float f) throws IOException;
void writeNumber(BigDecimal dec) throws IOException;
void writeBoolean(boolean state) throws IOException;
void writeNull() throws IOException;
@ -81,8 +75,6 @@ public interface XContentGenerator {
void writeNumberField(String fieldName, float value) throws IOException;
void writeNumberField(String fieldName, BigDecimal value) throws IOException;
void writeBinaryField(String fieldName, byte[] data) throws IOException;
void writeArrayFieldStart(String fieldName) throws IOException;

View File

@ -20,7 +20,6 @@
package org.elasticsearch.util.xcontent;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Map;
/**
@ -78,7 +77,7 @@ public interface XContentParser {
}
enum NumberType {
INT, LONG, BIG_INTEGER, FLOAT, DOUBLE, BIG_DECIMAL
INT, LONG, FLOAT, DOUBLE
}
XContentType contentType();
@ -107,7 +106,11 @@ public interface XContentParser {
NumberType numberType() throws IOException;
byte byteValue() throws IOException;
/**
* Is the number type estimated or not (i.e. an int might actually be a long, its just low enough
* to be an int).
*/
boolean estimatedNumberType();
short shortValue() throws IOException;
@ -115,14 +118,10 @@ public interface XContentParser {
long longValue() throws IOException;
BigInteger bigIntegerValue() throws IOException;
float floatValue() throws IOException;
double doubleValue() throws IOException;
java.math.BigDecimal decimalValue() throws IOException;
boolean booleanValue() throws IOException;
byte[] binaryValue() throws IOException;

View File

@ -24,7 +24,23 @@ package org.elasticsearch.util.xcontent;
*/
public enum XContentType {
JSON(0);
JSON(0),
XSON(1);
public static XContentType fromRestContentType(String contentType) {
if (contentType == null) {
return null;
}
if ("application/json".equals(contentType)) {
return JSON;
}
if ("application/xson".equals(contentType)) {
return XSON;
}
return null;
}
private int index;

View File

@ -36,6 +36,8 @@ import org.elasticsearch.util.xcontent.builder.TextXContentBuilder;
import java.io.*;
/**
* A JSON based content implementation using Jackson.
*
* @author kimchy (shay.banon)
*/
public class JsonXContent implements XContent {

View File

@ -24,8 +24,6 @@ import org.elasticsearch.util.xcontent.XContentGenerator;
import org.elasticsearch.util.xcontent.XContentType;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @author kimchy (shay.banon)
@ -90,10 +88,6 @@ public class JsonXContentGenerator implements XContentGenerator {
generator.writeNumber(v);
}
@Override public void writeNumber(BigInteger v) throws IOException {
generator.writeNumber(v);
}
@Override public void writeNumber(double d) throws IOException {
generator.writeNumber(d);
}
@ -102,10 +96,6 @@ public class JsonXContentGenerator implements XContentGenerator {
generator.writeNumber(f);
}
@Override public void writeNumber(BigDecimal dec) throws IOException {
generator.writeNumber(dec);
}
@Override public void writeBoolean(boolean state) throws IOException {
generator.writeBoolean(state);
}
@ -142,10 +132,6 @@ public class JsonXContentGenerator implements XContentGenerator {
generator.writeNumberField(fieldName, value);
}
@Override public void writeNumberField(String fieldName, BigDecimal value) throws IOException {
generator.writeNumberField(fieldName, value);
}
@Override public void writeBinaryField(String fieldName, byte[] data) throws IOException {
generator.writeBinaryField(fieldName, data);
}

View File

@ -22,14 +22,10 @@ package org.elasticsearch.util.xcontent.json;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;
import org.elasticsearch.ElasticSearchIllegalStateException;
import org.elasticsearch.util.xcontent.XContentParser;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.support.AbstractXContentParser;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Map;
/**
* @author kimchy (shay.banon)
@ -62,6 +58,10 @@ public class JsonXContentParser extends AbstractXContentParser {
return convertNumberType(parser.getNumberType());
}
@Override public boolean estimatedNumberType() {
return true;
}
@Override public String currentName() throws IOException {
return parser.getCurrentName();
}
@ -90,10 +90,6 @@ public class JsonXContentParser extends AbstractXContentParser {
return parser.getNumberValue();
}
@Override public byte byteValue() throws IOException {
return parser.getByteValue();
}
@Override public short doShortValue() throws IOException {
return parser.getShortValue();
}
@ -106,10 +102,6 @@ public class JsonXContentParser extends AbstractXContentParser {
return parser.getLongValue();
}
@Override public BigInteger bigIntegerValue() throws IOException {
return parser.getBigIntegerValue();
}
@Override public float doFloatValue() throws IOException {
return parser.getFloatValue();
}
@ -118,10 +110,6 @@ public class JsonXContentParser extends AbstractXContentParser {
return parser.getDoubleValue();
}
@Override public BigDecimal decimalValue() throws IOException {
return parser.getDecimalValue();
}
@Override public byte[] binaryValue() throws IOException {
return parser.getBinaryValue();
}
@ -144,10 +132,6 @@ public class JsonXContentParser extends AbstractXContentParser {
return NumberType.FLOAT;
case DOUBLE:
return NumberType.DOUBLE;
case BIG_DECIMAL:
return NumberType.BIG_DECIMAL;
case BIG_INTEGER:
return NumberType.BIG_INTEGER;
}
throw new ElasticSearchIllegalStateException("No matching token for number_type [" + numberType + "]");
}

View File

@ -0,0 +1,80 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.support;
import org.elasticsearch.util.xcontent.XContentGenerator;
import java.io.IOException;
/**
* @author kimchy (shay.banon)
*/
public abstract class AbstractXContentGenerator implements XContentGenerator {
@Override public void writeStringField(String fieldName, String value) throws IOException {
writeFieldName(fieldName);
writeString(value);
}
@Override public void writeBooleanField(String fieldName, boolean value) throws IOException {
writeFieldName(fieldName);
writeBoolean(value);
}
@Override public void writeNullField(String fieldName) throws IOException {
writeFieldName(fieldName);
writeNull();
}
@Override public void writeNumberField(String fieldName, int value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
@Override public void writeNumberField(String fieldName, long value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
@Override public void writeNumberField(String fieldName, double value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
@Override public void writeNumberField(String fieldName, float value) throws IOException {
writeFieldName(fieldName);
writeNumber(value);
}
@Override public void writeBinaryField(String fieldName, byte[] data) throws IOException {
writeFieldName(fieldName);
writeBinary(data);
}
@Override public void writeArrayFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartArray();
}
@Override public void writeObjectFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
writeStartObject();
}
}

View File

@ -0,0 +1,52 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.xson;
/**
* @author kimchy (shay.banon)
*/
public enum XsonType {
START_ARRAY((byte) 0x01),
END_ARRAY((byte) 0x02),
START_OBJECT((byte) 0x03),
END_OBJECT((byte) 0x04),
FIELD_NAME((byte) 0x05),
VALUE_STRING((byte) 0x06),
VALUE_BINARY((byte) 0x07),
VALUE_INTEGER((byte) 0x08),
VALUE_LONG((byte) 0x09),
VALUE_FLOAT((byte) 0x0A),
VALUE_DOUBLE((byte) 0x0B),
VALUE_BOOLEAN((byte) 0x0C),
VALUE_NULL((byte) 0x0D),;
public static final int HEADER = 0x00;
private final byte code;
XsonType(byte code) {
this.code = code;
}
public byte code() {
return code;
}
}

View File

@ -0,0 +1,99 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.xson;
import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.ElasticSearchIllegalStateException;
import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.io.FastByteArrayInputStream;
import org.elasticsearch.util.xcontent.XContent;
import org.elasticsearch.util.xcontent.XContentGenerator;
import org.elasticsearch.util.xcontent.XContentParser;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.builder.BinaryXContentBuilder;
import java.io.*;
/**
* A binary representation of content (basically, JSON encoded in optimized binary format).
*
* @author kimchy (shay.banon)
*/
public class XsonXContent implements XContent {
public static class CachedBinaryBuilder {
private static final ThreadLocal<ThreadLocals.CleanableValue<BinaryXContentBuilder>> cache = new ThreadLocal<ThreadLocals.CleanableValue<BinaryXContentBuilder>>() {
@Override protected ThreadLocals.CleanableValue<BinaryXContentBuilder> initialValue() {
try {
BinaryXContentBuilder builder = new BinaryXContentBuilder(new XsonXContent());
return new ThreadLocals.CleanableValue<BinaryXContentBuilder>(builder);
} catch (IOException e) {
throw new ElasticSearchException("Failed to create xson generator", e);
}
}
};
/**
* Returns the cached thread local generator, with its internal {@link StringBuilder} cleared.
*/
static BinaryXContentBuilder cached() throws IOException {
ThreadLocals.CleanableValue<BinaryXContentBuilder> cached = cache.get();
cached.get().reset();
return cached.get();
}
}
public static BinaryXContentBuilder contentBinaryBuilder() throws IOException {
return CachedBinaryBuilder.cached();
}
@Override public XContentType type() {
return XContentType.XSON;
}
@Override public XContentGenerator createGenerator(OutputStream os) throws IOException {
return new XsonXContentGenerator(os);
}
@Override public XContentGenerator createGenerator(Writer writer) throws IOException {
throw new ElasticSearchIllegalStateException("Can't create generator over xson with textual data");
}
@Override public XContentParser createParser(String content) throws IOException {
throw new ElasticSearchIllegalStateException("Can't create parser over xson for textual data");
}
@Override public XContentParser createParser(InputStream is) throws IOException {
return new XsonXContentParser(is);
}
@Override public XContentParser createParser(byte[] data) throws IOException {
return new XsonXContentParser(new FastByteArrayInputStream(data));
}
@Override public XContentParser createParser(byte[] data, int offset, int length) throws IOException {
return new XsonXContentParser(new FastByteArrayInputStream(data, offset, length));
}
@Override public XContentParser createParser(Reader reader) throws IOException {
throw new ElasticSearchIllegalStateException("Can't create parser over xson for textual data");
}
}

View File

@ -0,0 +1,211 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.xson;
import org.apache.lucene.util.UnicodeUtil;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.xcontent.XContentGenerator;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.support.AbstractXContentGenerator;
import java.io.IOException;
import java.io.OutputStream;
/**
* @author kimchy (shay.banon)
*/
public class XsonXContentGenerator extends AbstractXContentGenerator implements XContentGenerator {
private final OutputStream out;
public XsonXContentGenerator(OutputStream out) throws IOException {
this.out = out;
outInt(XsonType.HEADER);
}
@Override public XContentType contentType() {
return XContentType.XSON;
}
@Override public void usePrettyPrint() {
// irrelevant
}
@Override public void writeStartArray() throws IOException {
out.write(XsonType.START_ARRAY.code());
}
@Override public void writeEndArray() throws IOException {
out.write(XsonType.END_ARRAY.code());
}
@Override public void writeStartObject() throws IOException {
out.write(XsonType.START_OBJECT.code());
}
@Override public void writeEndObject() throws IOException {
out.write(XsonType.END_OBJECT.code());
}
@Override public void writeFieldName(String name) throws IOException {
out.write(XsonType.FIELD_NAME.code());
outUTF(name);
}
@Override public void writeString(String text) throws IOException {
out.write(XsonType.VALUE_STRING.code());
outUTF(text);
}
@Override public void writeString(char[] text, int offset, int len) throws IOException {
writeString(new String(text, offset, len));
}
@Override public void writeBinary(byte[] data, int offset, int len) throws IOException {
out.write(XsonType.VALUE_BINARY.code());
outVInt(len);
out.write(data, offset, len);
}
@Override public void writeBinary(byte[] data) throws IOException {
out.write(XsonType.VALUE_BINARY.code());
outVInt(data.length);
out.write(data);
}
@Override public void writeNumber(int v) throws IOException {
out.write(XsonType.VALUE_INTEGER.code());
outInt(v);
}
@Override public void writeNumber(long v) throws IOException {
out.write(XsonType.VALUE_LONG.code());
outLong(v);
}
@Override public void writeNumber(double d) throws IOException {
out.write(XsonType.VALUE_DOUBLE.code());
outDouble(d);
}
@Override public void writeNumber(float f) throws IOException {
out.write(XsonType.VALUE_FLOAT.code());
outFloat(f);
}
@Override public void writeBoolean(boolean state) throws IOException {
out.write(XsonType.VALUE_BOOLEAN.code());
outBoolean(state);
}
@Override public void writeNull() throws IOException {
out.write(XsonType.VALUE_NULL.code());
}
@Override public void writeRawFieldStart(String fieldName) throws IOException {
writeFieldName(fieldName);
}
@Override public void flush() throws IOException {
out.flush();
}
@Override public void close() throws IOException {
out.close();
}
private void outShort(short v) throws IOException {
out.write((byte) (v >> 8));
out.write((byte) v);
}
/**
* Writes an int as four bytes.
*/
private void outInt(int i) throws IOException {
out.write((byte) (i >> 24));
out.write((byte) (i >> 16));
out.write((byte) (i >> 8));
out.write((byte) i);
}
/**
* Writes an int in a variable-length format. Writes between one and
* five bytes. Smaller values take fewer bytes. Negative numbers are not
* supported.
*/
private void outVInt(int i) throws IOException {
while ((i & ~0x7F) != 0) {
out.write((byte) ((i & 0x7f) | 0x80));
i >>>= 7;
}
out.write((byte) i);
}
/**
* Writes a long as eight bytes.
*/
private void outLong(long i) throws IOException {
outInt((int) (i >> 32));
outInt((int) i);
}
/**
* Writes an long in a variable-length format. Writes between one and five
* bytes. Smaller values take fewer bytes. Negative numbers are not
* supported.
*/
private void outVLong(long i) throws IOException {
while ((i & ~0x7F) != 0) {
out.write((byte) ((i & 0x7f) | 0x80));
i >>>= 7;
}
out.write((byte) i);
}
/**
* Writes a string.
*/
private void outUTF(String s) throws IOException {
UnicodeUtil.UTF8Result utf8Result = Unicode.unsafeFromStringAsUtf8(s);
outVInt(utf8Result.length);
out.write(utf8Result.result, 0, utf8Result.length);
}
private void outFloat(float v) throws IOException {
outInt(Float.floatToIntBits(v));
}
private void outDouble(double v) throws IOException {
outLong(Double.doubleToLongBits(v));
}
private static byte ZERO = 0;
private static byte ONE = 1;
/**
* Writes a boolean.
*/
private void outBoolean(boolean b) throws IOException {
out.write(b ? ONE : ZERO);
}
}

View File

@ -0,0 +1,390 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.xson;
import org.apache.lucene.util.StringHelper;
import org.elasticsearch.util.ThreadLocals;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.xcontent.XContentType;
import org.elasticsearch.util.xcontent.support.AbstractXContentParser;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
/**
* @author kimchy (shay.banon)
*/
public class XsonXContentParser extends AbstractXContentParser {
private static ThreadLocal<ThreadLocals.CleanableValue<byte[]>> cachedBytes = new ThreadLocal<ThreadLocals.CleanableValue<byte[]>>() {
@Override protected ThreadLocals.CleanableValue<byte[]> initialValue() {
return new ThreadLocals.CleanableValue<byte[]>(new byte[256]);
}
};
private final InputStream is;
private Token currentToken;
private XsonType xsonType;
private NumberType currentNumberType;
private String currentName;
private Unicode.UTF16Result utf16Result;
private int valueInt;
private long valueLong;
private float valueFloat;
private double valueDouble;
private boolean valueBoolean;
private byte[] valueBytes;
public XsonXContentParser(InputStream is) throws IOException {
this.is = is;
int header = inInt();
if (header != XsonType.HEADER) {
throw new IOException("Not xson type header");
}
}
@Override public XContentType contentType() {
return XContentType.XSON;
}
@Override public Token nextToken() throws IOException {
byte tokenType = (byte) is.read();
if (tokenType == -1) {
xsonType = null;
currentToken = null;
currentNumberType = null;
return null;
} else if (tokenType == XsonType.START_ARRAY.code()) {
xsonType = XsonType.START_ARRAY;
currentToken = Token.START_ARRAY;
} else if (tokenType == XsonType.END_ARRAY.code()) {
xsonType = XsonType.END_ARRAY;
currentToken = Token.END_ARRAY;
} else if (tokenType == XsonType.START_OBJECT.code()) {
xsonType = XsonType.START_OBJECT;
currentToken = Token.START_OBJECT;
} else if (tokenType == XsonType.END_OBJECT.code()) {
xsonType = XsonType.END_OBJECT;
currentToken = Token.END_OBJECT;
} else if (tokenType == XsonType.FIELD_NAME.code()) {
xsonType = XsonType.FIELD_NAME;
currentToken = Token.FIELD_NAME;
// read the field name (interned)
currentName = StringHelper.intern(inUTF());
} else if (tokenType == XsonType.VALUE_STRING.code()) {
xsonType = XsonType.VALUE_STRING;
currentToken = Token.VALUE_STRING;
inUtf16();
} else if (tokenType == XsonType.VALUE_BINARY.code()) {
xsonType = XsonType.VALUE_BINARY;
currentToken = Token.VALUE_STRING;
int length = inVInt();
valueBytes = new byte[length];
inBytes(valueBytes, 0, length);
} else if (tokenType == XsonType.VALUE_INTEGER.code()) {
xsonType = XsonType.VALUE_INTEGER;
currentToken = Token.VALUE_NUMBER;
currentNumberType = NumberType.INT;
valueInt = inInt();
} else if (tokenType == XsonType.VALUE_LONG.code()) {
xsonType = XsonType.VALUE_LONG;
currentToken = Token.VALUE_NUMBER;
currentNumberType = NumberType.LONG;
valueLong = inLong();
} else if (tokenType == XsonType.VALUE_FLOAT.code()) {
xsonType = XsonType.VALUE_FLOAT;
currentToken = Token.VALUE_NUMBER;
currentNumberType = NumberType.FLOAT;
valueFloat = inFloat();
} else if (tokenType == XsonType.VALUE_DOUBLE.code()) {
xsonType = XsonType.VALUE_DOUBLE;
currentToken = Token.VALUE_NUMBER;
currentNumberType = NumberType.DOUBLE;
valueDouble = inDouble();
} else if (tokenType == XsonType.VALUE_BOOLEAN.code()) {
xsonType = XsonType.VALUE_BOOLEAN;
currentToken = Token.VALUE_BOOLEAN;
valueBoolean = inBoolean();
} else if (tokenType == XsonType.VALUE_NULL.code()) {
xsonType = XsonType.VALUE_NULL;
currentToken = Token.VALUE_NULL;
}
return currentToken;
}
@Override public void skipChildren() throws IOException {
if (xsonType != XsonType.START_OBJECT && xsonType != XsonType.START_ARRAY) {
return;
}
int open = 1;
/* Since proper matching of start/end markers is handled
* by nextToken(), we'll just count nesting levels here
*/
while (true) {
nextToken();
if (xsonType == null) {
return;
}
switch (xsonType) {
case START_OBJECT:
case START_ARRAY:
++open;
break;
case END_OBJECT:
case END_ARRAY:
if (--open == 0) {
return;
}
break;
}
}
}
@Override public Token currentToken() {
return currentToken;
}
@Override public String currentName() throws IOException {
return currentName;
}
@Override public String text() throws IOException {
return new String(utf16Result.result, 0, utf16Result.length);
}
@Override public char[] textCharacters() throws IOException {
return utf16Result.result;
}
@Override public int textLength() throws IOException {
return utf16Result.length;
}
@Override public int textOffset() throws IOException {
return 0;
}
@Override public Number numberValue() throws IOException {
if (currentNumberType == NumberType.INT) {
return valueInt;
} else if (currentNumberType == NumberType.LONG) {
return valueLong;
} else if (currentNumberType == NumberType.FLOAT) {
return valueFloat;
} else if (currentNumberType == NumberType.DOUBLE) {
return valueDouble;
}
throw new IOException("No number type");
}
@Override public NumberType numberType() throws IOException {
return currentNumberType;
}
@Override public boolean estimatedNumberType() {
return false;
}
@Override public byte[] binaryValue() throws IOException {
return valueBytes;
}
@Override protected boolean doBooleanValue() throws IOException {
return valueBoolean;
}
@Override protected short doShortValue() throws IOException {
if (currentNumberType == NumberType.INT) {
return (short) valueInt;
} else if (currentNumberType == NumberType.LONG) {
return (short) valueLong;
} else if (currentNumberType == NumberType.FLOAT) {
return (short) valueFloat;
} else if (currentNumberType == NumberType.DOUBLE) {
return (short) valueDouble;
}
throw new IOException("No number type");
}
@Override protected int doIntValue() throws IOException {
if (currentNumberType == NumberType.INT) {
return valueInt;
} else if (currentNumberType == NumberType.LONG) {
return (int) valueLong;
} else if (currentNumberType == NumberType.FLOAT) {
return (int) valueFloat;
} else if (currentNumberType == NumberType.DOUBLE) {
return (int) valueDouble;
}
throw new IOException("No number type");
}
@Override protected long doLongValue() throws IOException {
if (currentNumberType == NumberType.LONG) {
return valueLong;
} else if (currentNumberType == NumberType.INT) {
return (long) valueInt;
} else if (currentNumberType == NumberType.FLOAT) {
return (long) valueFloat;
} else if (currentNumberType == NumberType.DOUBLE) {
return (long) valueDouble;
}
throw new IOException("No number type");
}
@Override protected float doFloatValue() throws IOException {
if (currentNumberType == NumberType.FLOAT) {
return valueFloat;
} else if (currentNumberType == NumberType.INT) {
return (float) valueInt;
} else if (currentNumberType == NumberType.LONG) {
return (float) valueLong;
} else if (currentNumberType == NumberType.DOUBLE) {
return (float) valueDouble;
}
throw new IOException("No number type");
}
@Override protected double doDoubleValue() throws IOException {
if (currentNumberType == NumberType.DOUBLE) {
return valueDouble;
} else if (currentNumberType == NumberType.INT) {
return (double) valueInt;
} else if (currentNumberType == NumberType.FLOAT) {
return (double) valueFloat;
} else if (currentNumberType == NumberType.LONG) {
return (double) valueLong;
}
throw new IOException("No number type");
}
@Override public void close() {
try {
is.close();
} catch (IOException e) {
// ignore
}
}
private short inShort() throws IOException {
return (short) (((is.read() & 0xFF) << 8) | (is.read() & 0xFF));
}
/**
* Reads four bytes and returns an int.
*/
private int inInt() throws IOException {
return ((is.read() & 0xFF) << 24) | ((is.read() & 0xFF) << 16)
| ((is.read() & 0xFF) << 8) | (is.read() & 0xFF);
}
/**
* Reads an int stored in variable-length format. Reads between one and
* five bytes. Smaller values take fewer bytes. Negative numbers are not
* supported.
*/
private int inVInt() throws IOException {
int b = is.read();
int i = b & 0x7F;
for (int shift = 7; (b & 0x80) != 0; shift += 7) {
b = is.read();
i |= (b & 0x7F) << shift;
}
return i;
}
/**
* Reads eight bytes and returns a long.
*/
private long inLong() throws IOException {
return (((long) inInt()) << 32) | (inInt() & 0xFFFFFFFFL);
}
/**
* Reads a long stored in variable-length format. Reads between one and
* nine bytes. Smaller values take fewer bytes. Negative numbers are not
* supported.
*/
private long readVLong() throws IOException {
int b = is.read();
long i = b & 0x7F;
for (int shift = 7; (b & 0x80) != 0; shift += 7) {
b = is.read();
i |= (b & 0x7FL) << shift;
}
return i;
}
private String inUTF() throws IOException {
inUtf16();
return new String(utf16Result.result, 0, utf16Result.length);
}
/**
* Reads a string.
*/
private void inUtf16() throws IOException {
int length = inVInt();
byte[] bytes = cachedBytes.get().get();
if (bytes == null || length > bytes.length) {
bytes = new byte[(int) (length * 1.25)];
cachedBytes.get().set(bytes);
}
inBytes(bytes, 0, length);
utf16Result = Unicode.fromBytesAsUtf16(bytes, 0, length);
}
private float inFloat() throws IOException {
return Float.intBitsToFloat(inInt());
}
private double inDouble() throws IOException {
return Double.longBitsToDouble(inLong());
}
/**
* Reads a boolean.
*/
private boolean inBoolean() throws IOException {
byte ch = (byte) is.read();
if (ch < 0)
throw new EOFException();
return (ch != 0);
}
private void inBytes(byte[] b, int offset, int len) throws IOException {
int n = 0;
while (n < len) {
int count = is.read(b, offset + n, len - n);
if (count < 0)
throw new EOFException();
n += count;
}
}
}

View File

@ -606,7 +606,7 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanTermQuery.class));
SpanTermQuery termQuery = (SpanTermQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(termQuery.getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(termQuery.getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
}
@Test public void testSpanTermQuery() throws IOException {
@ -616,7 +616,7 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanTermQuery.class));
SpanTermQuery termQuery = (SpanTermQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(termQuery.getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(termQuery.getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
}
@Test public void testSpanNotQueryBuilder() throws IOException {
@ -625,8 +625,8 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanNotQuery.class));
SpanNotQuery spanNotQuery = (SpanNotQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(((SpanTermQuery) spanNotQuery.getInclude()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNotQuery.getExclude()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNotQuery.getInclude()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNotQuery.getExclude()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
}
@Test public void testSpanNotQuery() throws IOException {
@ -636,8 +636,8 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanNotQuery.class));
SpanNotQuery spanNotQuery = (SpanNotQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(((SpanTermQuery) spanNotQuery.getInclude()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNotQuery.getExclude()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNotQuery.getInclude()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNotQuery.getExclude()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
}
@Test public void testSpanFirstQueryBuilder() throws IOException {
@ -646,7 +646,7 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanFirstQuery.class));
SpanFirstQuery spanFirstQuery = (SpanFirstQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(((SpanTermQuery) spanFirstQuery.getMatch()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanFirstQuery.getMatch()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(spanFirstQuery.getEnd(), equalTo(12));
}
@ -657,7 +657,7 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanFirstQuery.class));
SpanFirstQuery spanFirstQuery = (SpanFirstQuery) parsedQuery;
// since age is automatically registered in data, we encode it as numeric
assertThat(((SpanTermQuery) spanFirstQuery.getMatch()).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanFirstQuery.getMatch()).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(spanFirstQuery.getEnd(), equalTo(12));
}
@ -667,9 +667,9 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanNearQuery.class));
SpanNearQuery spanNearQuery = (SpanNearQuery) parsedQuery;
assertThat(spanNearQuery.getClauses().length, equalTo(3));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(36))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(36))));
assertThat(spanNearQuery.isInOrder(), equalTo(false));
}
@ -680,9 +680,9 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanNearQuery.class));
SpanNearQuery spanNearQuery = (SpanNearQuery) parsedQuery;
assertThat(spanNearQuery.getClauses().length, equalTo(3));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(36))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanNearQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(36))));
assertThat(spanNearQuery.isInOrder(), equalTo(false));
}
@ -692,9 +692,9 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanOrQuery.class));
SpanOrQuery spanOrQuery = (SpanOrQuery) parsedQuery;
assertThat(spanOrQuery.getClauses().length, equalTo(3));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(36))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(36))));
}
@Test public void testSpanOrQuery() throws IOException {
@ -704,9 +704,9 @@ public class SimpleIndexQueryParserTests {
assertThat(parsedQuery, instanceOf(SpanOrQuery.class));
SpanOrQuery spanOrQuery = (SpanOrQuery) parsedQuery;
assertThat(spanOrQuery.getClauses().length, equalTo(3));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.intToPrefixCoded(36))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[0]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(34))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[1]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(35))));
assertThat(((SpanTermQuery) spanOrQuery.getClauses()[2]).getTerm(), equalTo(new Term("age", NumericUtils.longToPrefixCoded(36))));
}
@Test public void testQueryFilterBuilder() throws Exception {

View File

@ -0,0 +1,93 @@
/*
* Licensed to Elastic Search and Shay Banon under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. Elastic Search 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.elasticsearch.util.xcontent.xson;
import org.elasticsearch.util.io.FastByteArrayOutputStream;
import org.elasticsearch.util.xcontent.XContentFactory;
import org.elasticsearch.util.xcontent.XContentGenerator;
import org.elasticsearch.util.xcontent.XContentParser;
import org.elasticsearch.util.xcontent.XContentType;
import org.testng.annotations.Test;
import java.io.IOException;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
/**
* @author kimchy (shay.banon)
*/
public class JsonVsXsonTests {
@Test public void compareParsingTokens() throws IOException {
FastByteArrayOutputStream xsonOs = new FastByteArrayOutputStream();
XContentGenerator xsonGen = XContentFactory.xContent(XContentType.XSON).createGenerator(xsonOs);
FastByteArrayOutputStream jsonOs = new FastByteArrayOutputStream();
XContentGenerator jsonGen = XContentFactory.xContent(XContentType.JSON).createGenerator(jsonOs);
xsonGen.writeStartObject();
jsonGen.writeStartObject();
xsonGen.writeStringField("test", "value");
jsonGen.writeStringField("test", "value");
xsonGen.writeArrayFieldStart("arr");
jsonGen.writeArrayFieldStart("arr");
xsonGen.writeNumber(1);
jsonGen.writeNumber(1);
xsonGen.writeNull();
jsonGen.writeNull();
xsonGen.writeEndArray();
jsonGen.writeEndArray();
xsonGen.writeEndObject();
jsonGen.writeEndObject();
xsonGen.close();
jsonGen.close();
verifySameTokens(XContentFactory.xContent(XContentType.JSON).createParser(jsonOs.copiedByteArray()), XContentFactory.xContent(XContentType.XSON).createParser(xsonOs.copiedByteArray()));
}
private void verifySameTokens(XContentParser parser1, XContentParser parser2) throws IOException {
while (true) {
XContentParser.Token token1 = parser1.nextToken();
XContentParser.Token token2 = parser2.nextToken();
if (token1 == null) {
assertThat(token2, nullValue());
return;
}
assertThat(token1, equalTo(token2));
switch (token1) {
case FIELD_NAME:
assertThat(parser1.currentName(), equalTo(parser2.currentName()));
break;
case VALUE_STRING:
assertThat(parser1.text(), equalTo(parser2.text()));
break;
case VALUE_NUMBER:
assertThat(parser1.numberType(), equalTo(parser2.numberType()));
assertThat(parser1.numberValue(), equalTo(parser2.numberValue()));
break;
}
}
}
}

View File

@ -45,10 +45,10 @@ import org.testng.annotations.Test;
import java.util.Map;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.*;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@ -194,7 +194,7 @@ public class SingleInstanceEmbeddedSearchTests extends AbstractNodesTests {
private InternalSearchRequest searchRequest(SearchSourceBuilder builder) {
return new InternalSearchRequest("test", 0, builder.build());
return new InternalSearchRequest("test", 0, builder.buildAsBytes());
}
private void index(Client client, String id, String nameValue, int age) {

View File

@ -19,8 +19,6 @@
package org.elasticsearch.test.integration.search;
import org.elasticsearch.util.gcommon.collect.ImmutableMap;
import org.elasticsearch.util.gcommon.collect.Sets;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.ClusterService;
@ -45,6 +43,8 @@ import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.search.query.QuerySearchResultProvider;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.gcommon.collect.ImmutableMap;
import org.elasticsearch.util.gcommon.collect.Sets;
import org.elasticsearch.util.trove.ExtTIntArrayList;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
@ -55,12 +55,12 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.elasticsearch.util.gcommon.collect.Maps.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.*;
import static org.elasticsearch.util.TimeValue.*;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.elasticsearch.util.gcommon.collect.Maps.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@ -354,7 +354,7 @@ public class TwoInstanceEmbeddedSearchTests extends AbstractNodesTests {
}
private InternalSearchRequest searchRequest(ShardRouting shardRouting, SearchSourceBuilder builder) {
return new InternalSearchRequest(shardRouting, builder.build());
return new InternalSearchRequest(shardRouting, builder.buildAsBytes());
}
private void index(Client client, String id, String nameValue, int age) {

View File

@ -19,9 +19,6 @@
package org.elasticsearch.test.integration.search;
import org.elasticsearch.util.gcommon.collect.ImmutableMap;
import org.elasticsearch.util.guice.inject.AbstractModule;
import org.elasticsearch.util.guice.inject.Inject;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.Requests;
import org.elasticsearch.cluster.ClusterService;
@ -50,6 +47,9 @@ import org.elasticsearch.search.query.QuerySearchResult;
import org.elasticsearch.search.query.QuerySearchResultProvider;
import org.elasticsearch.test.integration.AbstractNodesTests;
import org.elasticsearch.util.TimeValue;
import org.elasticsearch.util.gcommon.collect.ImmutableMap;
import org.elasticsearch.util.guice.inject.AbstractModule;
import org.elasticsearch.util.guice.inject.Inject;
import org.elasticsearch.util.settings.Settings;
import org.elasticsearch.util.trove.ExtTIntArrayList;
import org.testng.annotations.AfterClass;
@ -60,12 +60,12 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.elasticsearch.util.gcommon.collect.Maps.*;
import static org.elasticsearch.client.Requests.*;
import static org.elasticsearch.index.query.xcontent.QueryBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.*;
import static org.elasticsearch.util.TimeValue.*;
import static org.elasticsearch.util.gcommon.collect.Lists.*;
import static org.elasticsearch.util.gcommon.collect.Maps.*;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@ -360,7 +360,7 @@ public class TwoInstanceUnbalancedShardsEmbeddedSearchTests extends AbstractNode
}
private static InternalSearchRequest searchRequest(ShardRouting shardRouting, SearchSourceBuilder builder) {
return new InternalSearchRequest(shardRouting, builder.build());
return new InternalSearchRequest(shardRouting, builder.buildAsBytes());
}
private void index(Client client, String id, String nameValue, int age) {

View File

@ -38,7 +38,8 @@ import org.elasticsearch.action.terms.TermsResponse
import org.elasticsearch.client.Client
import org.elasticsearch.client.internal.InternalClient
import org.elasticsearch.groovy.client.action.GActionFuture
import org.elasticsearch.groovy.util.json.JsonBuilder
import org.elasticsearch.groovy.util.xcontent.GXContentBuilder
import org.elasticsearch.util.xcontent.XContentType
/**
* @author kimchy (shay.banon)
@ -47,47 +48,51 @@ class GClient {
static {
IndexRequest.metaClass.setSource = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
delegate.source(new GXContentBuilder().buildAsBytes(c, indexContentType))
}
IndexRequest.metaClass.source = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
delegate.source(new GXContentBuilder().buildAsBytes(c, indexContentType))
}
DeleteByQueryRequest.metaClass.setQuery = {Closure c ->
delegate.query(new JsonBuilder().buildAsBytes(c))
delegate.query(new GXContentBuilder().buildAsBytes(c, contentType))
}
DeleteByQueryRequest.metaClass.query = {Closure c ->
delegate.query(new JsonBuilder().buildAsBytes(c))
delegate.query(new GXContentBuilder().buildAsBytes(c, contentType))
}
CountRequest.metaClass.setQuery = {Closure c ->
delegate.query(new JsonBuilder().buildAsBytes(c))
delegate.query(new GXContentBuilder().buildAsBytes(c, contentType))
}
CountRequest.metaClass.query = {Closure c ->
delegate.query(new JsonBuilder().buildAsBytes(c))
delegate.query(new GXContentBuilder().buildAsBytes(c, contentType))
}
SearchRequest.metaClass.setSource = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
delegate.source(new GXContentBuilder().buildAsBytes(c, contentType))
}
SearchRequest.metaClass.source = {Closure c ->
delegate.source(new JsonBuilder().buildAsBytes(c))
delegate.source(new GXContentBuilder().buildAsBytes(c, contentType))
}
SearchRequest.metaClass.setExtraSource = {Closure c ->
delegate.extraSource(new JsonBuilder().buildAsBytes(c))
delegate.extraSource(new GXContentBuilder().buildAsBytes(c, contentType))
}
SearchRequest.metaClass.extraSource = {Closure c ->
delegate.extraSource(new JsonBuilder().buildAsBytes(c))
delegate.extraSource(new GXContentBuilder().buildAsBytes(c, contentType))
}
MoreLikeThisRequest.metaClass.setSearchSource = {Closure c ->
delegate.searchSource(new JsonBuilder().buildAsBytes(c))
delegate.searchSource(new GXContentBuilder().buildAsBytes(c, contentType))
}
MoreLikeThisRequest.metaClass.searchSource = {Closure c ->
delegate.searchSource(new JsonBuilder().buildAsBytes(c))
delegate.searchSource(new GXContentBuilder().buildAsBytes(c, contentType))
}
}
static XContentType contentType = XContentType.XSON;
static XContentType indexContentType = XContentType.JSON;
final Client client;
int resolveStrategy = Closure.DELEGATE_FIRST

View File

@ -43,7 +43,7 @@ import org.elasticsearch.action.admin.indices.status.IndicesStatusResponse
import org.elasticsearch.client.IndicesAdminClient
import org.elasticsearch.client.internal.InternalClient
import org.elasticsearch.groovy.client.action.GActionFuture
import org.elasticsearch.groovy.util.json.JsonBuilder
import org.elasticsearch.groovy.util.xcontent.GXContentBuilder
/**
* @author kimchy (shay.banon)
@ -52,23 +52,23 @@ class GIndicesAdminClient {
static {
CreateIndexRequest.metaClass.setSettings = {Closure c ->
delegate.settings(new JsonBuilder().buildAsString(c))
delegate.settings(new GXContentBuilder().buildAsString(c))
}
CreateIndexRequest.metaClass.settings = {Closure c ->
delegate.settings(new JsonBuilder().buildAsString(c))
delegate.settings(new GXContentBuilder().buildAsString(c))
}
CreateIndexRequest.metaClass.mapping = {String type, Closure c ->
delegate.mapping(type, new JsonBuilder().buildAsString(c))
delegate.mapping(type, new GXContentBuilder().buildAsString(c))
}
CreateIndexRequest.metaClass.setMapping = {String type, Closure c ->
delegate.mapping(type, new JsonBuilder().buildAsString(c))
delegate.mapping(type, new GXContentBuilder().buildAsString(c))
}
PutMappingRequest.metaClass.setSource = {Closure c ->
delegate.source(new JsonBuilder().buildAsString(c))
delegate.source(new GXContentBuilder().buildAsString(c))
}
PutMappingRequest.metaClass.source = {Closure c ->
delegate.source(new JsonBuilder().buildAsString(c))
delegate.source(new GXContentBuilder().buildAsString(c))
}
}

View File

@ -19,14 +19,14 @@
package org.elasticsearch.groovy.node
import org.elasticsearch.groovy.util.json.JsonBuilder
import org.elasticsearch.groovy.util.xcontent.GXContentBuilder
import org.elasticsearch.node.Node
import org.elasticsearch.node.internal.InternalNode
import org.elasticsearch.util.settings.ImmutableSettings
import org.elasticsearch.util.settings.loader.JsonSettingsLoader
/**
* The node builder allow to build a {@link GNode} instance.
* The node builder allow to build a {@link GNode} instance.
*
* @author kimchy (shay.banon)
*/
@ -41,7 +41,7 @@ public class GNodeBuilder {
}
def settings(Closure settings) {
byte[] settingsBytes = new JsonBuilder().buildAsBytes(settings);
byte[] settingsBytes = new GXContentBuilder().buildAsBytes(settings);
settingsBuilder.put(new JsonSettingsLoader().load(settingsBytes))
}

View File

@ -17,7 +17,7 @@
* under the License.
*/
package org.elasticsearch.groovy.util.json
package org.elasticsearch.groovy.util.xcontent
import org.elasticsearch.util.xcontent.XContentFactory
import org.elasticsearch.util.xcontent.XContentType
@ -32,7 +32,7 @@ import org.elasticsearch.util.xcontent.builder.TextXContentBuilder
*
* @since 1.2
*/
class JsonBuilder {
class GXContentBuilder {
static NODE_ELEMENT = "element"
@ -56,7 +56,11 @@ class JsonBuilder {
}
byte[] buildAsBytes(Closure c) {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(XContentType.JSON);
return buildAsBytes(c, XContentType.JSON);
}
byte[] buildAsBytes(Closure c, XContentType contentType) {
BinaryXContentBuilder builder = XContentFactory.contentBinaryBuilder(contentType);
def json = build(c)
builder.map(json);
return builder.copiedBytes();
@ -164,7 +168,7 @@ class JsonBuilder {
value = value.collect {
if (it instanceof Closure) {
def callable = it
final JsonBuilder localBuilder = new JsonBuilder()
final GXContentBuilder localBuilder = new GXContentBuilder()
callable.delegate = localBuilder
callable.resolveStrategy = Closure.DELEGATE_FIRST
final Map nestedObject = localBuilder.buildRoot(callable)

View File

@ -54,7 +54,7 @@ class SimpleActionsTests {
@Test
void testSimpleOperations() {
def value1 = new org.elasticsearch.groovy.util.json.JsonBuilder().buildAsString {
def value1 = new org.elasticsearch.groovy.util.xcontent.GXContentBuilder().buildAsString {
something = "test"
}
println value1

View File

@ -17,15 +17,15 @@
* under the License.
*/
package org.elasticsearch.groovy.util.json
package org.elasticsearch.groovy.util.xcontent
/**
* @author kimchy (shay.banon)
*/
class JsonBuilderTests extends GroovyTestCase {
class GXContentBuilderTests extends GroovyTestCase {
void testSimple() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
rootprop = "something"
@ -35,7 +35,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testArrays() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
@ -46,7 +46,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testSubObjects() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
@ -60,7 +60,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testAssignedObjects() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
@ -74,7 +74,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testNamedArgumentHandling() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
categories = ['a', 'b', 'c']
rootprop = "something"
@ -87,7 +87,7 @@ class JsonBuilderTests extends GroovyTestCase {
void testArrayOfClosures() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def result = builder.buildAsString {
foo = [{ bar = "hello" }]
}
@ -96,7 +96,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testRootElementList() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def results = ['one', 'two', 'three']
@ -117,7 +117,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testExampleFromReferenceGuide() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def results = ['one', 'two', 'three']
@ -150,7 +150,7 @@ class JsonBuilderTests extends GroovyTestCase {
}
void testAppendToArray() {
def builder = new JsonBuilder()
def builder = new GXContentBuilder()
def results = ['one', 'two', 'three']

View File

@ -19,14 +19,12 @@
package org.elasticsearch.memcached;
import org.elasticsearch.util.gcommon.collect.ImmutableList;
import org.elasticsearch.util.gcommon.collect.ImmutableSet;
import org.elasticsearch.rest.support.AbstractRestRequest;
import org.elasticsearch.rest.support.RestUtils;
import org.elasticsearch.util.Unicode;
import org.elasticsearch.util.io.FastByteArrayInputStream;
import org.elasticsearch.util.gcommon.collect.ImmutableList;
import org.elasticsearch.util.gcommon.collect.ImmutableSet;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -111,10 +109,6 @@ public class MemcachedRestRequest extends AbstractRestRequest {
return data != null;
}
@Override public InputStream contentAsStream() {
return new FastByteArrayInputStream(data);
}
@Override public byte[] contentAsBytes() {
return data;
}