All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.sonar.server.es.NewIndex Maven / Gradle / Ivy

There is a newer version: 7.0
Show newest version
/*
 * SonarQube
 * Copyright (C) 2009-2016 SonarSource SA
 * mailto:contact AT sonarsource DOT com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package org.sonar.server.es;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Maps;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import javax.annotation.CheckForNull;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.sonar.api.config.Settings;
import org.sonar.process.ProcessProperties;

import static java.lang.String.format;
import static org.sonar.server.es.BaseIndex.SEARCH_PARTIAL_SUFFIX;
import static org.sonar.server.es.BaseIndex.SEARCH_WORDS_SUFFIX;
import static org.sonar.server.es.BaseIndex.SORT_SUFFIX;

public class NewIndex {

  public static final int DEFAULT_NUMBER_OF_SHARDS = 5;

  public void refreshHandledByIndexer() {
    getSettings().put("index.refresh_interval", "-1");
  }

  public static class NewIndexType {
    private final String name;
    private final Map attributes = new TreeMap<>();
    private final Map properties = new TreeMap<>();

    private NewIndexType(String typeName) {
      this.name = typeName;
      // defaults
      attributes.put("dynamic", false);
      attributes.put("_all", ImmutableSortedMap.of("enabled", false));
      attributes.put("_source", ImmutableSortedMap.of("enabled", true));
      attributes.put("properties", properties);
    }

    public String getName() {
      return name;
    }

    /**
     * Complete the root json hash of mapping type, for example to set the attribute "_id"
     */
    public NewIndexType setAttribute(String key, Object value) {
      attributes.put(key, value);
      return this;
    }

    /**
     * Complete the json hash named "properties" in mapping type, usually to declare fields
     */
    public NewIndexType setProperty(String key, Object value) {
      properties.put(key, value);
      return this;
    }

    public NewIndexType setEnableSource(boolean enableSource) {
      attributes.put("_source", ImmutableSortedMap.of("enabled", enableSource));
      return this;
    }

    public StringFieldBuilder stringFieldBuilder(String fieldName) {
      return new StringFieldBuilder(this, fieldName);
    }

    public NestedObjectBuilder nestedObjectBuilder(String fieldName, NewIndexType nestedMapping) {
      return new NestedObjectBuilder(this, nestedMapping, fieldName);
    }

    public NewIndexType createBooleanField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "boolean"));
    }

    public NewIndexType createByteField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "byte"));
    }

    public NewIndexType createDateTimeField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "date", "format", "date_time"));
    }

    public NewIndexType createDoubleField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "double"));
    }

    public NewIndexType createIntegerField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "integer"));
    }

    public NewIndexType createLongField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "long"));
    }

    public NewIndexType createDynamicNestedField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "nested", "dynamic", "true"));
    }

    public NewIndexType createShortField(String fieldName) {
      return setProperty(fieldName, ImmutableMap.of("type", "short"));
    }

    public NewIndexType createUuidPathField(String fieldName) {
      return setProperty(fieldName, ImmutableSortedMap.of(
        "type", "string",
        "index", "analyzed",
        "analyzer", "uuid_analyzer"));
    }

    public Map getAttributes() {
      return attributes;
    }

    @CheckForNull
    public Object getProperty(String key) {
      return properties.get(key);
    }
  }

  public static class NestedObjectBuilder {
    private final NewIndexType indexType;
    private final NewIndexType nestedType;
    private final String fieldName;

    public NestedObjectBuilder(NewIndexType indexType, NewIndexType nestedType, String fieldName) {
      this.indexType = indexType;
      this.nestedType = nestedType;
      this.fieldName = fieldName;
    }

    public void build() {
      nestedType.setAttribute("type", "nested");
      indexType.setProperty(fieldName, nestedType.attributes);
    }
  }

  /**
   * Helper to define a string field in mapping of index type
   */
  public static class StringFieldBuilder {
    private final NewIndexType indexType;
    private final String fieldName;
    private boolean docValues = false;
    private boolean disableSearch = false;
    private boolean hasAnalyzedField = false;
    private boolean disableNorms = false;
    private SortedMap subFields = Maps.newTreeMap();

    private StringFieldBuilder(NewIndexType indexType, String fieldName) {
      this.indexType = indexType;
      this.fieldName = fieldName;
    }

    /**
     * It is recommended to enable doc_values when the non-analyzed field is involved
     * into aggregations/sorting and only a small fraction of the values is used (high
     * number of different values)
     * 
    *
  • https://www.elastic.co/blog/found-sizing-elasticsearch
  • *
  • https://www.elastic.co/guide/en/elasticsearch/guide/current/doc-values.html
  • *
*/ public StringFieldBuilder docValues() { this.docValues = true; return this; } /** * Add a sub-field. A {@code SortedMap} is required for consistency of the index settings hash. * @see IndexDefinitionHash */ public StringFieldBuilder addSubField(String fieldName, SortedMap fieldDefinition) { subFields.put(fieldName, fieldDefinition); hasAnalyzedField |= "analyzed".equals(fieldDefinition.get("index")); return this; } /** * Create an inner-field named "sort" with analyzer "sortable" */ public StringFieldBuilder enableSorting() { addSubField(SORT_SUFFIX, ImmutableSortedMap.of( "type", "string", "index", "analyzed", "analyzer", "sortable")); return this; } /** * Create an inner-field named "words" with analyzer "words" */ public StringFieldBuilder enableWordSearch() { addSubField(SEARCH_WORDS_SUFFIX, ImmutableSortedMap.of( "type", "string", "index", "analyzed", "index_analyzer", "index_words", "search_analyzer", "search_words")); return this; } /** * Create a inner-field named "grams" with analyzer "grams" */ public StringFieldBuilder enableGramSearch() { addSubField(SEARCH_PARTIAL_SUFFIX, ImmutableSortedMap.of( "type", "string", "index", "analyzed", "index_analyzer", "index_grams", "search_analyzer", "search_grams")); return this; } /** * Norms consume useless memory if string field is used for filtering or aggregations. * * 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 StringFieldBuilder disableNorms() { this.disableNorms = true; return this; } /** * "index: no" -> Don’t index this field at all. This field will not be searchable. * By default field is "not_analyzed": it is searchable, but index the value exactly * as specified. */ public StringFieldBuilder disableSearch() { this.disableSearch = true; return this; } public void build() { validate(); Map hash = new TreeMap<>(); if (subFields.isEmpty()) { hash.putAll(ImmutableMap.of( "type", "string", "index", disableSearch ? "no" : "not_analyzed", "norms", ImmutableMap.of("enabled", String.valueOf(!disableNorms)), "doc_values", docValues)); } else { hash.put("type", "multi_field"); Map multiFields = new TreeMap<>(subFields); multiFields.put(fieldName, ImmutableMap.of( "type", "string", "index", "not_analyzed", "norms", ImmutableMap.of("enabled", "false"), "doc_values", docValues)); hash.put("fields", multiFields); } indexType.setProperty(fieldName, hash); } private void validate() { if (docValues && hasAnalyzedField) { throw new IllegalStateException("Doc values are not supported on analyzed strings of field: " + fieldName); } if (disableSearch && hasAnalyzedField) { throw new IllegalStateException("Can't mix searchable and non-searchable arguments on field: " + fieldName); } } } private final String indexName; private final ImmutableSettings.Builder settings = DefaultIndexSettings.defaults(); private final SortedMap types = new TreeMap<>(); NewIndex(String indexName) { Preconditions.checkArgument(StringUtils.isAllLowerCase(indexName), "Index name must be lower-case: " + indexName); this.indexName = indexName; } public String getName() { return indexName; } public ImmutableSettings.Builder getSettings() { return settings; } public NewIndexType createType(String typeName) { NewIndexType type = new NewIndexType(typeName); types.put(typeName, type); return type; } public SortedMap getTypes() { return types; } public void configureShards(Settings settings) { boolean clusterMode = settings.getBoolean(ProcessProperties.CLUSTER_ACTIVATE); int shards = settings.getInt(format("sonar.search.%s.shards", indexName)); if (shards == 0) { shards = DEFAULT_NUMBER_OF_SHARDS; } int replicas = settings.getInt(format("sonar.search.%s.replicas", indexName)); if (replicas == 0) { replicas = clusterMode ? 1 : 0; } getSettings().put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, shards); getSettings().put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, replicas); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy