diff --git a/gerrit-elasticsearch/BUILD b/gerrit-elasticsearch/BUILD index f91f98f3e8..0c19cafc7e 100644 --- a/gerrit-elasticsearch/BUILD +++ b/gerrit-elasticsearch/BUILD @@ -63,6 +63,7 @@ junit_tests( "//gerrit-server:query_tests_code", "//gerrit-server:server", "//gerrit-server:testutil", + "//lib:truth", "//lib/guice", "//lib/httpcomponents:httpcore", "//lib/jgit/org.eclipse.jgit:jgit", diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java index fa87a3f42a..e78416d9c3 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java +++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexModule.java @@ -64,6 +64,6 @@ public class ElasticIndexModule extends AbstractIndexModule { @Override protected Class getVersionManager() { - return ElasticVersionManager.class; + return ElasticIndexVersionManager.class; } } diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersionManager.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java similarity index 95% rename from gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersionManager.java rename to gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java index 4f7fbe315b..1ddff777a8 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersionManager.java +++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticIndexVersionManager.java @@ -32,14 +32,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton -public class ElasticVersionManager extends VersionManager { - private static final Logger log = LoggerFactory.getLogger(ElasticVersionManager.class); +public class ElasticIndexVersionManager extends VersionManager { + private static final Logger log = LoggerFactory.getLogger(ElasticIndexVersionManager.class); private final String prefix; private final ElasticIndexVersionDiscovery versionDiscovery; @Inject - ElasticVersionManager( + ElasticIndexVersionManager( ElasticConfiguration cfg, SitePaths sitePaths, DynamicSet listeners, 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 9e1c7298db..bf6da5c97d 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 @@ -26,7 +26,7 @@ class ElasticMapping { for (FieldDef field : schema.getFields().values()) { String name = field.getName(); FieldType fieldType = field.getType(); - if (fieldType == FieldType.EXACT) { + if (fieldType == FieldType.EXACT || fieldType == FieldType.KEYWORD) { mapping.addExactField(name); } else if (fieldType == FieldType.TIMESTAMP) { mapping.addTimestamp(name); diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java index 2a97e2ef39..c8c6345946 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java +++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticQueryBuilder.java @@ -94,7 +94,7 @@ public class ElasticQueryBuilder { return intRangeQuery(p); } else if (type == FieldType.TIMESTAMP) { return timestampQuery(p); - } else if (type == FieldType.EXACT) { + } else if (type == FieldType.EXACT || type == FieldType.KEYWORD) { return exactQuery(p); } else if (type == FieldType.PREFIX) { return QueryBuilders.matchPhrasePrefixQuery(name, value); diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java index a81cae6085..91f938c956 100644 --- a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java +++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticRestClientProvider.java @@ -67,8 +67,8 @@ class ElasticRestClientProvider implements Provider, LifecycleListen synchronized (this) { if (client == null) { client = build(); - String version = getVersion(); - log.info("Connected to Elasticsearch version {}", version); + ElasticVersion version = getVersion(); + log.info("Elasticsearch integration version {}", version); } } } @@ -102,20 +102,23 @@ class ElasticRestClientProvider implements Provider, LifecycleListen } } - private String getVersion() throws ElasticException { + private ElasticVersion getVersion() throws ElasticException { try { Response response = client.performRequest("GET", ""); StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() != HttpStatus.SC_OK) { throw new FailedToGetVersion(statusLine); } - return new JsonParser() - .parse(AbstractElasticIndex.getContent(response)) - .getAsJsonObject() - .get("version") - .getAsJsonObject() - .get("number") - .getAsString(); + String version = + new JsonParser() + .parse(AbstractElasticIndex.getContent(response)) + .getAsJsonObject() + .get("version") + .getAsJsonObject() + .get("number") + .getAsString(); + log.info("Connected to Elasticsearch version {}", version); + return ElasticVersion.forVersion(version); } catch (IOException e) { throw new FailedToGetVersion(e); } diff --git a/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersion.java b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersion.java new file mode 100644 index 0000000000..ff26382a93 --- /dev/null +++ b/gerrit-elasticsearch/src/main/java/com/google/gerrit/elasticsearch/ElasticVersion.java @@ -0,0 +1,60 @@ +// Copyright (C) 2018 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 com.google.common.base.Joiner; +import java.util.regex.Pattern; + +public enum ElasticVersion { + V2_4("2.4.*"), + V5_6("5.6.*"), + V6_2("6.2.*"); + + private final String version; + private final Pattern pattern; + + private ElasticVersion(String version) { + this.version = version; + this.pattern = Pattern.compile(version); + } + + public static class InvalidVersion extends ElasticException { + private static final long serialVersionUID = 1L; + + InvalidVersion(String version) { + super( + String.format( + "Invalid version: [%s]. Supported versions: %s", version, supportedVersions())); + } + } + + public static ElasticVersion forVersion(String version) throws InvalidVersion { + for (ElasticVersion value : ElasticVersion.values()) { + if (value.pattern.matcher(version).matches()) { + return value; + } + } + throw new InvalidVersion(version); + } + + public static String supportedVersions() { + return Joiner.on(", ").join(ElasticVersion.values()); + } + + @Override + public String toString() { + return version; + } +} diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java index 862c340696..6df742c1d0 100644 --- a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java +++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticContainer.java @@ -15,6 +15,7 @@ package com.google.gerrit.elasticsearch; import com.google.common.collect.ImmutableSet; +import com.google.gerrit.elasticsearch.ElasticVersion; import java.util.Set; import org.apache.http.HttpHost; import org.junit.internal.AssumptionViolatedException; @@ -24,13 +25,7 @@ import org.testcontainers.containers.GenericContainer; public class ElasticContainer> extends GenericContainer { private static final int ELASTICSEARCH_DEFAULT_PORT = 9200; - public enum Version { - V2, - V5, - V6 - } - - public static ElasticContainer createAndStart(Version version) { + public static ElasticContainer createAndStart(ElasticVersion version) { // Assumption violation is not natively supported by Testcontainers. // See https://github.com/testcontainers/testcontainers-java/issues/343 try { @@ -43,22 +38,22 @@ public class ElasticContainer> extends Gener } public static ElasticContainer createAndStart() { - return createAndStart(Version.V2); + return createAndStart(ElasticVersion.V2_4); } - private static String getImageName(Version version) { + private static String getImageName(ElasticVersion version) { switch (version) { - case V2: + case V2_4: return "elasticsearch:2.4.6-alpine"; - case V5: + case V5_6: return "elasticsearch:5.6.9-alpine"; - case V6: + case V6_2: return "docker.elastic.co/elasticsearch/elasticsearch:6.2.4"; } - throw new IllegalStateException("Unsupported version: " + version.name()); + throw new IllegalStateException("No tests for version: " + version.name()); } - private ElasticContainer(Version version) { + private ElasticContainer(ElasticVersion version) { super(getImageName(version)); } diff --git a/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticVersionTest.java b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticVersionTest.java new file mode 100644 index 0000000000..e0da86a9e2 --- /dev/null +++ b/gerrit-elasticsearch/src/test/java/com/google/gerrit/elasticsearch/ElasticVersionTest.java @@ -0,0 +1,45 @@ +// Copyright (C) 2018 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 org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class ElasticVersionTest { + @Rule public ExpectedException exception = ExpectedException.none(); + + @Test + public void supportedVersion() throws Exception { + assertThat(ElasticVersion.forVersion("2.4.0")).isEqualTo(ElasticVersion.V2_4); + assertThat(ElasticVersion.forVersion("2.4.6")).isEqualTo(ElasticVersion.V2_4); + + assertThat(ElasticVersion.forVersion("5.6.0")).isEqualTo(ElasticVersion.V5_6); + assertThat(ElasticVersion.forVersion("5.6.9")).isEqualTo(ElasticVersion.V5_6); + + assertThat(ElasticVersion.forVersion("6.2.0")).isEqualTo(ElasticVersion.V6_2); + assertThat(ElasticVersion.forVersion("6.2.4")).isEqualTo(ElasticVersion.V6_2); + } + + @Test + public void unsupportedVersion() throws Exception { + exception.expect(ElasticVersion.InvalidVersion.class); + exception.expectMessage( + "Invalid version: [4.0.0]. Supported versions: " + ElasticVersion.supportedVersions()); + ElasticVersion.forVersion("4.0.0"); + } +} diff --git a/gerrit-index/src/main/java/com/google/gerrit/index/FieldDef.java b/gerrit-index/src/main/java/com/google/gerrit/index/FieldDef.java index b1ffac1a1b..1d0a5946fe 100644 --- a/gerrit-index/src/main/java/com/google/gerrit/index/FieldDef.java +++ b/gerrit-index/src/main/java/com/google/gerrit/index/FieldDef.java @@ -34,6 +34,10 @@ public final class FieldDef { return new FieldDef.Builder<>(FieldType.EXACT, name); } + public static FieldDef.Builder keyword(String name) { + return new FieldDef.Builder<>(FieldType.KEYWORD, name); + } + public static FieldDef.Builder fullText(String name) { return new FieldDef.Builder<>(FieldType.FULL_TEXT, name); } diff --git a/gerrit-index/src/main/java/com/google/gerrit/index/FieldType.java b/gerrit-index/src/main/java/com/google/gerrit/index/FieldType.java index 0db0284388..376c071f04 100644 --- a/gerrit-index/src/main/java/com/google/gerrit/index/FieldType.java +++ b/gerrit-index/src/main/java/com/google/gerrit/index/FieldType.java @@ -33,6 +33,9 @@ public class FieldType { /** A string field searched using exact-match semantics. */ public static final FieldType EXACT = new FieldType<>("EXACT"); + /** A Keyword field searched using non-analyzed-match semantics. */ + public static final FieldType KEYWORD = new FieldType<>("KEYWORD"); + /** A string field searched using prefix. */ public static final FieldType PREFIX = new FieldType<>("PREFIX"); diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AbstractLuceneIndex.java index 9d474dd8ea..7c4aa4fec2 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AbstractLuceneIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AbstractLuceneIndex.java @@ -311,7 +311,7 @@ public abstract class AbstractLuceneIndex implements Index { for (Object value : values.getValues()) { doc.add(new LongField(name, ((Timestamp) value).getTime(), store)); } - } else if (type == FieldType.EXACT || type == FieldType.PREFIX) { + } else if (type == FieldType.KEYWORD || type == FieldType.EXACT || type == FieldType.PREFIX) { for (Object value : values.getValues()) { doc.add(new StringField(name, (String) value, store)); } diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java index 4500942d35..c7fc8800a2 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/QueryBuilder.java @@ -141,20 +141,21 @@ public class QueryBuilder { "field not in schema v%s: %s", schema.getVersion(), p.getField().getName()); - if (p.getType() == FieldType.INTEGER) { + FieldType type = p.getType(); + if (type == FieldType.INTEGER) { return intQuery(p); - } else if (p.getType() == FieldType.INTEGER_RANGE) { + } else if (type == FieldType.INTEGER_RANGE) { return intRangeQuery(p); - } else if (p.getType() == FieldType.TIMESTAMP) { + } else if (type == FieldType.TIMESTAMP) { return timestampQuery(p); - } else if (p.getType() == FieldType.EXACT) { + } else if (type == FieldType.EXACT || type == FieldType.KEYWORD) { return exactQuery(p); - } else if (p.getType() == FieldType.PREFIX) { + } else if (type == FieldType.PREFIX) { return prefixQuery(p); - } else if (p.getType() == FieldType.FULL_TEXT) { + } else if (type == FieldType.FULL_TEXT) { return fullTextQuery(p); } else { - throw FieldType.badFieldType(p.getType()); + throw FieldType.badFieldType(type); } } diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/group/GroupField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/group/GroupField.java index 078433aaf4..c7d22ff266 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/group/GroupField.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/group/GroupField.java @@ -18,6 +18,7 @@ import static com.google.common.collect.ImmutableList.toImmutableList; import static com.google.gerrit.index.FieldDef.exact; import static com.google.gerrit.index.FieldDef.fullText; import static com.google.gerrit.index.FieldDef.integer; +import static com.google.gerrit.index.FieldDef.keyword; import static com.google.gerrit.index.FieldDef.prefix; import static com.google.gerrit.index.FieldDef.timestamp; @@ -36,11 +37,11 @@ public class GroupField { /** Group UUID. */ public static final FieldDef UUID = - exact("uuid").stored().build(g -> g.getGroupUUID().get()); + keyword("uuid").stored().build(g -> g.getGroupUUID().get()); /** Group owner UUID. */ public static final FieldDef OWNER_UUID = - exact("owner_uuid").build(g -> g.getOwnerGroupUUID().get()); + keyword("owner_uuid").build(g -> g.getOwnerGroupUUID().get()); /** Timestamp indicating when this group was created. */ public static final FieldDef CREATED_ON =