Skip to content

Commit 86c3a40

Browse files
committed
Java API: Expose source as Map (in GetResponse, SearchHit), allow to index a Map, closes #58.
1 parent 4b04db9 commit 86c3a40

File tree

7 files changed

+120
-4
lines changed

7 files changed

+120
-4
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to Elastic Search and Shay Banon under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. Elastic Search licenses this
6+
* file to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.elasticsearch;
21+
22+
/**
23+
* A generic exception indicating failure to generate.
24+
*
25+
* @author kimchy (shay.banon)
26+
*/
27+
public class ElasticSearchGenerationException extends ElasticSearchException {
28+
29+
public ElasticSearchGenerationException(String msg) {
30+
super(msg);
31+
}
32+
33+
public ElasticSearchGenerationException(String msg, Throwable cause) {
34+
super(msg, cause);
35+
}
36+
}

modules/elasticsearch/src/main/java/org/elasticsearch/action/get/GetResponse.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@
1919

2020
package org.elasticsearch.action.get;
2121

22+
import org.elasticsearch.ElasticSearchParseException;
2223
import org.elasticsearch.action.ActionResponse;
2324
import org.elasticsearch.util.Unicode;
2425
import org.elasticsearch.util.io.Streamable;
2526

2627
import java.io.DataInput;
2728
import java.io.DataOutput;
2829
import java.io.IOException;
30+
import java.util.Map;
31+
32+
import static org.elasticsearch.util.json.Jackson.*;
2933

