Skip to content

Commit b13f6b1

Browse files
committed
Mapping - MultiField Mapping, closes #51.
1 parent 54dc5a5 commit b13f6b1

File tree

13 files changed

+517
-3
lines changed

13 files changed

+517
-3
lines changed

modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonDocumentMapperParser.java

+50
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,8 @@ private void parseProperties(JsonObjectMapper.Builder objBuilder, ObjectNode pro
261261
// lets see if we can derive this...
262262
if (propNode.isObject() && propNode.get("properties") != null) {
263263
type = JsonObjectMapper.JSON_TYPE;
264+
} else if (propNode.isObject() && propNode.get("fields") != null) {
265+
type = JsonMultiFieldMapper.JSON_TYPE;
264266
} else {
265267
throw new MapperParsingException("No type specified for property [" + propName + "]");
266268
}
@@ -281,12 +283,60 @@ private void parseProperties(JsonObjectMapper.Builder objBuilder, ObjectNode pro
281283
objBuilder.add(parseBoolean(propName, (ObjectNode) propNode));
282284
} else if (type.equals(JsonObjectMapper.JSON_TYPE)) {
283285
objBuilder.add(parseObject(propName, (ObjectNode) propNode));
286+
} else if (type.equals(JsonMultiFieldMapper.JSON_TYPE)) {
287+
objBuilder.add(parseMultiField(propName, (ObjectNode) propNode));
284288
} else if (type.equals(JsonBinaryFieldMapper.JSON_TYPE)) {
285289
objBuilder.add(parseBinary(propName, (ObjectNode) propNode));
286290
}
287291
}
288292
}
289293

294+
private JsonMultiFieldMapper.Builder parseMultiField(String name, ObjectNode multiFieldNode) {
295+
JsonMultiFieldMapper.Builder builder = multiField(name);
296+
for (Iterator<Map.Entry<String, JsonNode>> fieldsIt = multiFieldNode.getFields(); fieldsIt.hasNext();) {
297+
Map.Entry<String, JsonNode> entry = fieldsIt.next();
298+
String fieldName = entry.getKey();
299+
JsonNode fieldNode = entry.getValue();
300+
if (fieldName.equals("pathType")) {
301+
builder.pathType(parsePathType(name, fieldNode.getValueAsText()));
302+
} else if (fieldName.equals("fields")) {
303+
ObjectNode fieldsNode = (ObjectNode) fieldNode;
304+
for (Iterator<Map.Entry<String, JsonNode>> propsIt = fieldsNode.getFields(); propsIt.hasNext();) {
305+
Map.Entry<String, JsonNode> entry1 = propsIt.next();
306+
String propName = entry1.getKey();
307+
JsonNode propNode = entry1.getValue();
308+
309+
String type;
310+
JsonNode typeNode = propNode.get("type");
311+
if (typeNode != null) {
312+
type = typeNode.getTextValue();
313+
} else {
314+
throw new MapperParsingException("No type specified for property [" + propName + "]");
315+
}
316+
317+
if (type.equals(JsonStringFieldMapper.JSON_TYPE)) {
318+
builder.add(parseString(propName, (ObjectNode) propNode));
319+
} else if (type.equals(JsonDateFieldMapper.JSON_TYPE)) {
320+
builder.add(parseDate(propName, (ObjectNode) propNode));
321+
} else if (type.equals(JsonIntegerFieldMapper.JSON_TYPE)) {
322+
builder.add(parseInteger(propName, (ObjectNode) propNode));
323+
} else if (type.equals(JsonLongFieldMapper.JSON_TYPE)) {
324+
builder.add(parseLong(propName, (ObjectNode) propNode));
325+
} else if (type.equals(JsonFloatFieldMapper.JSON_TYPE)) {
326+
builder.add(parseFloat(propName, (ObjectNode) propNode));
327+
} else if (type.equals(JsonDoubleFieldMapper.JSON_TYPE)) {
328+
builder.add(parseDouble(propName, (ObjectNode) propNode));
329+
} else if (type.equals(JsonBooleanFieldMapper.JSON_TYPE)) {
330+
builder.add(parseBoolean(propName, (ObjectNode) propNode));
331+
} else if (type.equals(JsonBinaryFieldMapper.JSON_TYPE)) {
332+
builder.add(parseBinary(propName, (ObjectNode) propNode));
333+
}
334+
}
335+
}
336+
}
337+
return builder;
338+
}
339+
290340
private JsonDateFieldMapper.Builder parseDate(String name, ObjectNode dateNode) {
291341
JsonDateFieldMapper.Builder builder = dateField(name);
292342
parseNumberField(builder, name, dateNode);

modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonFieldMapper.java

-1
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,6 @@ public abstract static class Builder<T extends Builder, Y extends JsonFieldMappe
112112

113113
protected Builder(String name) {
114114
super(name);
115-
indexName = name;
116115
}
117116

118117
protected T index(Field.Index index) {

modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonMapper.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public JsonPath path() {
4949
@NotThreadSafe
5050
public static abstract class Builder<T extends Builder, Y extends JsonMapper> {
5151

52-
protected final String name;
52+
protected String name;
5353

5454
protected T builder;
5555

modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonMapperBuilders.java

+4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public static JsonBoostFieldMapper.Builder boost(String name) {
5252
return new JsonBoostFieldMapper.Builder(name);
5353
}
5454

55+
public static JsonMultiFieldMapper.Builder multiField(String name) {
56+
return new JsonMultiFieldMapper.Builder(name);
57+
}
58+
5559
public static JsonObjectMapper.Builder object(String name) {
5660
return new JsonObjectMapper.Builder(name);
5761
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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.index.mapper.json;
21+
22+
import com.google.common.collect.ImmutableMap;
23+
import org.elasticsearch.index.mapper.FieldMapper;
24+
import org.elasticsearch.index.mapper.FieldMapperListener;
25+
import org.elasticsearch.index.mapper.MergeMappingException;
26+
import org.elasticsearch.util.json.JsonBuilder;
27+
28+
import java.io.IOException;
29+
import java.util.HashMap;
30+
import java.util.List;
31+
import java.util.Map;
32+
33+
import static com.google.common.collect.Lists.*;
34+
import static org.elasticsearch.util.MapBuilder.*;
35+
36+
/**
37+
* @author kimchy (shay.banon)
38+
*/
39+
public class JsonMultiFieldMapper implements JsonMapper {
40+
41+
public static final String JSON_TYPE = "multi_field";
42+
43+
public static class Defaults {
44+
public static final JsonPath.Type PATH_TYPE = JsonPath.Type.FULL;
45+
}
46+
47+
public static class Builder extends JsonMapper.Builder<Builder, JsonMultiFieldMapper> {
48+
49+
private JsonPath.Type pathType = Defaults.PATH_TYPE;
50+
51+
private final List<JsonMapper.Builder> mappersBuilders = newArrayList();
52+
53+
private JsonMapper.Builder defaultMapperBuilder;
54+
55+
public Builder(String name) {
56+
super(name);
57+
this.builder = this;
58+
}
59+
60+
public Builder pathType(JsonPath.Type pathType) {
61+
this.pathType = pathType;
62+
return this;
63+
}
64+
65+
public Builder add(JsonMapper.Builder builder) {
66+
if (builder.name.equals(name)) {
67+
defaultMapperBuilder = builder;
68+
} else {
69+
mappersBuilders.add(builder);
70+
}
71+
return this;
72+
}
73+
74+
@Override public JsonMultiFieldMapper build(BuilderContext context) {
75+
JsonPath.Type origPathType = context.path().pathType();
76+
context.path().pathType(pathType);
77+
78+
JsonMapper defaultMapper = null;
79+
if (defaultMapperBuilder != null) {
80+
defaultMapper = defaultMapperBuilder.build(context);
81+
}
82+
83+
context.path().add(name);
84+
Map<String, JsonMapper> mappers = new HashMap<String, JsonMapper>();
85+
for (JsonMapper.Builder builder : mappersBuilders) {
86+
JsonMapper mapper = builder.build(context);
87+
mappers.put(mapper.name(), mapper);
88+
}
89+
context.path().remove();
90+
91+
context.path().pathType(origPathType);
92+
93+
return new JsonMultiFieldMapper(name, pathType, mappers, defaultMapper);
94+
}
95+
}
96+
97+
private final String name;
98+
99+
private final JsonPath.Type pathType;
100+
101+
private final Object mutex = new Object();
102+
103+
private volatile ImmutableMap<String, JsonMapper> mappers = ImmutableMap.of();
104+
105+
private volatile JsonMapper defaultMapper;
106+
107+
public JsonMultiFieldMapper(String name, JsonPath.Type pathType, JsonMapper defaultMapper) {
108+
this(name, pathType, new HashMap<String, JsonMapper>(), defaultMapper);
109+
}
110+
111+
public JsonMultiFieldMapper(String name, JsonPath.Type pathType, Map<String, JsonMapper> mappers, JsonMapper defaultMapper) {
112+
this.name = name;
113+
this.pathType = pathType;
114+
this.mappers = ImmutableMap.copyOf(mappers);
115+
this.defaultMapper = defaultMapper;
116+
}
117+
118+
@Override public String name() {
119+
return this.name;
120+
}
121+
122+
public JsonPath.Type pathType() {
123+
return pathType;
124+
}
125+
126+
public JsonMapper defaultMapper() {
127+
return this.defaultMapper;
128+
}
129+
130+
public ImmutableMap<String, JsonMapper> mappers() {
131+
return this.mappers;
132+
}
133+
134+
@Override public void parse(JsonParseContext jsonContext) throws IOException {
135+
JsonPath.Type origPathType = jsonContext.path().pathType();
136+
jsonContext.path().pathType(pathType);
137+
138+
// do the default mapper without adding the path
139+
if (defaultMapper != null) {
140+
defaultMapper.parse(jsonContext);
141+
}
142+
143+
jsonContext.path().add(name);
144+
for (JsonMapper mapper : mappers.values()) {
145+
mapper.parse(jsonContext);
146+
}
147+
jsonContext.path().remove();
148+
149+
jsonContext.path().pathType(origPathType);
150+
}
151+
152+
@Override public void merge(JsonMapper mergeWith, JsonMergeContext mergeContext) throws MergeMappingException {
153+
if (!(mergeWith instanceof JsonMultiFieldMapper)) {
154+
mergeContext.addConflict("Can't merge a non multi_field mapping [" + mergeWith.name() + "] with a multi_field mapping [" + name() + "]");
155+
return;
156+
}
157+
JsonMultiFieldMapper mergeWithMultiField = (JsonMultiFieldMapper) mergeWith;
158+
synchronized (mutex) {
159+
// merge the default mapper
160+
if (defaultMapper == null) {
161+
if (mergeWithMultiField.defaultMapper != null) {
162+
if (!mergeContext.mergeFlags().simulate()) {
163+
defaultMapper = mergeWithMultiField.defaultMapper;
164+
mergeContext.docMapper().addFieldMapper((FieldMapper) defaultMapper);
165+
}
166+
}
167+
} else {
168+
if (mergeWithMultiField.defaultMapper != null) {
169+
defaultMapper.merge(mergeWithMultiField.defaultMapper, mergeContext);
170+
}
171+
}
172+
173+
// merge all the other mappers
174+
for (JsonMapper mergeWithMapper : mergeWithMultiField.mappers.values()) {
175+
JsonMapper mergeIntoMapper = mappers.get(mergeWithMapper.name());
176+
if (mergeIntoMapper == null) {
177+
// no mapping, simply add it if not simulating
178+
if (!mergeContext.mergeFlags().simulate()) {
179+
mappers = newMapBuilder(mappers).put(mergeWithMapper.name(), mergeWithMapper).immutableMap();
180+
if (mergeWithMapper instanceof JsonFieldMapper) {
181+
mergeContext.docMapper().addFieldMapper((FieldMapper) mergeWithMapper);
182+
}
183+
}
184+
} else {
185+
mergeIntoMapper.merge(mergeWithMapper, mergeContext);
186+
}
187+
}
188+
}
189+
}
190+
191+
@Override public void traverse(FieldMapperListener fieldMapperListener) {
192+
for (JsonMapper mapper : mappers.values()) {
193+
mapper.traverse(fieldMapperListener);
194+
}
195+
}
196+
197+
@Override public void toJson(JsonBuilder builder, Params params) throws IOException {
198+
builder.startObject(name);
199+
builder.field("type", JSON_TYPE);
200+
builder.field("pathType", pathType.name().toLowerCase());
201+
202+
builder.startObject("fields");
203+
if (defaultMapper != null) {
204+
defaultMapper.toJson(builder, params);
205+
}
206+
for (JsonMapper mapper : mappers.values()) {
207+
mapper.toJson(builder, params);
208+
}
209+
builder.endObject();
210+
211+
builder.endObject();
212+
}
213+
}

modules/elasticsearch/src/main/java/org/elasticsearch/index/mapper/json/JsonObjectMapper.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ public static class Builder extends JsonMapper.Builder<Builder, JsonObjectMapper
7070

7171
public Builder(String name) {
7272
super(name);
73+
this.builder = this;
7374
}
7475

7576
public Builder enabled(boolean enabled) {
@@ -358,7 +359,21 @@ private void serializeValue(JsonParseContext jsonContext, String currentFieldNam
358359
}
359360
}
360361
} else {
361-
mergeIntoMapper.merge(mergeWithMapper, mergeContext);
362+
if ((mergeWithMapper instanceof JsonMultiFieldMapper) && !(mergeIntoMapper instanceof JsonMultiFieldMapper)) {
363+
JsonMultiFieldMapper mergeWithMultiField = (JsonMultiFieldMapper) mergeWithMapper;
364+
mergeWithMultiField.merge(mergeIntoMapper, mergeContext);
365+
if (!mergeContext.mergeFlags().simulate()) {
366+
putMapper(mergeWithMultiField);
367+
// now, raise events for all mappers
368+
for (JsonMapper mapper : mergeWithMultiField.mappers().values()) {
369+
if (mapper instanceof JsonFieldMapper) {
370+
mergeContext.docMapper().addFieldMapper((FieldMapper) mapper);
371+
}
372+
}
373+
}
374+
} else {
375+
mergeIntoMapper.merge(mergeWithMapper, mergeContext);
376+
}
362377
}
363378
}
364379
}

0 commit comments

Comments
 (0)