Add ElasticQuerySource to ElasticLuceneIndex
This commit is a no-op change that reduces duplicate code in the ES indices by creating a shared implementation of QuerySource. Change-Id: Ibbb0058c3a0d9b99516ed86a342bf809148becbb
This commit is contained in:
@@ -23,15 +23,21 @@ import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
|
|||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.FluentIterable;
|
import com.google.common.collect.FluentIterable;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.ListMultimap;
|
import com.google.common.collect.ListMultimap;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Streams;
|
import com.google.common.collect.Streams;
|
||||||
import com.google.gerrit.index.FieldDef;
|
import com.google.gerrit.index.FieldDef;
|
||||||
import com.google.gerrit.index.FieldType;
|
import com.google.gerrit.index.FieldType;
|
||||||
import com.google.gerrit.index.Index;
|
import com.google.gerrit.index.Index;
|
||||||
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.Schema.Values;
|
import com.google.gerrit.index.Schema.Values;
|
||||||
|
import com.google.gerrit.index.query.DataSource;
|
||||||
import com.google.gerrit.index.query.FieldBundle;
|
import com.google.gerrit.index.query.FieldBundle;
|
||||||
|
import com.google.gerrit.index.query.Predicate;
|
||||||
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
@@ -41,24 +47,38 @@ import com.google.gson.JsonArray;
|
|||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gwtorm.protobuf.ProtobufCodec;
|
import com.google.gwtorm.protobuf.ProtobufCodec;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.gwtorm.server.ResultSet;
|
||||||
import io.searchbox.client.JestResult;
|
import io.searchbox.client.JestResult;
|
||||||
import io.searchbox.client.http.JestHttpClient;
|
import io.searchbox.client.http.JestHttpClient;
|
||||||
import io.searchbox.core.Bulk;
|
import io.searchbox.core.Bulk;
|
||||||
import io.searchbox.core.Delete;
|
import io.searchbox.core.Delete;
|
||||||
|
import io.searchbox.core.Search;
|
||||||
|
import io.searchbox.core.search.sort.Sort;
|
||||||
import io.searchbox.indices.CreateIndex;
|
import io.searchbox.indices.CreateIndex;
|
||||||
import io.searchbox.indices.DeleteIndex;
|
import io.searchbox.indices.DeleteIndex;
|
||||||
import io.searchbox.indices.IndicesExists;
|
import io.searchbox.indices.IndicesExists;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.common.xcontent.XContentBuilder;
|
import org.elasticsearch.common.xcontent.XContentBuilder;
|
||||||
|
import org.elasticsearch.index.query.QueryBuilder;
|
||||||
|
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(AbstractElasticIndex.class);
|
||||||
|
|
||||||
protected static <T> List<T> decodeProtos(
|
protected static <T> List<T> decodeProtos(
|
||||||
JsonObject doc, String fieldName, ProtobufCodec<T> codec) {
|
JsonObject doc, String fieldName, ProtobufCodec<T> codec) {
|
||||||
JsonArray field = doc.getAsJsonArray(fieldName);
|
JsonArray field = doc.getAsJsonArray(fieldName);
|
||||||
@@ -158,7 +178,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
|||||||
|
|
||||||
protected io.searchbox.core.Index insert(String type, V v) throws IOException {
|
protected io.searchbox.core.Index insert(String type, V v) throws IOException {
|
||||||
String id = getId(v);
|
String id = getId(v);
|
||||||
String doc = toDoc(v);
|
String doc = toDocument(v);
|
||||||
return new io.searchbox.core.Index.Builder(doc).index(indexName).type(type).id(id).build();
|
return new io.searchbox.core.Index.Builder(doc).index(indexName).type(type).id(id).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +186,7 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
|||||||
return !(element instanceof String) || !((String) element).isEmpty();
|
return !(element instanceof String) || !((String) element).isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String toDoc(V v) throws IOException {
|
private String toDocument(V v) throws IOException {
|
||||||
XContentBuilder builder = jsonBuilder().startObject();
|
XContentBuilder builder = jsonBuilder().startObject();
|
||||||
for (Values<V> values : schema.buildFields(v)) {
|
for (Values<V> values : schema.buildFields(v)) {
|
||||||
String name = values.getField().getName();
|
String name = values.getField().getName();
|
||||||
@@ -184,6 +204,8 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
|||||||
return builder.endObject().string();
|
return builder.endObject().string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract V fromDocument(JsonObject doc, Set<String> fields);
|
||||||
|
|
||||||
protected FieldBundle toFieldBundle(JsonObject doc) {
|
protected FieldBundle toFieldBundle(JsonObject doc) {
|
||||||
Map<String, FieldDef<V, ?>> allFields = getSchema().getFields();
|
Map<String, FieldDef<V, ?>> allFields = getSchema().getFields();
|
||||||
ListMultimap<String, Object> rawFields = ArrayListMultimap.create();
|
ListMultimap<String, Object> rawFields = ArrayListMultimap.create();
|
||||||
@@ -215,4 +237,95 @@ abstract class AbstractElasticIndex<K, V> implements Index<K, V> {
|
|||||||
}
|
}
|
||||||
return new FieldBundle(rawFields);
|
return new FieldBundle(rawFields);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected class ElasticQuerySource implements DataSource<V> {
|
||||||
|
private final QueryOptions opts;
|
||||||
|
private final Search search;
|
||||||
|
|
||||||
|
ElasticQuerySource(Predicate<V> p, QueryOptions opts, String type, Sort sort)
|
||||||
|
throws QueryParseException {
|
||||||
|
this(p, opts, ImmutableList.of(type), ImmutableList.of(sort));
|
||||||
|
}
|
||||||
|
|
||||||
|
ElasticQuerySource(
|
||||||
|
Predicate<V> p, QueryOptions opts, Collection<String> types, Collection<Sort> sorts)
|
||||||
|
throws QueryParseException {
|
||||||
|
this.opts = opts;
|
||||||
|
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
||||||
|
SearchSourceBuilder searchSource =
|
||||||
|
new SearchSourceBuilder()
|
||||||
|
.query(qb)
|
||||||
|
.from(opts.start())
|
||||||
|
.size(opts.limit())
|
||||||
|
.fields(Lists.newArrayList(opts.fields()));
|
||||||
|
|
||||||
|
search =
|
||||||
|
new Search.Builder(searchSource.toString())
|
||||||
|
.addType(types)
|
||||||
|
.addSort(sorts)
|
||||||
|
.addIndex(indexName)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCardinality() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet<V> read() throws OrmException {
|
||||||
|
return readImpl((doc) -> AbstractElasticIndex.this.fromDocument(doc, opts.fields()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ResultSet<FieldBundle> readRaw() throws OrmException {
|
||||||
|
return readImpl(AbstractElasticIndex.this::toFieldBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return search.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
|
||||||
|
try {
|
||||||
|
List<T> results = Collections.emptyList();
|
||||||
|
JestResult result = client.execute(search);
|
||||||
|
if (result.isSucceeded()) {
|
||||||
|
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
||||||
|
if (obj.get("hits") != null) {
|
||||||
|
JsonArray json = obj.getAsJsonArray("hits");
|
||||||
|
results = Lists.newArrayListWithCapacity(json.size());
|
||||||
|
for (int i = 0; i < json.size(); i++) {
|
||||||
|
T mapperResult = mapper.apply(json.get(i).getAsJsonObject());
|
||||||
|
if (mapperResult != null) {
|
||||||
|
results.add(mapperResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.error(result.getErrorMessage());
|
||||||
|
}
|
||||||
|
final List<T> r = Collections.unmodifiableList(results);
|
||||||
|
return new ResultSet<T>() {
|
||||||
|
@Override
|
||||||
|
public Iterator<T> iterator() {
|
||||||
|
return r.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<T> toList() {
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new OrmException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,11 @@ package com.google.gerrit.elasticsearch;
|
|||||||
|
|
||||||
import static com.google.gerrit.server.index.account.AccountField.ID;
|
import static com.google.gerrit.server.index.account.AccountField.ID;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.query.DataSource;
|
import com.google.gerrit.index.query.DataSource;
|
||||||
import com.google.gerrit.index.query.FieldBundle;
|
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
@@ -34,31 +31,19 @@ import com.google.gerrit.server.config.SitePaths;
|
|||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.index.account.AccountField;
|
import com.google.gerrit.server.index.account.AccountField;
|
||||||
import com.google.gerrit.server.index.account.AccountIndex;
|
import com.google.gerrit.server.index.account.AccountIndex;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gwtorm.server.OrmException;
|
|
||||||
import com.google.gwtorm.server.ResultSet;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import io.searchbox.client.JestResult;
|
import io.searchbox.client.JestResult;
|
||||||
import io.searchbox.core.Bulk;
|
import io.searchbox.core.Bulk;
|
||||||
import io.searchbox.core.Bulk.Builder;
|
import io.searchbox.core.Bulk.Builder;
|
||||||
import io.searchbox.core.Search;
|
|
||||||
import io.searchbox.core.search.sort.Sort;
|
import io.searchbox.core.search.sort.Sort;
|
||||||
import io.searchbox.core.search.sort.Sort.Sorting;
|
import io.searchbox.core.search.sort.Sort.Sorting;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
|
public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, AccountState>
|
||||||
implements AccountIndex {
|
implements AccountIndex {
|
||||||
@@ -73,8 +58,6 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
|
|||||||
static final String ACCOUNTS = "accounts";
|
static final String ACCOUNTS = "accounts";
|
||||||
static final String ACCOUNTS_PREFIX = ACCOUNTS + "_";
|
static final String ACCOUNTS_PREFIX = ACCOUNTS + "_";
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ElasticAccountIndex.class);
|
|
||||||
|
|
||||||
private final AccountMapping mapping;
|
private final AccountMapping mapping;
|
||||||
private final Provider<AccountCache> accountCache;
|
private final Provider<AccountCache> accountCache;
|
||||||
|
|
||||||
@@ -111,7 +94,9 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
|
|||||||
@Override
|
@Override
|
||||||
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
|
public DataSource<AccountState> getSource(Predicate<AccountState> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new QuerySource(p, opts);
|
Sort sort = new Sort(AccountField.ID.getName(), Sorting.ASC);
|
||||||
|
sort.setIgnoreUnmapped();
|
||||||
|
return new ElasticQuerySource(p, opts.filterFields(IndexUtils::accountFields), ACCOUNTS, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -130,104 +115,18 @@ public class ElasticAccountIndex extends AbstractElasticIndex<Account.Id, Accoun
|
|||||||
return as.getAccount().getId().toString();
|
return as.getAccount().getId().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements DataSource<AccountState> {
|
@Override
|
||||||
private final Search search;
|
protected AccountState fromDocument(JsonObject json, Set<String> fields) {
|
||||||
private final Set<String> fields;
|
JsonElement source = json.get("_source");
|
||||||
|
if (source == null) {
|
||||||
QuerySource(Predicate<AccountState> p, QueryOptions opts) throws QueryParseException {
|
source = json.getAsJsonObject().get("fields");
|
||||||
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
|
||||||
fields = IndexUtils.accountFields(opts);
|
|
||||||
SearchSourceBuilder searchSource =
|
|
||||||
new SearchSourceBuilder()
|
|
||||||
.query(qb)
|
|
||||||
.from(opts.start())
|
|
||||||
.size(opts.limit())
|
|
||||||
.fields(Lists.newArrayList(fields));
|
|
||||||
|
|
||||||
Sort sort = new Sort(AccountField.ID.getName(), Sorting.ASC);
|
|
||||||
sort.setIgnoreUnmapped();
|
|
||||||
|
|
||||||
search =
|
|
||||||
new Search.Builder(searchSource.toString())
|
|
||||||
.addType(ACCOUNTS)
|
|
||||||
.addIndex(indexName)
|
|
||||||
.addSort(ImmutableList.of(sort))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt());
|
||||||
public int getCardinality() {
|
// Use the AccountCache rather than depending on any stored fields in the
|
||||||
return 10;
|
// document (of which there shouldn't be any). The most expensive part to
|
||||||
}
|
// compute anyway is the effective group IDs, and we don't have a good way
|
||||||
|
// to reindex when those change.
|
||||||
@Override
|
return accountCache.get().get(id);
|
||||||
public ResultSet<AccountState> read() throws OrmException {
|
|
||||||
return readImpl(this::toAccountState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultSet<FieldBundle> readRaw() throws OrmException {
|
|
||||||
return readImpl(ElasticAccountIndex.this::toFieldBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
|
|
||||||
try {
|
|
||||||
List<T> results = Collections.emptyList();
|
|
||||||
JestResult result = client.execute(search);
|
|
||||||
if (result.isSucceeded()) {
|
|
||||||
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
|
||||||
if (obj.get("hits") != null) {
|
|
||||||
JsonArray json = obj.getAsJsonArray("hits");
|
|
||||||
results = Lists.newArrayListWithCapacity(json.size());
|
|
||||||
for (int i = 0; i < json.size(); i++) {
|
|
||||||
T mapperResult = mapper.apply(json.get(i).getAsJsonObject());
|
|
||||||
if (mapperResult != null) {
|
|
||||||
results.add(mapperResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.error(result.getErrorMessage());
|
|
||||||
}
|
|
||||||
final List<T> r = Collections.unmodifiableList(results);
|
|
||||||
return new ResultSet<T>() {
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return r.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> toList() {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new OrmException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return search.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private AccountState toAccountState(JsonElement json) {
|
|
||||||
JsonElement source = json.getAsJsonObject().get("_source");
|
|
||||||
if (source == null) {
|
|
||||||
source = json.getAsJsonObject().get("fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
Account.Id id = new Account.Id(source.getAsJsonObject().get(ID.getName()).getAsInt());
|
|
||||||
// Use the AccountCache rather than depending on any stored fields in the
|
|
||||||
// document (of which there shouldn't be any). The most expensive part to
|
|
||||||
// compute anyway is the effective group IDs, and we don't have a good way
|
|
||||||
// to reindex when those change.
|
|
||||||
return accountCache.get().get(id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ import com.google.common.collect.Sets;
|
|||||||
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.query.FieldBundle;
|
import com.google.gerrit.index.query.DataSource;
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
@@ -54,39 +54,28 @@ import com.google.gerrit.server.index.change.ChangeIndex;
|
|||||||
import com.google.gerrit.server.index.change.ChangeIndexRewriter;
|
import com.google.gerrit.server.index.change.ChangeIndexRewriter;
|
||||||
import com.google.gerrit.server.project.SubmitRuleOptions;
|
import com.google.gerrit.server.project.SubmitRuleOptions;
|
||||||
import com.google.gerrit.server.query.change.ChangeData;
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
import com.google.gerrit.server.query.change.ChangeDataSource;
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.gwtorm.server.ResultSet;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import io.searchbox.client.JestResult;
|
import io.searchbox.client.JestResult;
|
||||||
import io.searchbox.core.Bulk;
|
import io.searchbox.core.Bulk;
|
||||||
import io.searchbox.core.Bulk.Builder;
|
import io.searchbox.core.Bulk.Builder;
|
||||||
import io.searchbox.core.Search;
|
|
||||||
import io.searchbox.core.search.sort.Sort;
|
import io.searchbox.core.search.sort.Sort;
|
||||||
import io.searchbox.core.search.sort.Sort.Sorting;
|
import io.searchbox.core.search.sort.Sort.Sorting;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
import org.apache.commons.codec.binary.Base64;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/** Secondary index implementation using Elasticsearch. */
|
/** Secondary index implementation using Elasticsearch. */
|
||||||
class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
|
class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
|
||||||
implements ChangeIndex {
|
implements ChangeIndex {
|
||||||
private static final Logger log = LoggerFactory.getLogger(ElasticChangeIndex.class);
|
|
||||||
|
|
||||||
static class ChangeMapping {
|
static class ChangeMapping {
|
||||||
MappingProperties openChanges;
|
MappingProperties openChanges;
|
||||||
MappingProperties closedChanges;
|
MappingProperties closedChanges;
|
||||||
@@ -155,7 +144,7 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChangeDataSource getSource(Predicate<ChangeData> p, QueryOptions opts)
|
public DataSource getSource(Predicate<ChangeData> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
Set<Change.Status> statuses = ChangeIndexRewriter.getPossibleStatus(p);
|
Set<Change.Status> statuses = ChangeIndexRewriter.getPossibleStatus(p);
|
||||||
List<String> indexes = Lists.newArrayListWithCapacity(2);
|
List<String> indexes = Lists.newArrayListWithCapacity(2);
|
||||||
@@ -165,7 +154,16 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
|
|||||||
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
if (!Sets.intersection(statuses, CLOSED_STATUSES).isEmpty()) {
|
||||||
indexes.add(CLOSED_CHANGES);
|
indexes.add(CLOSED_CHANGES);
|
||||||
}
|
}
|
||||||
return new QuerySource(indexes, p, opts);
|
|
||||||
|
List<Sort> sorts =
|
||||||
|
ImmutableList.of(
|
||||||
|
new Sort(ChangeField.UPDATED.getName(), Sorting.DESC),
|
||||||
|
new Sort(ChangeField.LEGACY_ID.getName(), Sorting.DESC));
|
||||||
|
for (Sort sort : sorts) {
|
||||||
|
sort.setIgnoreUnmapped();
|
||||||
|
}
|
||||||
|
QueryOptions filteredOpts = opts.filterFields(IndexUtils::changeFields);
|
||||||
|
return new ElasticQuerySource(p, filteredOpts, indexes, sorts);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -183,306 +181,210 @@ class ElasticChangeIndex extends AbstractElasticIndex<Change.Id, ChangeData>
|
|||||||
return cd.getId().toString();
|
return cd.getId().toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements ChangeDataSource {
|
@Override
|
||||||
private final Search search;
|
protected ChangeData fromDocument(JsonObject json, Set<String> fields) {
|
||||||
private final Set<String> fields;
|
JsonElement sourceElement = json.get("_source");
|
||||||
|
if (sourceElement == null) {
|
||||||
|
sourceElement = json.getAsJsonObject().get("fields");
|
||||||
|
}
|
||||||
|
JsonObject source = sourceElement.getAsJsonObject();
|
||||||
|
JsonElement c = source.get(ChangeField.CHANGE.getName());
|
||||||
|
|
||||||
QuerySource(List<String> types, Predicate<ChangeData> p, QueryOptions opts)
|
if (c == null) {
|
||||||
throws QueryParseException {
|
int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
|
||||||
List<Sort> sorts =
|
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
|
||||||
ImmutableList.of(
|
String projectName = checkNotNull(source.get(ChangeField.PROJECT.getName()).getAsString());
|
||||||
new Sort(ChangeField.UPDATED.getName(), Sorting.DESC),
|
return changeDataFactory.create(
|
||||||
new Sort(ChangeField.LEGACY_ID.getName(), Sorting.DESC));
|
db.get(), new Project.NameKey(projectName), new Change.Id(id));
|
||||||
for (Sort sort : sorts) {
|
}
|
||||||
sort.setIgnoreUnmapped();
|
|
||||||
|
ChangeData cd =
|
||||||
|
changeDataFactory.create(
|
||||||
|
db.get(), CHANGE_CODEC.decode(Base64.decodeBase64(c.getAsString())));
|
||||||
|
|
||||||
|
// Any decoding that is done here must also be done in {@link LuceneChangeIndex}.
|
||||||
|
|
||||||
|
// Patch sets.
|
||||||
|
cd.setPatchSets(decodeProtos(source, ChangeField.PATCH_SET.getName(), PATCH_SET_CODEC));
|
||||||
|
|
||||||
|
// Approvals.
|
||||||
|
if (source.get(ChangeField.APPROVAL.getName()) != null) {
|
||||||
|
cd.setCurrentApprovals(decodeProtos(source, ChangeField.APPROVAL.getName(), APPROVAL_CODEC));
|
||||||
|
} else if (fields.contains(ChangeField.APPROVAL.getName())) {
|
||||||
|
cd.setCurrentApprovals(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added & Deleted.
|
||||||
|
JsonElement addedElement = source.get(ChangeField.ADDED.getName());
|
||||||
|
JsonElement deletedElement = source.get(ChangeField.DELETED.getName());
|
||||||
|
if (addedElement != null && deletedElement != null) {
|
||||||
|
// Changed lines.
|
||||||
|
int added = addedElement.getAsInt();
|
||||||
|
int deleted = deletedElement.getAsInt();
|
||||||
|
if (added != 0 && deleted != 0) {
|
||||||
|
cd.setChangedLines(added, deleted);
|
||||||
}
|
}
|
||||||
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
|
||||||
fields = IndexUtils.changeFields(opts);
|
|
||||||
SearchSourceBuilder searchSource =
|
|
||||||
new SearchSourceBuilder()
|
|
||||||
.query(qb)
|
|
||||||
.from(opts.start())
|
|
||||||
.size(opts.limit())
|
|
||||||
.fields(Lists.newArrayList(fields));
|
|
||||||
|
|
||||||
search =
|
|
||||||
new Search.Builder(searchSource.toString())
|
|
||||||
.addType(types)
|
|
||||||
.addSort(sorts)
|
|
||||||
.addIndex(indexName)
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Mergeable.
|
||||||
public int getCardinality() {
|
JsonElement mergeableElement = source.get(ChangeField.MERGEABLE.getName());
|
||||||
return 10;
|
if (mergeableElement != null) {
|
||||||
|
String mergeable = mergeableElement.getAsString();
|
||||||
|
if ("1".equals(mergeable)) {
|
||||||
|
cd.setMergeable(true);
|
||||||
|
} else if ("0".equals(mergeable)) {
|
||||||
|
cd.setMergeable(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// Reviewed-by.
|
||||||
public ResultSet<ChangeData> read() throws OrmException {
|
if (source.get(ChangeField.REVIEWEDBY.getName()) != null) {
|
||||||
return readImpl(this::toChangeData);
|
JsonArray reviewedBy = source.get(ChangeField.REVIEWEDBY.getName()).getAsJsonArray();
|
||||||
}
|
if (reviewedBy.size() > 0) {
|
||||||
|
Set<Account.Id> accounts = Sets.newHashSetWithExpectedSize(reviewedBy.size());
|
||||||
@Override
|
for (int i = 0; i < reviewedBy.size(); i++) {
|
||||||
public ResultSet<FieldBundle> readRaw() throws OrmException {
|
int aId = reviewedBy.get(i).getAsInt();
|
||||||
return readImpl(ElasticChangeIndex.this::toFieldBundle);
|
if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) {
|
||||||
}
|
break;
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasChange() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return search.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
|
|
||||||
try {
|
|
||||||
List<T> results = Collections.emptyList();
|
|
||||||
JestResult result = client.execute(search);
|
|
||||||
if (result.isSucceeded()) {
|
|
||||||
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
|
||||||
if (obj.get("hits") != null) {
|
|
||||||
JsonArray json = obj.getAsJsonArray("hits");
|
|
||||||
results = Lists.newArrayListWithCapacity(json.size());
|
|
||||||
for (int i = 0; i < json.size(); i++) {
|
|
||||||
results.add(mapper.apply(json.get(i).getAsJsonObject()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
accounts.add(new Account.Id(aId));
|
||||||
log.error(result.getErrorMessage());
|
|
||||||
}
|
}
|
||||||
final List<T> r = Collections.unmodifiableList(results);
|
cd.setReviewedBy(accounts);
|
||||||
return new ResultSet<T>() {
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return r.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> toList() {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new OrmException(e);
|
|
||||||
}
|
}
|
||||||
|
} else if (fields.contains(ChangeField.REVIEWEDBY.getName())) {
|
||||||
|
cd.setReviewedBy(Collections.emptySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
private ChangeData toChangeData(JsonElement json) {
|
// Hashtag.
|
||||||
JsonElement sourceElement = json.getAsJsonObject().get("_source");
|
if (source.get(ChangeField.HASHTAG.getName()) != null) {
|
||||||
if (sourceElement == null) {
|
JsonArray hashtagArray = source.get(ChangeField.HASHTAG.getName()).getAsJsonArray();
|
||||||
sourceElement = json.getAsJsonObject().get("fields");
|
if (hashtagArray.size() > 0) {
|
||||||
}
|
Set<String> hashtags = Sets.newHashSetWithExpectedSize(hashtagArray.size());
|
||||||
JsonObject source = sourceElement.getAsJsonObject();
|
for (int i = 0; i < hashtagArray.size(); i++) {
|
||||||
JsonElement c = source.get(ChangeField.CHANGE.getName());
|
hashtags.add(hashtagArray.get(i).getAsString());
|
||||||
|
|
||||||
if (c == null) {
|
|
||||||
int id = source.get(ChangeField.LEGACY_ID.getName()).getAsInt();
|
|
||||||
// IndexUtils#changeFields ensures either CHANGE or PROJECT is always present.
|
|
||||||
String projectName = checkNotNull(source.get(ChangeField.PROJECT.getName()).getAsString());
|
|
||||||
return changeDataFactory.create(
|
|
||||||
db.get(), new Project.NameKey(projectName), new Change.Id(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
ChangeData cd =
|
|
||||||
changeDataFactory.create(
|
|
||||||
db.get(), CHANGE_CODEC.decode(Base64.decodeBase64(c.getAsString())));
|
|
||||||
|
|
||||||
// Any decoding that is done here must also be done in {@link LuceneChangeIndex}.
|
|
||||||
|
|
||||||
// Patch sets.
|
|
||||||
cd.setPatchSets(decodeProtos(source, ChangeField.PATCH_SET.getName(), PATCH_SET_CODEC));
|
|
||||||
|
|
||||||
// Approvals.
|
|
||||||
if (source.get(ChangeField.APPROVAL.getName()) != null) {
|
|
||||||
cd.setCurrentApprovals(
|
|
||||||
decodeProtos(source, ChangeField.APPROVAL.getName(), APPROVAL_CODEC));
|
|
||||||
} else if (fields.contains(ChangeField.APPROVAL.getName())) {
|
|
||||||
cd.setCurrentApprovals(Collections.emptyList());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Added & Deleted.
|
|
||||||
JsonElement addedElement = source.get(ChangeField.ADDED.getName());
|
|
||||||
JsonElement deletedElement = source.get(ChangeField.DELETED.getName());
|
|
||||||
if (addedElement != null && deletedElement != null) {
|
|
||||||
// Changed lines.
|
|
||||||
int added = addedElement.getAsInt();
|
|
||||||
int deleted = deletedElement.getAsInt();
|
|
||||||
if (added != 0 && deleted != 0) {
|
|
||||||
cd.setChangedLines(added, deleted);
|
|
||||||
}
|
}
|
||||||
|
cd.setHashtags(hashtags);
|
||||||
}
|
}
|
||||||
|
} else if (fields.contains(ChangeField.HASHTAG.getName())) {
|
||||||
// Mergeable.
|
cd.setHashtags(Collections.emptySet());
|
||||||
JsonElement mergeableElement = source.get(ChangeField.MERGEABLE.getName());
|
|
||||||
if (mergeableElement != null) {
|
|
||||||
String mergeable = mergeableElement.getAsString();
|
|
||||||
if ("1".equals(mergeable)) {
|
|
||||||
cd.setMergeable(true);
|
|
||||||
} else if ("0".equals(mergeable)) {
|
|
||||||
cd.setMergeable(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reviewed-by.
|
|
||||||
if (source.get(ChangeField.REVIEWEDBY.getName()) != null) {
|
|
||||||
JsonArray reviewedBy = source.get(ChangeField.REVIEWEDBY.getName()).getAsJsonArray();
|
|
||||||
if (reviewedBy.size() > 0) {
|
|
||||||
Set<Account.Id> accounts = Sets.newHashSetWithExpectedSize(reviewedBy.size());
|
|
||||||
for (int i = 0; i < reviewedBy.size(); i++) {
|
|
||||||
int aId = reviewedBy.get(i).getAsInt();
|
|
||||||
if (reviewedBy.size() == 1 && aId == ChangeField.NOT_REVIEWED) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
accounts.add(new Account.Id(aId));
|
|
||||||
}
|
|
||||||
cd.setReviewedBy(accounts);
|
|
||||||
}
|
|
||||||
} else if (fields.contains(ChangeField.REVIEWEDBY.getName())) {
|
|
||||||
cd.setReviewedBy(Collections.emptySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hashtag.
|
|
||||||
if (source.get(ChangeField.HASHTAG.getName()) != null) {
|
|
||||||
JsonArray hashtagArray = source.get(ChangeField.HASHTAG.getName()).getAsJsonArray();
|
|
||||||
if (hashtagArray.size() > 0) {
|
|
||||||
Set<String> hashtags = Sets.newHashSetWithExpectedSize(hashtagArray.size());
|
|
||||||
for (int i = 0; i < hashtagArray.size(); i++) {
|
|
||||||
hashtags.add(hashtagArray.get(i).getAsString());
|
|
||||||
}
|
|
||||||
cd.setHashtags(hashtags);
|
|
||||||
}
|
|
||||||
} else if (fields.contains(ChangeField.HASHTAG.getName())) {
|
|
||||||
cd.setHashtags(Collections.emptySet());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Star.
|
|
||||||
if (source.get(ChangeField.STAR.getName()) != null) {
|
|
||||||
JsonArray starArray = source.get(ChangeField.STAR.getName()).getAsJsonArray();
|
|
||||||
if (starArray.size() > 0) {
|
|
||||||
ListMultimap<Account.Id, String> stars =
|
|
||||||
MultimapBuilder.hashKeys().arrayListValues().build();
|
|
||||||
for (int i = 0; i < starArray.size(); i++) {
|
|
||||||
StarredChangesUtil.StarField starField =
|
|
||||||
StarredChangesUtil.StarField.parse(starArray.get(i).getAsString());
|
|
||||||
stars.put(starField.accountId(), starField.label());
|
|
||||||
}
|
|
||||||
cd.setStars(stars);
|
|
||||||
}
|
|
||||||
} else if (fields.contains(ChangeField.STAR.getName())) {
|
|
||||||
cd.setStars(ImmutableListMultimap.of());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reviewer.
|
|
||||||
if (source.get(ChangeField.REVIEWER.getName()) != null) {
|
|
||||||
cd.setReviewers(
|
|
||||||
ChangeField.parseReviewerFieldValues(
|
|
||||||
FluentIterable.from(source.get(ChangeField.REVIEWER.getName()).getAsJsonArray())
|
|
||||||
.transform(JsonElement::getAsString)));
|
|
||||||
} else if (fields.contains(ChangeField.REVIEWER.getName())) {
|
|
||||||
cd.setReviewers(ReviewerSet.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reviewer-by-email.
|
|
||||||
if (source.get(ChangeField.REVIEWER_BY_EMAIL.getName()) != null) {
|
|
||||||
cd.setReviewersByEmail(
|
|
||||||
ChangeField.parseReviewerByEmailFieldValues(
|
|
||||||
FluentIterable.from(
|
|
||||||
source.get(ChangeField.REVIEWER_BY_EMAIL.getName()).getAsJsonArray())
|
|
||||||
.transform(JsonElement::getAsString)));
|
|
||||||
} else if (fields.contains(ChangeField.REVIEWER_BY_EMAIL.getName())) {
|
|
||||||
cd.setReviewersByEmail(ReviewerByEmailSet.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pending-reviewer.
|
|
||||||
if (source.get(ChangeField.PENDING_REVIEWER.getName()) != null) {
|
|
||||||
cd.setPendingReviewers(
|
|
||||||
ChangeField.parseReviewerFieldValues(
|
|
||||||
FluentIterable.from(
|
|
||||||
source.get(ChangeField.PENDING_REVIEWER.getName()).getAsJsonArray())
|
|
||||||
.transform(JsonElement::getAsString)));
|
|
||||||
} else if (fields.contains(ChangeField.PENDING_REVIEWER.getName())) {
|
|
||||||
cd.setPendingReviewers(ReviewerSet.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pending-reviewer-by-email.
|
|
||||||
if (source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()) != null) {
|
|
||||||
cd.setPendingReviewersByEmail(
|
|
||||||
ChangeField.parseReviewerByEmailFieldValues(
|
|
||||||
FluentIterable.from(
|
|
||||||
source
|
|
||||||
.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName())
|
|
||||||
.getAsJsonArray())
|
|
||||||
.transform(JsonElement::getAsString)));
|
|
||||||
} else if (fields.contains(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName())) {
|
|
||||||
cd.setPendingReviewersByEmail(ReviewerByEmailSet.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stored-submit-record-strict.
|
|
||||||
decodeSubmitRecords(
|
|
||||||
source,
|
|
||||||
ChangeField.STORED_SUBMIT_RECORD_STRICT.getName(),
|
|
||||||
ChangeField.SUBMIT_RULE_OPTIONS_STRICT,
|
|
||||||
cd);
|
|
||||||
|
|
||||||
// Stored-submit-record-leniant.
|
|
||||||
decodeSubmitRecords(
|
|
||||||
source,
|
|
||||||
ChangeField.STORED_SUBMIT_RECORD_LENIENT.getName(),
|
|
||||||
ChangeField.SUBMIT_RULE_OPTIONS_LENIENT,
|
|
||||||
cd);
|
|
||||||
|
|
||||||
// Ref-state.
|
|
||||||
if (fields.contains(ChangeField.REF_STATE.getName())) {
|
|
||||||
cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ref-state-pattern.
|
|
||||||
if (fields.contains(ChangeField.REF_STATE_PATTERN.getName())) {
|
|
||||||
cd.setRefStatePatterns(getByteArray(source, ChangeField.REF_STATE_PATTERN.getName()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unresolved-comment-count.
|
|
||||||
decodeUnresolvedCommentCount(source, ChangeField.UNRESOLVED_COMMENT_COUNT.getName(), cd);
|
|
||||||
|
|
||||||
return cd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Iterable<byte[]> getByteArray(JsonObject source, String name) {
|
// Star.
|
||||||
JsonElement element = source.get(name);
|
if (source.get(ChangeField.STAR.getName()) != null) {
|
||||||
return element != null
|
JsonArray starArray = source.get(ChangeField.STAR.getName()).getAsJsonArray();
|
||||||
? Iterables.transform(element.getAsJsonArray(), e -> Base64.decodeBase64(e.getAsString()))
|
if (starArray.size() > 0) {
|
||||||
: Collections.emptyList();
|
ListMultimap<Account.Id, String> stars =
|
||||||
|
MultimapBuilder.hashKeys().arrayListValues().build();
|
||||||
|
for (int i = 0; i < starArray.size(); i++) {
|
||||||
|
StarredChangesUtil.StarField starField =
|
||||||
|
StarredChangesUtil.StarField.parse(starArray.get(i).getAsString());
|
||||||
|
stars.put(starField.accountId(), starField.label());
|
||||||
|
}
|
||||||
|
cd.setStars(stars);
|
||||||
|
}
|
||||||
|
} else if (fields.contains(ChangeField.STAR.getName())) {
|
||||||
|
cd.setStars(ImmutableListMultimap.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decodeSubmitRecords(
|
// Reviewer.
|
||||||
JsonObject doc, String fieldName, SubmitRuleOptions opts, ChangeData out) {
|
if (source.get(ChangeField.REVIEWER.getName()) != null) {
|
||||||
JsonArray records = doc.getAsJsonArray(fieldName);
|
cd.setReviewers(
|
||||||
if (records == null) {
|
ChangeField.parseReviewerFieldValues(
|
||||||
return;
|
FluentIterable.from(source.get(ChangeField.REVIEWER.getName()).getAsJsonArray())
|
||||||
}
|
.transform(JsonElement::getAsString)));
|
||||||
ChangeField.parseSubmitRecords(
|
} else if (fields.contains(ChangeField.REVIEWER.getName())) {
|
||||||
FluentIterable.from(records)
|
cd.setReviewers(ReviewerSet.empty());
|
||||||
.transform(i -> new String(decodeBase64(i.toString()), UTF_8))
|
|
||||||
.toList(),
|
|
||||||
opts,
|
|
||||||
out);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void decodeUnresolvedCommentCount(JsonObject doc, String fieldName, ChangeData out) {
|
// Reviewer-by-email.
|
||||||
JsonElement count = doc.get(fieldName);
|
if (source.get(ChangeField.REVIEWER_BY_EMAIL.getName()) != null) {
|
||||||
if (count == null) {
|
cd.setReviewersByEmail(
|
||||||
return;
|
ChangeField.parseReviewerByEmailFieldValues(
|
||||||
}
|
FluentIterable.from(
|
||||||
out.setUnresolvedCommentCount(count.getAsInt());
|
source.get(ChangeField.REVIEWER_BY_EMAIL.getName()).getAsJsonArray())
|
||||||
|
.transform(JsonElement::getAsString)));
|
||||||
|
} else if (fields.contains(ChangeField.REVIEWER_BY_EMAIL.getName())) {
|
||||||
|
cd.setReviewersByEmail(ReviewerByEmailSet.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pending-reviewer.
|
||||||
|
if (source.get(ChangeField.PENDING_REVIEWER.getName()) != null) {
|
||||||
|
cd.setPendingReviewers(
|
||||||
|
ChangeField.parseReviewerFieldValues(
|
||||||
|
FluentIterable.from(
|
||||||
|
source.get(ChangeField.PENDING_REVIEWER.getName()).getAsJsonArray())
|
||||||
|
.transform(JsonElement::getAsString)));
|
||||||
|
} else if (fields.contains(ChangeField.PENDING_REVIEWER.getName())) {
|
||||||
|
cd.setPendingReviewers(ReviewerSet.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pending-reviewer-by-email.
|
||||||
|
if (source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()) != null) {
|
||||||
|
cd.setPendingReviewersByEmail(
|
||||||
|
ChangeField.parseReviewerByEmailFieldValues(
|
||||||
|
FluentIterable.from(
|
||||||
|
source.get(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName()).getAsJsonArray())
|
||||||
|
.transform(JsonElement::getAsString)));
|
||||||
|
} else if (fields.contains(ChangeField.PENDING_REVIEWER_BY_EMAIL.getName())) {
|
||||||
|
cd.setPendingReviewersByEmail(ReviewerByEmailSet.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stored-submit-record-strict.
|
||||||
|
decodeSubmitRecords(
|
||||||
|
source,
|
||||||
|
ChangeField.STORED_SUBMIT_RECORD_STRICT.getName(),
|
||||||
|
ChangeField.SUBMIT_RULE_OPTIONS_STRICT,
|
||||||
|
cd);
|
||||||
|
|
||||||
|
// Stored-submit-record-leniant.
|
||||||
|
decodeSubmitRecords(
|
||||||
|
source,
|
||||||
|
ChangeField.STORED_SUBMIT_RECORD_LENIENT.getName(),
|
||||||
|
ChangeField.SUBMIT_RULE_OPTIONS_LENIENT,
|
||||||
|
cd);
|
||||||
|
|
||||||
|
// Ref-state.
|
||||||
|
if (fields.contains(ChangeField.REF_STATE.getName())) {
|
||||||
|
cd.setRefStates(getByteArray(source, ChangeField.REF_STATE.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ref-state-pattern.
|
||||||
|
if (fields.contains(ChangeField.REF_STATE_PATTERN.getName())) {
|
||||||
|
cd.setRefStatePatterns(getByteArray(source, ChangeField.REF_STATE_PATTERN.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unresolved-comment-count.
|
||||||
|
decodeUnresolvedCommentCount(source, ChangeField.UNRESOLVED_COMMENT_COUNT.getName(), cd);
|
||||||
|
|
||||||
|
return cd;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Iterable<byte[]> getByteArray(JsonObject source, String name) {
|
||||||
|
JsonElement element = source.get(name);
|
||||||
|
return element != null
|
||||||
|
? Iterables.transform(element.getAsJsonArray(), e -> Base64.decodeBase64(e.getAsString()))
|
||||||
|
: Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeSubmitRecords(
|
||||||
|
JsonObject doc, String fieldName, SubmitRuleOptions opts, ChangeData out) {
|
||||||
|
JsonArray records = doc.getAsJsonArray(fieldName);
|
||||||
|
if (records == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ChangeField.parseSubmitRecords(
|
||||||
|
FluentIterable.from(records)
|
||||||
|
.transform(i -> new String(decodeBase64(i.toString()), UTF_8))
|
||||||
|
.toList(),
|
||||||
|
opts,
|
||||||
|
out);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void decodeUnresolvedCommentCount(JsonObject doc, String fieldName, ChangeData out) {
|
||||||
|
JsonElement count = doc.get(fieldName);
|
||||||
|
if (count == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
out.setUnresolvedCommentCount(count.getAsInt());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.elasticsearch;
|
package com.google.gerrit.elasticsearch;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.query.DataSource;
|
import com.google.gerrit.index.query.DataSource;
|
||||||
import com.google.gerrit.index.query.FieldBundle;
|
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
@@ -32,31 +29,18 @@ import com.google.gerrit.server.group.InternalGroup;
|
|||||||
import com.google.gerrit.server.index.IndexUtils;
|
import com.google.gerrit.server.index.IndexUtils;
|
||||||
import com.google.gerrit.server.index.group.GroupField;
|
import com.google.gerrit.server.index.group.GroupField;
|
||||||
import com.google.gerrit.server.index.group.GroupIndex;
|
import com.google.gerrit.server.index.group.GroupIndex;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gwtorm.server.OrmException;
|
|
||||||
import com.google.gwtorm.server.ResultSet;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import io.searchbox.client.JestResult;
|
import io.searchbox.client.JestResult;
|
||||||
import io.searchbox.core.Bulk;
|
import io.searchbox.core.Bulk;
|
||||||
import io.searchbox.core.Bulk.Builder;
|
import io.searchbox.core.Bulk.Builder;
|
||||||
import io.searchbox.core.Search;
|
|
||||||
import io.searchbox.core.search.sort.Sort;
|
import io.searchbox.core.search.sort.Sort;
|
||||||
import io.searchbox.core.search.sort.Sort.Sorting;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Function;
|
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, InternalGroup>
|
public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, InternalGroup>
|
||||||
implements GroupIndex {
|
implements GroupIndex {
|
||||||
@@ -71,8 +55,6 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
|
|||||||
static final String GROUPS = "groups";
|
static final String GROUPS = "groups";
|
||||||
static final String GROUPS_PREFIX = GROUPS + "_";
|
static final String GROUPS_PREFIX = GROUPS + "_";
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ElasticGroupIndex.class);
|
|
||||||
|
|
||||||
private final GroupMapping mapping;
|
private final GroupMapping mapping;
|
||||||
private final Provider<GroupCache> groupCache;
|
private final Provider<GroupCache> groupCache;
|
||||||
|
|
||||||
@@ -109,7 +91,9 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
|
|||||||
@Override
|
@Override
|
||||||
public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts)
|
public DataSource<InternalGroup> getSource(Predicate<InternalGroup> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new QuerySource(p, opts);
|
Sort sort = new Sort(GroupField.UUID.getName(), Sort.Sorting.ASC);
|
||||||
|
sort.setIgnoreUnmapped();
|
||||||
|
return new ElasticQuerySource(p, opts.filterFields(IndexUtils::groupFields), GROUPS, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -128,104 +112,18 @@ public class ElasticGroupIndex extends AbstractElasticIndex<AccountGroup.UUID, I
|
|||||||
return group.getGroupUUID().get();
|
return group.getGroupUUID().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements DataSource<InternalGroup> {
|
@Override
|
||||||
private final Search search;
|
protected InternalGroup fromDocument(JsonObject json, Set<String> fields) {
|
||||||
private final Set<String> fields;
|
JsonElement source = json.get("_source");
|
||||||
|
if (source == null) {
|
||||||
QuerySource(Predicate<InternalGroup> p, QueryOptions opts) throws QueryParseException {
|
source = json.getAsJsonObject().get("fields");
|
||||||
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
|
||||||
fields = IndexUtils.groupFields(opts);
|
|
||||||
SearchSourceBuilder searchSource =
|
|
||||||
new SearchSourceBuilder()
|
|
||||||
.query(qb)
|
|
||||||
.from(opts.start())
|
|
||||||
.size(opts.limit())
|
|
||||||
.fields(Lists.newArrayList(fields));
|
|
||||||
|
|
||||||
Sort sort = new Sort(GroupField.UUID.getName(), Sorting.ASC);
|
|
||||||
sort.setIgnoreUnmapped();
|
|
||||||
|
|
||||||
search =
|
|
||||||
new Search.Builder(searchSource.toString())
|
|
||||||
.addType(GROUPS)
|
|
||||||
.addIndex(indexName)
|
|
||||||
.addSort(ImmutableList.of(sort))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
AccountGroup.UUID uuid =
|
||||||
public int getCardinality() {
|
new AccountGroup.UUID(
|
||||||
return 10;
|
source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
|
||||||
}
|
// Use the GroupCache rather than depending on any stored fields in the
|
||||||
|
// document (of which there shouldn't be any).
|
||||||
@Override
|
return groupCache.get().get(uuid).orElse(null);
|
||||||
public ResultSet<InternalGroup> read() throws OrmException {
|
|
||||||
return readImpl(this::toInternalGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultSet<FieldBundle> readRaw() throws OrmException {
|
|
||||||
return readImpl(ElasticGroupIndex.this::toFieldBundle);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return search.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private <T> ResultSet<T> readImpl(Function<JsonObject, T> mapper) throws OrmException {
|
|
||||||
try {
|
|
||||||
List<T> results = Collections.emptyList();
|
|
||||||
JestResult result = client.execute(search);
|
|
||||||
if (result.isSucceeded()) {
|
|
||||||
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
|
||||||
if (obj.get("hits") != null) {
|
|
||||||
JsonArray json = obj.getAsJsonArray("hits");
|
|
||||||
results = Lists.newArrayListWithCapacity(json.size());
|
|
||||||
for (int i = 0; i < json.size(); i++) {
|
|
||||||
T mapperResult = mapper.apply(json.get(i).getAsJsonObject());
|
|
||||||
if (mapperResult != null) {
|
|
||||||
results.add(mapperResult);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.error(result.getErrorMessage());
|
|
||||||
}
|
|
||||||
final List<T> r = Collections.unmodifiableList(results);
|
|
||||||
return new ResultSet<T>() {
|
|
||||||
@Override
|
|
||||||
public Iterator<T> iterator() {
|
|
||||||
return r.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> toList() {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new OrmException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private InternalGroup toInternalGroup(JsonObject json) {
|
|
||||||
JsonElement source = json.get("_source");
|
|
||||||
if (source == null) {
|
|
||||||
source = json.getAsJsonObject().get("fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
AccountGroup.UUID uuid =
|
|
||||||
new AccountGroup.UUID(
|
|
||||||
source.getAsJsonObject().get(GroupField.UUID.getName()).getAsString());
|
|
||||||
// Use the GroupCache rather than depending on any stored fields in the
|
|
||||||
// document (of which there shouldn't be any).
|
|
||||||
return groupCache.get().get(uuid).orElse(null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,14 +14,11 @@
|
|||||||
|
|
||||||
package com.google.gerrit.elasticsearch;
|
package com.google.gerrit.elasticsearch;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Lists;
|
|
||||||
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
import com.google.gerrit.elasticsearch.ElasticMapping.MappingProperties;
|
||||||
import com.google.gerrit.index.QueryOptions;
|
import com.google.gerrit.index.QueryOptions;
|
||||||
import com.google.gerrit.index.Schema;
|
import com.google.gerrit.index.Schema;
|
||||||
import com.google.gerrit.index.query.DataSource;
|
import com.google.gerrit.index.query.DataSource;
|
||||||
import com.google.gerrit.index.query.FieldBundle;
|
|
||||||
import com.google.gerrit.index.query.Predicate;
|
import com.google.gerrit.index.query.Predicate;
|
||||||
import com.google.gerrit.index.query.QueryParseException;
|
import com.google.gerrit.index.query.QueryParseException;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
@@ -32,30 +29,19 @@ import com.google.gerrit.server.index.project.ProjectField;
|
|||||||
import com.google.gerrit.server.index.project.ProjectIndex;
|
import com.google.gerrit.server.index.project.ProjectIndex;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gerrit.server.project.ProjectData;
|
import com.google.gerrit.server.project.ProjectData;
|
||||||
import com.google.gson.JsonArray;
|
|
||||||
import com.google.gson.JsonElement;
|
import com.google.gson.JsonElement;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
import com.google.gwtorm.server.OrmException;
|
|
||||||
import com.google.gwtorm.server.ResultSet;
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import io.searchbox.client.JestResult;
|
import io.searchbox.client.JestResult;
|
||||||
import io.searchbox.core.Bulk;
|
import io.searchbox.core.Bulk;
|
||||||
import io.searchbox.core.Bulk.Builder;
|
import io.searchbox.core.Bulk.Builder;
|
||||||
import io.searchbox.core.Search;
|
|
||||||
import io.searchbox.core.search.sort.Sort;
|
import io.searchbox.core.search.sort.Sort;
|
||||||
import io.searchbox.core.search.sort.Sort.Sorting;
|
import io.searchbox.core.search.sort.Sort.Sorting;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.elasticsearch.index.query.QueryBuilder;
|
|
||||||
import org.elasticsearch.search.builder.SearchSourceBuilder;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, ProjectData>
|
public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, ProjectData>
|
||||||
implements ProjectIndex {
|
implements ProjectIndex {
|
||||||
@@ -70,8 +56,6 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
|
|||||||
static final String PROJECTS = "projects";
|
static final String PROJECTS = "projects";
|
||||||
static final String PROJECTS_PREFIX = PROJECTS + "_";
|
static final String PROJECTS_PREFIX = PROJECTS + "_";
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ElasticProjectIndex.class);
|
|
||||||
|
|
||||||
private final ProjectMapping mapping;
|
private final ProjectMapping mapping;
|
||||||
private final Provider<ProjectCache> projectCache;
|
private final Provider<ProjectCache> projectCache;
|
||||||
|
|
||||||
@@ -108,7 +92,9 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
|
|||||||
@Override
|
@Override
|
||||||
public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts)
|
public DataSource<ProjectData> getSource(Predicate<ProjectData> p, QueryOptions opts)
|
||||||
throws QueryParseException {
|
throws QueryParseException {
|
||||||
return new QuerySource(p, opts);
|
Sort sort = new Sort(ProjectField.NAME.getName(), Sorting.ASC);
|
||||||
|
sort.setIgnoreUnmapped();
|
||||||
|
return new ElasticQuerySource(p, opts.filterFields(IndexUtils::projectFields), PROJECTS, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -127,96 +113,16 @@ public class ElasticProjectIndex extends AbstractElasticIndex<Project.NameKey, P
|
|||||||
return projectState.getProject().getName();
|
return projectState.getProject().getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
private class QuerySource implements DataSource<ProjectData> {
|
@Override
|
||||||
private final Search search;
|
protected ProjectData fromDocument(JsonObject json, Set<String> fields) {
|
||||||
private final Set<String> fields;
|
JsonElement source = json.get("_source");
|
||||||
|
if (source == null) {
|
||||||
QuerySource(Predicate<ProjectData> p, QueryOptions opts) throws QueryParseException {
|
source = json.getAsJsonObject().get("fields");
|
||||||
QueryBuilder qb = queryBuilder.toQueryBuilder(p);
|
|
||||||
fields = IndexUtils.projectFields(opts);
|
|
||||||
SearchSourceBuilder searchSource =
|
|
||||||
new SearchSourceBuilder()
|
|
||||||
.query(qb)
|
|
||||||
.from(opts.start())
|
|
||||||
.size(opts.limit())
|
|
||||||
.fields(Lists.newArrayList(fields));
|
|
||||||
|
|
||||||
Sort sort = new Sort(ProjectField.NAME.getName(), Sorting.ASC);
|
|
||||||
sort.setIgnoreUnmapped();
|
|
||||||
|
|
||||||
search =
|
|
||||||
new Search.Builder(searchSource.toString())
|
|
||||||
.addType(PROJECTS)
|
|
||||||
.addIndex(indexName)
|
|
||||||
.addSort(ImmutableList.of(sort))
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
Project.NameKey nameKey =
|
||||||
public int getCardinality() {
|
new Project.NameKey(
|
||||||
return 10;
|
source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
|
||||||
}
|
return projectCache.get().get(nameKey).toProjectData();
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultSet<ProjectData> read() throws OrmException {
|
|
||||||
try {
|
|
||||||
List<ProjectData> results = Collections.emptyList();
|
|
||||||
JestResult result = client.execute(search);
|
|
||||||
if (result.isSucceeded()) {
|
|
||||||
JsonObject obj = result.getJsonObject().getAsJsonObject("hits");
|
|
||||||
if (obj.get("hits") != null) {
|
|
||||||
JsonArray json = obj.getAsJsonArray("hits");
|
|
||||||
results = Lists.newArrayListWithCapacity(json.size());
|
|
||||||
for (int i = 0; i < json.size(); i++) {
|
|
||||||
results.add(toProjectData(json.get(i)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.error(result.getErrorMessage());
|
|
||||||
}
|
|
||||||
final List<ProjectData> r = Collections.unmodifiableList(results);
|
|
||||||
return new ResultSet<ProjectData>() {
|
|
||||||
@Override
|
|
||||||
public Iterator<ProjectData> iterator() {
|
|
||||||
return r.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<ProjectData> toList() {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
// Do nothing.
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new OrmException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ResultSet<FieldBundle> readRaw() throws OrmException {
|
|
||||||
// TOOD(hiesel): Make a generic implementation for Lucene/ES
|
|
||||||
throw new UnsupportedOperationException("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return search.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProjectData toProjectData(JsonElement json) {
|
|
||||||
JsonElement source = json.getAsJsonObject().get("_source");
|
|
||||||
if (source == null) {
|
|
||||||
source = json.getAsJsonObject().get("fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
Project.NameKey nameKey =
|
|
||||||
new Project.NameKey(
|
|
||||||
source.getAsJsonObject().get(ProjectField.NAME.getName()).getAsString());
|
|
||||||
return projectCache.get().get(nameKey).toProjectData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user