3034
/**
3135
* The response of a get action.
@@ -96,6 +100,20 @@ public String sourceAsString() {
96100
return Unicode.fromBytes(source);
97101
}
98102

103+
/**
104+
* The source of the document (As a map).
105+
*/
106+
public Map<String, Object> sourceAsMap() throws ElasticSearchParseException {
107+
if (!exists()) {
108+
return null;
109+
}
110+
try {
111+
return defaultObjectMapper().readValue(source, 0, source.length, Map.class);
112+
} catch (Exception e) {
113+
throw new ElasticSearchParseException("Failed to parse source to map", e);
114+
}
115+
}
116+
99117
@Override public void readFrom(DataInput in) throws IOException, ClassNotFoundException {
100118
index = in.readUTF();
101119
type = in.readUTF();

modules/elasticsearch/src/main/java/org/elasticsearch/action/index/IndexRequest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,19 +19,23 @@
1919

2020
package org.elasticsearch.action.index;
2121

22+
import org.elasticsearch.ElasticSearchGenerationException;
2223
import org.elasticsearch.ElasticSearchIllegalArgumentException;
2324
import org.elasticsearch.action.ActionRequestValidationException;
2425
import org.elasticsearch.action.support.replication.ShardReplicationOperationRequest;
2526
import org.elasticsearch.util.Required;
2627
import org.elasticsearch.util.TimeValue;
2728
import org.elasticsearch.util.Unicode;
29+
import org.elasticsearch.util.io.FastByteArrayOutputStream;
2830
import org.elasticsearch.util.json.JsonBuilder;
2931

3032
import java.io.DataInput;
3133
import java.io.DataOutput;
3234
import java.io.IOException;
35+
import java.util.Map;
3336

3437
import static org.elasticsearch.action.Actions.*;
38+
import static org.elasticsearch.util.json.Jackson.*;
3539

3640
/**
3741
* Index request to index a typed JSON document into a specific index and make it searchable. Best
@@ -190,6 +194,22 @@ byte[] source() {
190194
return source;
191195
}
192196

197+
/**
198+
* Writes the JSON as a {@link Map}.
199+
*
200+
* @param source The map to index
201+
*/
202+
@Required public IndexRequest source(Map source) throws ElasticSearchGenerationException {
203+
FastByteArrayOutputStream os = FastByteArrayOutputStream.Cached.cached();
204+
try {
205+
defaultObjectMapper().writeValue(os, source);
206+
} catch (IOException e) {
207+
throw new ElasticSearchGenerationException("Failed to generate [" + source + "]", e);
208+
}
209+
this.source = os.copiedByteArray();
210+
return this;
211+
}
212+
193213
/**
194214
* Sets the JSON source to index.
195215
*

modules/elasticsearch/src/main/java/org/elasticsearch/search/SearchHit.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
package org.elasticsearch.search;
2121

2222
import org.apache.lucene.search.Explanation;
23+
import org.elasticsearch.ElasticSearchParseException;
2324
import org.elasticsearch.util.io.Streamable;
2425
import org.elasticsearch.util.json.ToJson;
2526

@@ -58,6 +59,11 @@ public interface SearchHit extends Streamable, ToJson, Iterable<SearchHitField>
5859
*/
5960
String sourceAsString();
6061

62+
/**
63+
* The source of the document as a map (can be <tt>null</tt>).
64+
*/
65+
Map<String, Object> sourceAsMap() throws ElasticSearchParseException;
66+
6167
/**
6268
* If enabled, the explanation of the search hit.
6369
*/

modules/elasticsearch/src/main/java/org/elasticsearch/search/internal/InternalSearchHit.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import com.google.common.collect.ImmutableMap;
2323
import org.apache.lucene.search.Explanation;
24+
import org.elasticsearch.ElasticSearchParseException;
2425
import org.elasticsearch.search.SearchHit;
2526
import org.elasticsearch.search.SearchHitField;
2627
import org.elasticsearch.search.SearchShardTarget;
@@ -36,6 +37,7 @@
3637

3738
import static org.elasticsearch.search.SearchShardTarget.*;
3839
import static org.elasticsearch.search.internal.InternalSearchHitField.*;
40+
import static org.elasticsearch.util.json.Jackson.*;
3941
import static org.elasticsearch.util.lucene.Lucene.*;
4042

4143
/**
@@ -89,6 +91,17 @@ public InternalSearchHit(String id, String type, byte[] source, Map<String, Sear
8991
return Unicode.fromBytes(source);
9092
}
9193

94+
@Override public Map<String, Object> sourceAsMap() throws ElasticSearchParseException {
95+
if (source == null) {
96+
return null;
97+
}
98+
try {
99+
return defaultObjectMapper().readValue(source, 0, source.length, Map.class);
100+
} catch (Exception e) {
101+
throw new ElasticSearchParseException("Failed to parse source to map", e);
102+
}
103+
}
104+
92105
@Override public Iterator<SearchHitField> iterator() {
93106
return fields.values().iterator();
94107
}

modules/elasticsearch/src/main/java/org/elasticsearch/util/io/FastByteArrayOutputStream.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@
3131
*/
3232
public class FastByteArrayOutputStream extends OutputStream {
3333

34+
/**
35+
* A thread local based cache of {@link FastByteArrayOutputStream}.
36+
*/
37+
public static class Cached {
38+
39+
private static final ThreadLocal<FastByteArrayOutputStream> cache = new ThreadLocal<FastByteArrayOutputStream>() {
40+
@Override protected FastByteArrayOutputStream initialValue() {
41+
return new FastByteArrayOutputStream();
42+
}
43+
};
44+
45+
/**
46+
* Returns the cached thread local byte stream, with its internal stream cleared.
47+
*/
48+
public static FastByteArrayOutputStream cached() {
49+
FastByteArrayOutputStream os = cache.get();
50+
os.reset();
51+
return os;
52+
}
53+
}
54+
3455
/**
3556
* The buffer where data is stored.
3657
*/
@@ -82,15 +103,14 @@ public void write(int b) {
82103
* Writes <code>len</code> bytes from the specified byte array
83104
* starting at offset <code>off</code> to this byte array output stream.
84105
*
106+
* <b>NO checks for bounds, parameters must be ok!</b>
107+
*
85108
* @param b the data.
86109
* @param off the start offset in the data.
87110
* @param len the number of bytes to write.
88111
*/
89112
public void write(byte b[], int off, int len) {
90-
if ((off < 0) || (off > b.length) || (len < 0) ||
91-
((off + len) > b.length) || ((off + len) < 0)) {
92-
throw new IndexOutOfBoundsException();
93-
} else if (len == 0) {
113+
if (len == 0) {
94114
return;
95115
}
96116
int newcount = count + len;

modules/test/integration/src/test/java/org/elasticsearch/test/integration/document/DocumentActionsTests.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@
3737
import org.testng.annotations.BeforeMethod;
3838
import org.testng.annotations.Test;
3939

40+
import java.util.Map;
41+
4042
import static org.elasticsearch.client.Requests.*;
4143
import static org.elasticsearch.index.query.json.JsonQueryBuilders.*;
4244
import static org.hamcrest.MatcherAssert.*;
@@ -101,6 +103,7 @@ protected Client getClient2() {
101103
for (int i = 0; i < 5; i++) {
102104
getResult = client1.get(getRequest("test").type("type1").id("1").threadedOperation(false)).actionGet();
103105
assertThat("cycle #" + i, getResult.sourceAsString(), equalTo(source("1", "test")));
106+
assertThat("cycle(map) #" + i, (String) ((Map) getResult.sourceAsMap().get("type1")).get("name"), equalTo("test"));
104107
getResult = client1.get(getRequest("test").type("type1").id("1").threadedOperation(true)).actionGet();
105108
assertThat("cycle #" + i, getResult.sourceAsString(), equalTo(source("1", "test")));
106109
}

0 commit comments

Comments
 (0)