Add a "projects" field for searching projects by prefix

Index alongside the "project" field, which is an exact match field,
since we do not assume index implementations can search exact match
and prefix on the same field. We do not want to modify the existing
"project" to return prefixes.

Upgrade Lucene to 4.7.0, as this was released since the last schema
change.

Change-Id: I7c3379c087fc54af3e5790cb875a5e676e674338
This commit is contained in:
Dave Borowitz 2014-03-25 14:17:03 -07:00
parent 2212801e98
commit 4aed07b7ce
13 changed files with 129 additions and 8 deletions

View File

@ -127,6 +127,11 @@ matches project names by regular expression. The
link:http://www.brics.dk/automaton/[dk.brics.automaton
library] is used for evaluation of such patterns.
[[projects]]
projects:'PREFIX'::
+
Changes occurring in projects starting with 'PREFIX'.
[[parentproject]]
parentproject:'PROJECT'::
+

View File

@ -81,6 +81,7 @@ public class SearchSuggestOracle extends HighlightSuggestOracle {
suggestions.add("comment:");
suggestions.add("conflicts:");
suggestions.add("project:");
suggestions.add("projects:");
suggestions.add("parentproject:");
suggestions.add("branch:");
suggestions.add("topic:");

View File

@ -125,14 +125,18 @@ public class LuceneChangeIndex implements ChangeIndex {
Version lucene43 = Version.LUCENE_43;
@SuppressWarnings("deprecation")
Version lucene44 = Version.LUCENE_44;
@SuppressWarnings("deprecation")
Version lucene46 = Version.LUCENE_46;
for (Map.Entry<Integer, Schema<ChangeData>> e
: ChangeSchemas.ALL.entrySet()) {
if (e.getKey() <= 3) {
versions.put(e.getValue(), lucene43);
} else if (e.getKey() <= 5) {
versions.put(e.getValue(), lucene44);
} else if (e.getKey() <= 8) {
versions.put(e.getValue(), lucene46);
} else {
versions.put(e.getValue(), Version.LUCENE_46);
versions.put(e.getValue(), Version.LUCENE_47);
}
}
LUCENE_VERSIONS = versions.build();

View File

@ -46,6 +46,7 @@ public class QueryDocumentationExecutor {
private static final Logger log =
LoggerFactory.getLogger(QueryDocumentationExecutor.class);
@SuppressWarnings("deprecation")
private static final Version LUCENE_VERSION = Version.LUCENE_46;
private IndexSearcher searcher;

View File

@ -93,6 +93,17 @@ public class ChangeField {
}
};
/** Project containing the change, as a prefix field. */
public static final FieldDef<ChangeData, String> PROJECTS =
new FieldDef.Single<ChangeData, String>(
ChangeQueryBuilder.FIELD_PROJECTS, FieldType.PREFIX, false) {
@Override
public String get(ChangeData input, FillArgs args)
throws OrmException {
return input.change().getProject().get();
}
};
/** Reference (aka branch) the change will submit onto. */
public static final FieldDef<ChangeData, String> REF =
new FieldDef.Single<ChangeData, String>(

View File

@ -169,6 +169,29 @@ public class ChangeSchemas {
ChangeField.APPROVAL,
ChangeField.MERGEABLE);
static final Schema<ChangeData> V9 = release(
ChangeField.LEGACY_ID,
ChangeField.ID,
ChangeField.STATUS,
ChangeField.PROJECT,
ChangeField.PROJECTS,
ChangeField.REF,
ChangeField.TOPIC,
ChangeField.UPDATED,
ChangeField.FILE_PART,
ChangeField.PATH,
ChangeField.OWNER,
ChangeField.REVIEWER,
ChangeField.COMMIT,
ChangeField.TR,
ChangeField.LABEL,
ChangeField.REVIEWED,
ChangeField.COMMIT_MESSAGE,
ChangeField.COMMENT,
ChangeField.CHANGE,
ChangeField.APPROVAL,
ChangeField.MERGEABLE);
private static Schema<ChangeData> release(Collection<FieldDef<ChangeData, ?>> fields) {
return new Schema<ChangeData>(true, fields);

View File

@ -82,6 +82,10 @@ public class Schema<T> {
return fields;
}
public final boolean hasField(FieldDef<T, ?> field) {
return fields.get(field.getName()) == field;
}
/**
* Build all fields in the schema from an input object.
* <p>

View File

@ -36,6 +36,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.TrackingFooters;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.strategy.SubmitStrategyFactory;
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.ChangeIndex;
import com.google.gerrit.server.index.IndexCollection;
import com.google.gerrit.server.index.Schema;
@ -106,6 +107,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
public static final String FIELD_PARENTPROJECT = "parentproject";
public static final String FIELD_PATH = "path";
public static final String FIELD_PROJECT = "project";
public static final String FIELD_PROJECTS = "projects";
public static final String FIELD_REF = "ref";
public static final String FIELD_REVIEWER = "reviewer";
public static final String FIELD_REVIEWERIN = "reviewerin";
@ -371,6 +373,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
return new ProjectPredicate(name);
}
@Operator
public Predicate<ChangeData> projects(String name) throws QueryParseException {
if (!schema(args.indexes).hasField(ChangeField.PROJECTS)) {
throw new QueryParseException("Unsupported operator: " + FIELD_PROJECTS);
}
return new ProjectPrefixPredicate(name);
}
@Operator
public Predicate<ChangeData> parentproject(String name) {
return new ParentProjectPredicate(args.db, args.projectCache,

View File

@ -0,0 +1,37 @@
// Copyright (C) 2014 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.server.query.change;
import com.google.gerrit.reviewdb.client.Change;
import com.google.gerrit.server.index.ChangeField;
import com.google.gerrit.server.index.IndexPredicate;
import com.google.gwtorm.server.OrmException;
class ProjectPrefixPredicate extends IndexPredicate<ChangeData> {
ProjectPrefixPredicate(String prefix) {
super(ChangeField.PROJECTS, prefix);
}
@Override
public boolean match(ChangeData object) throws OrmException {
Change c = object.change();
return c != null && c.getDest().getParentKey().get().startsWith(getValue());
}
@Override
public int getCost() {
return 1;
}
}

View File

@ -304,10 +304,29 @@ public abstract class AbstractQueryChangesTest {
Change change2 = newChange(repo2, null, null, null, null).insert();
assertTrue(query("project:foo").isEmpty());
assertTrue(query("project:repo").isEmpty());
assertResultEquals(change1, queryOne("project:repo1"));
assertResultEquals(change2, queryOne("project:repo2"));
}
@Test
public void byProjectPrefix() throws Exception {
TestRepository<InMemoryRepository> repo1 = createProject("repo1");
TestRepository<InMemoryRepository> repo2 = createProject("repo2");
Change change1 = newChange(repo1, null, null, null, null).insert();
Change change2 = newChange(repo2, null, null, null, null).insert();
assertTrue(query("projects:foo").isEmpty());
assertResultEquals(change1, queryOne("projects:repo1"));
assertResultEquals(change2, queryOne("projects:repo2"));
List<ChangeInfo> results;
results = query("projects:repo");
assertEquals(results.toString(), 2, results.size());
assertResultEquals(change2, results.get(0));
assertResultEquals(change1, results.get(1));
}
@Test
public void byBranchAndRef() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");

View File

@ -32,6 +32,7 @@ import com.google.inject.Injector;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.junit.Ignore;
import org.junit.Test;
import java.util.List;
@ -43,6 +44,13 @@ public class LuceneQueryChangesV7Test extends AbstractQueryChangesTest {
return Guice.createInjector(new InMemoryModule(cfg));
}
// Tests for features not supported in V7.
@Ignore
@Override
@Test
public void byProjectPrefix() {}
// End tests for features not supported in V7.
@Test
public void pagination() throws Exception {
TestRepository<InMemoryRepository> repo = createProject("repo");

View File

@ -51,6 +51,7 @@ import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class DocIndexer {
@SuppressWarnings("deprecation")
private static final Version LUCENE_VERSION = Version.LUCENE_46;
private static final Pattern SECTION_HEADER = Pattern.compile("^=+ (.*)");

View File

@ -1,12 +1,11 @@
include_defs('//lib/maven.defs')
VERSION = '4.6.0'
VERSION = '4.7.0'
maven_jar(
name = 'core',
id = 'org.apache.lucene:lucene-core:' + VERSION,
bin_sha1 = 'f1d974facaea30a3a0c1752a24097af5a7d40e60',
src_sha1 = '19d4eb5def4bc2517a00b50f7a875b7ce33988a7',
sha1 = '12d2b92d15158ac0d7b2864f537403acb4d7f69e',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@ -17,8 +16,7 @@ maven_jar(
maven_jar(
name = 'analyzers-common',
id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
bin_sha1 = '25dda6706bcb7a741f25f57cdbec6c2f36adc557',
src_sha1 = '04866d0e36e3ef708d099014752ad4fef61d4243',
sha1 = '399fa6b0d750c8e5c9e4ae73e6407c8b3ed4e8c1',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@ -29,7 +27,6 @@ maven_jar(
maven_jar(
name = 'query-parser',
id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
bin_sha1 = 'ef35f1eb55e50725777162e376e7b5222f45f7fa',
src_sha1 = 'e99e3b298e83461c03ef6eb66ab9798a4b712dc6',
sha1 = 'f78a804de1582c511224d214c2d9c82ce48379e7',
license = 'Apache2.0',
)