Elastic Search: Split reusable code to utility classes
As a preparatory step before adding the implementation of the account
index in Elastic Search, split out code that can be reused into utility
classes.
Change-Id: I46e0fe4e4e2ef969565cd2f481ab45c46397016e
Signed-off-by: Dariusz Luksza <dluksza@collab.net>
Signed-off-by: David Pursehouse <dpursehouse@collab.net>
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
index 75b1f2e..917238b 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/AbstractElasticIndex.java
@@ -15,9 +15,11 @@
package com.google.gerrit.elasticsearch;
import static com.google.common.base.Preconditions.checkState;
+import static org.apache.commons.codec.binary.Base64.decodeBase64;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import com.google.common.base.Strings;
+import com.google.common.collect.FluentIterable;
import com.google.common.collect.Iterables;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
@@ -26,6 +28,9 @@
import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.index.Schema;
import com.google.gerrit.server.index.Schema.Values;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gwtorm.protobuf.ProtobufCodec;
import org.eclipse.jgit.lib.Config;
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -33,6 +38,7 @@
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.List;
import java.util.concurrent.TimeUnit;
import io.searchbox.client.JestClientFactory;
@@ -46,6 +52,16 @@
import io.searchbox.indices.IndicesExists;
abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
+ protected static <T> List<T> decodeProtos(JsonObject doc, String fieldName,
+ ProtobufCodec<T> codec) {
+ JsonArray field = doc.getAsJsonArray(fieldName);
+ if (field == null) {
+ return null;
+ }
+ return FluentIterable.from(field)
+ .transform(i -> codec.decode(decodeBase64(i.toString())))
+ .toList();
+ }
private final Schema<V> schema;
private final FillArgs fillArgs;
@@ -55,7 +71,6 @@
protected final String indexName;
protected final JestHttpClient client;
-
AbstractElasticIndex(@GerritServerConfig Config cfg,
FillArgs fillArgs,
SitePaths sitePaths,
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
index 762b868..a4c74ed 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticChangeIndex.java
@@ -34,9 +34,7 @@
import com.google.gerrit.server.ReviewerSet;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
-import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.FieldDef.FillArgs;
-import com.google.gerrit.server.index.FieldType;
import com.google.gerrit.server.index.IndexUtils;
import com.google.gerrit.server.index.QueryOptions;
import com.google.gerrit.server.index.Schema;
@@ -56,7 +54,6 @@
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-import com.google.gwtorm.protobuf.ProtobufCodec;
import com.google.gwtorm.server.OrmException;
import com.google.gwtorm.server.ResultSet;
import com.google.inject.Provider;
@@ -94,30 +91,9 @@
MappingProperties closedChanges;
ChangeMapping(Schema<ChangeData> schema) {
- ElasticMapping.Builder mappingBuilder = new ElasticMapping.Builder();
- for (FieldDef<?, ?> field : schema.getFields().values()) {
- String name = field.getName();
- FieldType<?> fieldType = field.getType();
- if (fieldType == FieldType.EXACT) {
- mappingBuilder.addExactField(name);
- } else if (fieldType == FieldType.TIMESTAMP) {
- mappingBuilder.addTimestamp(name);
- } else if (fieldType == FieldType.INTEGER
- || fieldType == FieldType.INTEGER_RANGE
- || fieldType == FieldType.LONG) {
- mappingBuilder.addNumber(name);
- } else if (fieldType == FieldType.PREFIX
- || fieldType == FieldType.FULL_TEXT
- || fieldType == FieldType.STORED_ONLY) {
- mappingBuilder.addString(name);
- } else {
- throw new IllegalArgumentException(
- "Unsupported field type " + fieldType.getName());
- }
- }
- MappingProperties mapping = mappingBuilder.build();
- openChanges = mapping;
- closedChanges = mapping;
+ MappingProperties mapping = ElasticMapping.createMapping(schema);
+ this.openChanges = mapping;
+ this.closedChanges = mapping;
}
}
@@ -149,17 +125,6 @@
.setFieldNamingPolicy(LOWER_CASE_WITH_UNDERSCORES).create();
}
- private static <T> List<T> decodeProtos(JsonObject doc, String fieldName,
- ProtobufCodec<T> codec) {
- JsonArray field = doc.getAsJsonArray(fieldName);
- if (field == null) {
- return null;
- }
- return FluentIterable.from(field)
- .transform(i -> codec.decode(decodeBase64(i.toString())))
- .toList();
- }
-
@Override
public void replace(ChangeData cd) throws IOException {
String deleteIndex;
@@ -236,7 +201,7 @@
sort.setIgnoreUnmapped();
}
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
- fields = IndexUtils.fields(opts);
+ fields = IndexUtils.changeFields(opts);
SearchSourceBuilder searchSource = new SearchSourceBuilder()
.query(qb)
.from(opts.start())
diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java
index e3f7e96..45f686f 100644
--- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java
+++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticMapping.java
@@ -15,10 +15,38 @@
package com.google.gerrit.elasticsearch;
import com.google.common.collect.ImmutableMap;
+import com.google.gerrit.server.index.FieldDef;
+import com.google.gerrit.server.index.FieldType;
+import com.google.gerrit.server.index.Schema;
import java.util.Map;
class ElasticMapping {
+ static MappingProperties createMapping(Schema<?> schema) {
+ ElasticMapping.Builder mapping = new ElasticMapping.Builder();
+ for (FieldDef<?, ?> field : schema.getFields().values()) {
+ String name = field.getName();
+ FieldType<?> fieldType = field.getType();
+ if (fieldType == FieldType.EXACT) {
+ mapping.addExactField(name);
+ } else if (fieldType == FieldType.TIMESTAMP) {
+ mapping.addTimestamp(name);
+ } else if (fieldType == FieldType.INTEGER
+ || fieldType == FieldType.INTEGER_RANGE
+ || fieldType == FieldType.LONG) {
+ mapping.addNumber(name);
+ } else if (fieldType == FieldType.PREFIX
+ || fieldType == FieldType.FULL_TEXT
+ || fieldType == FieldType.STORED_ONLY) {
+ mapping.addString(name);
+ } else {
+ throw new IllegalStateException(
+ "Unsupported field type: " + fieldType.getName());
+ }
+ }
+ return mapping.build();
+ }
+
static class Builder {
private final ImmutableMap.Builder<String, FieldProperties> fields =
new ImmutableMap.Builder<>();
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryChangesTest.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryChangesTest.java
index 9a6755f..4b76e4b 100644
--- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryChangesTest.java
+++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticQueryChangesTest.java
@@ -14,123 +14,60 @@
package com.google.gerrit.elasticsearch;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.truth.Truth.assertThat;
import static com.google.gerrit.elasticsearch.ElasticChangeIndex.CHANGES_PREFIX;
import static com.google.gerrit.elasticsearch.ElasticChangeIndex.CLOSED_CHANGES;
import static com.google.gerrit.elasticsearch.ElasticChangeIndex.OPEN_CHANGES;
-import com.google.common.base.Strings;
-import com.google.common.io.Files;
import com.google.gerrit.elasticsearch.ElasticChangeIndex.ChangeMapping;
-import com.google.gerrit.server.index.IndexModule.IndexType;
+import com.google.gerrit.elasticsearch.ElasticTestUtils.ElasticNodeInfo;
import com.google.gerrit.server.index.change.ChangeSchemaDefinitions;
import com.google.gerrit.server.query.change.AbstractQueryChangesTest;
import com.google.gerrit.testutil.InMemoryModule;
import com.google.gerrit.testutil.InMemoryRepositoryManager.Repo;
-import com.google.gson.FieldNamingPolicy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
-import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
-import org.elasticsearch.common.settings.Settings;
-import org.elasticsearch.node.Node;
-import org.elasticsearch.node.NodeBuilder;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
-import java.io.File;
-import java.nio.file.Path;
-import java.util.Iterator;
-import java.util.Map;
import java.util.concurrent.ExecutionException;
public class ElasticQueryChangesTest extends AbstractQueryChangesTest {
- private static final Gson gson = new GsonBuilder()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .create();
private static final String INDEX_NAME =
String.format("%s%04d", CHANGES_PREFIX,
ChangeSchemaDefinitions.INSTANCE.getLatest().getVersion());
- private static Node node;
- private static String port;
- private static File elasticDir;
-
- static class NodeInfo {
- String httpAddress;
- }
-
- static class Info {
- Map<String, NodeInfo> nodes;
- }
+ private static ElasticNodeInfo nodeInfo;
@BeforeClass
public static void startIndexService()
throws InterruptedException, ExecutionException {
- if (node != null) {
+ if (nodeInfo != null) {
// do not start Elasticsearch twice
return;
}
- elasticDir = Files.createTempDir();
- Path elasticDirPath = elasticDir.toPath();
- Settings settings = Settings.settingsBuilder()
- .put("cluster.name", "gerrit")
- .put("node.name", "Gerrit Elasticsearch Test Node")
- .put("node.local", true)
- .put("discovery.zen.ping.multicast.enabled", false)
- .put("index.store.fs.memory.enabled", true)
- .put("index.gateway.type", "none")
- .put("index.max_result_window", Integer.MAX_VALUE)
- .put("gateway.type", "default")
- .put("http.port", 0)
- .put("discovery.zen.ping.unicast.hosts", "[\"localhost\"]")
- .put("path.home", elasticDirPath.toAbsolutePath())
- .put("path.data", elasticDirPath.resolve("data").toAbsolutePath())
- .put("path.work", elasticDirPath.resolve("work").toAbsolutePath())
- .put("path.logs", elasticDirPath.resolve("logs").toAbsolutePath())
- .put("transport.tcp.connect_timeout", "60s")
- .build();
-
- // Start the node
- node = NodeBuilder.nodeBuilder()
- .settings(settings)
- .node();
-
- // Wait for it to be ready
- node.client()
- .admin()
- .cluster()
- .prepareHealth()
- .setWaitForYellowStatus()
- .execute()
- .actionGet();
+ nodeInfo = ElasticTestUtils.startElasticsearchNode();
createIndexes();
-
- assertThat(node.isClosed()).isFalse();
- port = getHttpPort();
}
@After
public void cleanupIndex() {
- node.client().admin().indices().prepareDelete(INDEX_NAME).execute();
- createIndexes();
+ if (nodeInfo != null) {
+ ElasticTestUtils.deleteIndexes(nodeInfo.node, INDEX_NAME);
+ createIndexes();
+ }
}
@AfterClass
public static void stopElasticsearchServer() {
- if (node != null) {
- node.close();
- node = null;
- }
- if (elasticDir != null && elasticDir.delete()) {
- elasticDir = null;
+ if (nodeInfo != null) {
+ nodeInfo.node.close();
+ nodeInfo.elasticDir.delete();
+ nodeInfo = null;
}
}
@@ -138,11 +75,7 @@
protected Injector createInjector() {
Config elasticsearchConfig = new Config(config);
InMemoryModule.setDefaults(elasticsearchConfig);
- elasticsearchConfig.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
- elasticsearchConfig.setString("index", null, "protocol", "http");
- elasticsearchConfig.setString("index", null, "hostname", "localhost");
- elasticsearchConfig.setString("index", null, "port", port);
- elasticsearchConfig.setBoolean("index", "elasticsearch", "test", true);
+ ElasticTestUtils.configure(elasticsearchConfig, nodeInfo.port);
return Guice.createInjector(
new InMemoryModule(elasticsearchConfig, notesMigration));
}
@@ -154,35 +87,19 @@
new ChangeMapping(ChangeSchemaDefinitions.INSTANCE.getLatest());
openChangesMapping.closedChanges = null;
closedChangesMapping.openChanges = null;
- node.client()
+ nodeInfo.node
+ .client()
.admin()
.indices()
.prepareCreate(INDEX_NAME)
- .addMapping(OPEN_CHANGES, gson.toJson(openChangesMapping))
- .addMapping(CLOSED_CHANGES, gson.toJson(closedChangesMapping))
+ .addMapping(OPEN_CHANGES,
+ ElasticTestUtils.gson.toJson(openChangesMapping))
+ .addMapping(CLOSED_CHANGES,
+ ElasticTestUtils.gson.toJson(closedChangesMapping))
.execute()
.actionGet();
}
- private static String getHttpPort()
- throws InterruptedException, ExecutionException {
- String nodes = node.client().admin().cluster()
- .nodesInfo(new NodesInfoRequest("*")).get().toString();
- Gson gson = new GsonBuilder()
- .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
- .create();
- Info info = gson.fromJson(nodes, Info.class);
-
- checkState(info.nodes != null && info.nodes.size() == 1);
- Iterator<NodeInfo> values = info.nodes.values().iterator();
- String httpAddress = values.next().httpAddress;
-
- checkState(
- !Strings.isNullOrEmpty(httpAddress) && httpAddress.indexOf(':') > 0);
- return httpAddress.substring(httpAddress.indexOf(':') + 1,
- httpAddress.length());
- }
-
@Test
public void byOwnerInvalidQuery() throws Exception {
TestRepository<Repo> repo = createProject("repo");
diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
new file mode 100644
index 0000000..7c26edc
--- /dev/null
+++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticTestUtils.java
@@ -0,0 +1,144 @@
+// Copyright (C) 2016 The Android Open Source Project
+//
+// Licensed 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 com.google.gerrit.elasticsearch;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.base.Strings;
+import com.google.common.io.Files;
+import com.google.gerrit.server.index.IndexModule.IndexType;
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import org.eclipse.jgit.lib.Config;
+import org.elasticsearch.action.admin.cluster.node.info.NodesInfoRequest;
+import org.elasticsearch.common.settings.Settings;
+import org.elasticsearch.node.Node;
+import org.elasticsearch.node.NodeBuilder;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.concurrent.ExecutionException;
+
+final class ElasticTestUtils {
+ static final Gson gson = new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
+
+ static class ElasticNodeInfo {
+ final Node node;
+ final String port;
+ final File elasticDir;
+
+ private ElasticNodeInfo(Node node, File rootDir, String port) {
+ this.node = node;
+ this.port = port;
+ this.elasticDir = rootDir;
+ }
+ }
+
+ static void configure(Config config, String port) {
+ config.setEnum("index", null, "type", IndexType.ELASTICSEARCH);
+ config.setString("index", null, "protocol", "http");
+ config.setString("index", null, "hostname", "localhost");
+ config.setString("index", null, "port", port);
+ config.setBoolean("index", "elasticsearch", "test", true);
+ }
+
+ static ElasticNodeInfo startElasticsearchNode()
+ throws InterruptedException, ExecutionException {
+ File elasticDir = Files.createTempDir();
+ Path elasticDirPath = elasticDir.toPath();
+ Settings settings = Settings.settingsBuilder()
+ .put("cluster.name", "gerrit")
+ .put("node.name", "Gerrit Elasticsearch Test Node")
+ .put("node.local", true)
+ .put("discovery.zen.ping.multicast.enabled", false)
+ .put("index.store.fs.memory.enabled", true)
+ .put("index.gateway.type", "none")
+ .put("index.max_result_window", Integer.MAX_VALUE)
+ .put("gateway.type", "default")
+ .put("http.port", 0)
+ .put("discovery.zen.ping.unicast.hosts", "[\"localhost\"]")
+ .put("path.home", elasticDirPath.toAbsolutePath())
+ .put("path.data", elasticDirPath.resolve("data").toAbsolutePath())
+ .put("path.work", elasticDirPath.resolve("work").toAbsolutePath())
+ .put("path.logs", elasticDirPath.resolve("logs").toAbsolutePath())
+ .put("transport.tcp.connect_timeout", "60s")
+ .build();
+
+ // Start the node
+ Node node = NodeBuilder.nodeBuilder()
+ .settings(settings)
+ .node();
+
+ // Wait for it to be ready
+ node.client()
+ .admin()
+ .cluster()
+ .prepareHealth()
+ .setWaitForYellowStatus()
+ .execute()
+ .actionGet();
+
+ assertThat(node.isClosed()).isFalse();
+ return new ElasticNodeInfo(node, elasticDir, getHttpPort(node));
+ }
+
+ static void deleteIndexes(Node node, String index) {
+ node.client().admin().indices().prepareDelete(index).execute();
+ }
+
+ static class NodeInfo {
+ String httpAddress;
+ }
+
+ static class Info {
+ Map<String, NodeInfo> nodes;
+ }
+
+ private static String getHttpPort(Node node)
+ throws InterruptedException, ExecutionException {
+ String nodes = node.client().admin().cluster()
+ .nodesInfo(new NodesInfoRequest("*")).get().toString();
+ Gson gson = new GsonBuilder()
+ .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
+ .create();
+ Info info = gson.fromJson(nodes, Info.class);
+ if (info.nodes == null || info.nodes.size() != 1) {
+ throw new RuntimeException(
+ "Cannot extract local Elasticsearch http port");
+ }
+ Iterator<NodeInfo> values = info.nodes.values().iterator();
+ String httpAddress = values.next().httpAddress;
+ if (Strings.isNullOrEmpty(httpAddress)) {
+ throw new RuntimeException(
+ "Cannot extract local Elasticsearch http port");
+ }
+ if (httpAddress.indexOf(':') < 0) {
+ throw new RuntimeException(
+ "Seems that port is not included in Elasticsearch http_address");
+ }
+ return httpAddress.substring(httpAddress.indexOf(':') + 1,
+ httpAddress.length());
+ }
+
+ private ElasticTestUtils() {
+ // hide default constructor
+ }
+}