import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static org.sonar.server.es.DefaultIndexSettings.ANALYZER;
+import static org.sonar.server.es.DefaultIndexSettings.FIELDDATA_ENABLED;
+import static org.sonar.server.es.DefaultIndexSettings.FIELD_FIELDDATA;
import static org.sonar.server.es.DefaultIndexSettings.FIELD_TYPE_KEYWORD;
import static org.sonar.server.es.DefaultIndexSettings.FIELD_TYPE_TEXT;
import static org.sonar.server.es.DefaultIndexSettings.INDEX;
return new KeywordFieldBuilder(this, fieldName);
}
+ public TextFieldBuilder textFieldBuilder(String fieldName) {
+ return new TextFieldBuilder(this, fieldName);
+ }
+
public NestedFieldBuilder nestedFieldBuilder(String fieldName) {
return new NestedFieldBuilder(this, fieldName);
}
/**
* Helper to define a string field in mapping of index type
*/
- public static class KeywordFieldBuilder {
+ public abstract static class StringFieldBuilder {
private final NewIndexType indexType;
private final String fieldName;
private boolean disableSearch = false;
private SortedMap<String, Object> subFields = Maps.newTreeMap();
private boolean store = false;
- private KeywordFieldBuilder(NewIndexType indexType, String fieldName) {
+ private StringFieldBuilder(NewIndexType indexType, String fieldName) {
this.indexType = indexType;
this.fieldName = fieldName;
}
* Add a sub-field. A {@code SortedMap} is required for consistency of the index settings hash.
* @see IndexDefinitionHash
*/
- private KeywordFieldBuilder addSubField(String fieldName, SortedMap<String, String> fieldDefinition) {
+ private StringFieldBuilder addSubField(String fieldName, SortedMap<String, String> fieldDefinition) {
subFields.put(fieldName, fieldDefinition);
return this;
}
/**
* Add subfields, one for each analyzer.
*/
- public KeywordFieldBuilder addSubFields(DefaultIndexSettingsElement... analyzers) {
+ public StringFieldBuilder addSubFields(DefaultIndexSettingsElement... analyzers) {
Arrays.stream(analyzers)
- .forEach(analyzer -> addSubField(analyzer.getSubFieldSuffix(), analyzer.fieldMapping()));
+ .forEach(analyzer -> addSubField(analyzer.getSubFieldSuffix(), analyzer.fieldMapping()));
return this;
}
* https://www.elastic.co/guide/en/elasticsearch/reference/2.3/norms.html
* https://www.elastic.co/guide/en/elasticsearch/guide/current/scoring-theory.html#field-norm
*/
- public KeywordFieldBuilder disableNorms() {
+ public StringFieldBuilder disableNorms() {
this.disableNorms = true;
return this;
}
/**
* Position offset term vectors are required for the fast_vector_highlighter (fvh).
*/
- public KeywordFieldBuilder termVectorWithPositionOffsetsForAllSubfields() {
+ public StringFieldBuilder termVectorWithPositionOffsetsForAllSubfields() {
this.termVectorWithPositionOffsets = true;
return this;
}
* By default field is "true": it is searchable, but index the value exactly
* as specified.
*/
- public KeywordFieldBuilder disableSearch() {
+ public StringFieldBuilder disableSearch() {
this.disableSearch = true;
return this;
}
- public KeywordFieldBuilder store() {
+ public StringFieldBuilder store() {
this.store = true;
return this;
}
Map<String, Object> hash = new TreeMap<>();
if (subFields.isEmpty()) {
hash.putAll(ImmutableMap.of(
- "type", FIELD_TYPE_KEYWORD,
- "index", disableSearch ? INDEX_NOT_SEARCHABLE : INDEX_SEARCHABLE,
- "norms", String.valueOf(!disableNorms),
- "store", String.valueOf(store)));
+ "type", getFieldType(),
+ "index", disableSearch ? INDEX_NOT_SEARCHABLE : INDEX_SEARCHABLE,
+ "norms", String.valueOf(!disableNorms),
+ "store", String.valueOf(store)));
+ if (getFieldData()) {
+ hash.put(FIELD_FIELDDATA, FIELDDATA_ENABLED);
+ }
} else {
- hash.put("type", "multi_field");
+ hash.put("type", getFieldType());
Map<String, Object> multiFields = new TreeMap<>(subFields);
}
});
}
+ if (getFieldData()) {
+ multiFields.entrySet().forEach(entry -> {
+ Object subFieldMapping = entry.getValue();
+ if (subFieldMapping instanceof Map) {
+ entry.setValue(
+ addFieldToMapping(
+ (Map<String, String>) subFieldMapping,
+ FIELD_FIELDDATA, FIELDDATA_ENABLED));
+ }
+ });
+ hash.put(FIELD_FIELDDATA, FIELDDATA_ENABLED);
+ }
multiFields.put(fieldName, ImmutableMap.of(
- "type", FIELD_TYPE_KEYWORD,
- "index", INDEX_SEARCHABLE,
- "norms", "false",
- "store", String.valueOf(store)
- ));
+ "type", getFieldType(),
+ "index", INDEX_SEARCHABLE,
+ "norms", "false",
+ "store", String.valueOf(store)));
hash.put("fields", multiFields);
}
return indexType.setProperty(fieldName, hash);
}
+ protected abstract boolean getFieldData();
+
+ protected abstract String getFieldType();
+
private static SortedMap<String, String> addFieldToMapping(Map<String, String> source, String key, String value) {
SortedMap<String, String> mutable = new TreeMap<>(source);
mutable.put(key, value);
}
}
+ public static class KeywordFieldBuilder extends StringFieldBuilder {
+
+ private KeywordFieldBuilder(NewIndexType indexType, String fieldName) {
+ super(indexType, fieldName);
+ }
+
+ @Override
+ protected boolean getFieldData() {
+ return false;
+ }
+
+ protected String getFieldType() {
+ return FIELD_TYPE_KEYWORD;
+ }
+ }
+
+ public static class TextFieldBuilder extends StringFieldBuilder {
+
+ private boolean fieldData = false;
+
+ private TextFieldBuilder(NewIndexType indexType, String fieldName) {
+ super(indexType, fieldName);
+ }
+
+ protected String getFieldType() {
+ return FIELD_TYPE_TEXT;
+ }
+
+ /**
+ * Required to enable sorting, aggregation and access to field data on fields of type "text".
+ * <p>Disabled by default as this can have significant memory cost</p>
+ */
+ public StringFieldBuilder withFieldData() {
+ this.fieldData = true;
+ return this;
+ }
+
+ @Override
+ protected boolean getFieldData() {
+ return fieldData;
+ }
+ }
+
public static class NestedFieldBuilder {
private final NewIndexType indexType;
private final String fieldName;