Merge changes from topic 'lucene5'
* changes:
Update Lucene to 5.0.0
Add config option to disable online reindexing
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index c93a01b..9fd0db2 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -2176,6 +2176,18 @@
thread pool as interactive operations (unless
link:#changeMerge.threadPoolSize[changeMerge.threadPoolSize] is set).
+[[index.onlineUpgrade]]index.onlineUpgrade::
++
+Whether to upgrade to new index schema versions while the server is
+running. This is recommended as it prevents additional downtime during
+Gerrit version upgrades (avoiding the need for an offline reindex step
+using Reindex), but can add additional server load during the upgrade.
++
+If set to false, there is no way to upgrade the index schema to take
+advantage of new search features without restarting the server.
++
+Defaults to true.
+
==== Lucene configuration
Open and closed changes are indexed in separate indexes named
diff --git a/gerrit-lucene/BUCK b/gerrit-lucene/BUCK
index 2b45d2b..a146774 100644
--- a/gerrit-lucene/BUCK
+++ b/gerrit-lucene/BUCK
@@ -34,7 +34,9 @@
'//lib/jgit:jgit',
'//lib/log:api',
'//lib/lucene:analyzers-common',
+ '//lib/lucene:backward-codecs',
'//lib/lucene:core',
+ '//lib/lucene:misc',
],
visibility = ['PUBLIC'],
)
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java
index e0c13ae..27ded17 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/AutoCommitWriter.java
@@ -14,7 +14,6 @@
package com.google.gerrit.lucene;
-import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
@@ -43,13 +42,6 @@
}
@Override
- public void addDocument(Iterable<? extends IndexableField> doc,
- Analyzer analyzer) throws IOException {
- super.addDocument(doc, analyzer);
- autoFlush();
- }
-
- @Override
public void addDocuments(
Iterable<? extends Iterable<? extends IndexableField>> docs)
throws IOException {
@@ -58,14 +50,6 @@
}
@Override
- public void addDocuments(
- Iterable<? extends Iterable<? extends IndexableField>> docs,
- Analyzer analyzer) throws IOException {
- super.addDocuments(docs, analyzer);
- autoFlush();
- }
-
- @Override
public void updateDocuments(Term delTerm,
Iterable<? extends Iterable<? extends IndexableField>> docs)
throws IOException {
@@ -74,14 +58,6 @@
}
@Override
- public void updateDocuments(Term delTerm,
- Iterable<? extends Iterable<? extends IndexableField>> docs,
- Analyzer analyzer) throws IOException {
- super.updateDocuments(delTerm, docs, analyzer);
- autoFlush();
- }
-
- @Override
public void deleteDocuments(Term... term) throws IOException {
super.deleteDocuments(term);
autoFlush();
@@ -111,13 +87,6 @@
}
@Override
- public void updateDocument(Term term, Iterable<? extends IndexableField> doc,
- Analyzer analyzer) throws IOException {
- super.updateDocument(term, doc, analyzer);
- autoFlush();
- }
-
- @Override
public void deleteAll() throws IOException {
super.deleteAll();
autoFlush();
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index 0084536..15b8fc3 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -14,10 +14,12 @@
package com.google.gerrit.lucene;
-import static com.google.common.base.Preconditions.checkNotNull;
+import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
import static com.google.gerrit.server.git.QueueProvider.QueueType.INTERACTIVE;
import static com.google.gerrit.server.index.IndexRewriteImpl.CLOSED_STATUSES;
import static com.google.gerrit.server.index.IndexRewriteImpl.OPEN_STATUSES;
+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.MINUTES;
@@ -39,7 +41,6 @@
import com.google.gerrit.server.index.ChangeField.ChangeProtoField;
import com.google.gerrit.server.index.ChangeField.PatchSetApprovalProtoField;
import com.google.gerrit.server.index.ChangeIndex;
-import com.google.gerrit.server.index.ChangeSchemas;
import com.google.gerrit.server.index.FieldDef;
import com.google.gerrit.server.index.FieldDef.FillArgs;
import com.google.gerrit.server.index.FieldType;
@@ -64,9 +65,12 @@
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.LongField;
+import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
@@ -76,13 +80,14 @@
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
+import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.uninverting.UninvertingReader;
import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.Version;
import org.eclipse.jgit.errors.ConfigInvalidException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -120,54 +125,19 @@
private static final String CHANGE_FIELD = ChangeField.CHANGE.getName();
private static final String DELETED_FIELD = ChangeField.DELETED.getName();
private static final String ID_FIELD = ChangeField.LEGACY_ID.getName();
+ private static final String ID_SORT_FIELD =
+ sortFieldName(ChangeField.LEGACY_ID);
private static final String MERGEABLE_FIELD = ChangeField.MERGEABLE.getName();
+ private static final String UPDATED_SORT_FIELD =
+ sortFieldName(ChangeField.UPDATED);
+
private static final ImmutableSet<String> FIELDS = ImmutableSet.of(
ADDED_FIELD, APPROVAL_FIELD, CHANGE_FIELD, DELETED_FIELD, ID_FIELD,
MERGEABLE_FIELD);
+
private static final Map<String, String> CUSTOM_CHAR_MAPPING = ImmutableMap.of(
"_", " ", ".", " ");
- private static final Map<Schema<ChangeData>, Version> LUCENE_VERSIONS;
- static {
- ImmutableMap.Builder<Schema<ChangeData>, Version> versions =
- ImmutableMap.builder();
- @SuppressWarnings("deprecation")
- Version lucene43 = Version.LUCENE_43;
- @SuppressWarnings("deprecation")
- Version lucene44 = Version.LUCENE_44;
- @SuppressWarnings("deprecation")
- Version lucene46 = Version.LUCENE_46;
- @SuppressWarnings("deprecation")
- Version lucene47 = Version.LUCENE_47;
- @SuppressWarnings("deprecation")
- Version lucene48 = Version.LUCENE_48;
- @SuppressWarnings("deprecation")
- Version lucene410 = Version.LUCENE_4_10_0;
- // We are using 4.10.2 but there is no difference in the index
- // format since 4.10.1, so we reuse the version here.
- @SuppressWarnings("deprecation")
- Version lucene4101 = Version.LUCENE_4_10_1;
- 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 if (e.getKey() <= 10) {
- versions.put(e.getValue(), lucene47);
- } else if (e.getKey() <= 11) {
- versions.put(e.getValue(), lucene48);
- } else if (e.getKey() <= 13) {
- versions.put(e.getValue(), lucene410);
- } else {
- versions.put(e.getValue(), lucene4101);
- }
- }
- LUCENE_VERSIONS = versions.build();
- }
-
public static void setReady(SitePaths sitePaths, int version, boolean ready)
throws IOException {
try {
@@ -180,6 +150,10 @@
}
}
+ private static String sortFieldName(FieldDef<?, ?> f) {
+ return f.getName() + "_SORT";
+ }
+
static interface Factory {
LuceneChangeIndex create(Schema<ChangeData> schema, String base);
}
@@ -188,12 +162,13 @@
private final IndexWriterConfig luceneConfig;
private long commitWithinMs;
- private GerritIndexWriterConfig(Version version, Config cfg, String name) {
+ private GerritIndexWriterConfig(Config cfg, String name) {
CustomMappingAnalyzer analyzer =
new CustomMappingAnalyzer(new StandardAnalyzer(
CharArraySet.EMPTY_SET), CUSTOM_CHAR_MAPPING);
- luceneConfig = new IndexWriterConfig(version, analyzer);
- luceneConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
+ luceneConfig = new IndexWriterConfig(analyzer)
+ .setOpenMode(OpenMode.CREATE_OR_APPEND)
+ .setCommitOnClose(true);
double m = 1 << 20;
luceneConfig.setRAMBufferSizeMB(cfg.getLong(
"index", name, "ramBufferSize",
@@ -229,6 +204,17 @@
private final SubIndex openIndex;
private final SubIndex closedIndex;
+ /**
+ * Whether to use DocValues for range/sorted numeric fields.
+ * <p>
+ * Lucene 5 removed support for sorting based on normal numeric fields, so we
+ * use the newer API for more strongly typed numeric fields in newer schema
+ * versions. These fields also are not stored, so we need to store auxiliary
+ * stored-only field for them as well.
+ */
+ // TODO(dborowitz): Delete when we delete support for pre-Lucene-5.0 schemas.
+ private final boolean useDocValuesForSorting;
+
@AssistedInject
LuceneChangeIndex(
@GerritServerConfig Config cfg,
@@ -245,10 +231,8 @@
this.db = db;
this.changeDataFactory = changeDataFactory;
this.schema = schema;
+ this.useDocValuesForSorting = schema.getVersion() >= 15;
- Version luceneVersion = checkNotNull(
- LUCENE_VERSIONS.get(schema),
- "unknown Lucene version for index schema: %s", schema);
CustomMappingAnalyzer analyzer =
new CustomMappingAnalyzer(new StandardAnalyzer(CharArraySet.EMPTY_SET),
CUSTOM_CHAR_MAPPING);
@@ -258,21 +242,44 @@
BooleanQuery.getMaxClauseCount()));
GerritIndexWriterConfig openConfig =
- new GerritIndexWriterConfig(luceneVersion, cfg, "changes_open");
+ new GerritIndexWriterConfig(cfg, "changes_open");
GerritIndexWriterConfig closedConfig =
- new GerritIndexWriterConfig(luceneVersion, cfg, "changes_closed");
+ new GerritIndexWriterConfig(cfg, "changes_closed");
+ SearcherFactory searcherFactory = newSearcherFactory();
if (cfg.getBoolean("index", "lucene", "testInmemory", false)) {
- openIndex = new SubIndex(new RAMDirectory(), "ramOpen", openConfig);
- closedIndex = new SubIndex(new RAMDirectory(), "ramClosed", closedConfig);
+ openIndex = new SubIndex(new RAMDirectory(), "ramOpen", openConfig,
+ searcherFactory);
+ closedIndex = new SubIndex(new RAMDirectory(), "ramClosed", closedConfig,
+ searcherFactory);
} else {
Path dir = base != null ? Paths.get(base)
: LuceneVersionManager.getDir(sitePaths, schema);
- openIndex = new SubIndex(dir.resolve(CHANGES_OPEN), openConfig);
- closedIndex = new SubIndex(dir.resolve(CHANGES_CLOSED), closedConfig);
+ openIndex = new SubIndex(dir.resolve(CHANGES_OPEN), openConfig,
+ searcherFactory);
+ closedIndex = new SubIndex(dir.resolve(CHANGES_CLOSED), closedConfig,
+ searcherFactory);
}
}
+ private SearcherFactory newSearcherFactory() {
+ if (useDocValuesForSorting) {
+ return new SearcherFactory();
+ }
+ final Map<String, UninvertingReader.Type> mapping = ImmutableMap.of(
+ ChangeField.LEGACY_ID.getName(), UninvertingReader.Type.INTEGER,
+ ChangeField.UPDATED.getName(), UninvertingReader.Type.LONG);
+ return new SearcherFactory() {
+ @Override
+ public IndexSearcher newSearcher(IndexReader reader) {
+ checkState(reader instanceof DirectoryReader,
+ "expected DirectoryReader, found %s", reader.getClass().getName());
+ return new IndexSearcher(
+ UninvertingReader.wrap((DirectoryReader) reader, mapping));
+ }
+ };
+ }
+
@Override
public void close() {
List<ListenableFuture<?>> closeFutures = Lists.newArrayListWithCapacity(2);
@@ -355,12 +362,18 @@
setReady(sitePaths, schema.getVersion(), ready);
}
- private static Sort getSort() {
- return new Sort(
- new SortField(
- ChangeField.UPDATED.getName(), SortField.Type.LONG, true),
- new SortField(
- ChangeField.LEGACY_ID.getName(), SortField.Type.INT, true));
+ private Sort getSort() {
+ if (useDocValuesForSorting) {
+ return new Sort(
+ new SortField(UPDATED_SORT_FIELD, SortField.Type.LONG, true),
+ new SortField(ID_SORT_FIELD, SortField.Type.LONG, true));
+ } else {
+ return new Sort(
+ new SortField(
+ ChangeField.UPDATED.getName(), SortField.Type.LONG, true),
+ new SortField(
+ ChangeField.LEGACY_ID.getName(), SortField.Type.INT, true));
+ }
}
private class QuerySource implements ChangeDataSource {
@@ -506,6 +519,16 @@
FieldType<?> type = values.getField().getType();
Store store = store(values.getField());
+ if (useDocValuesForSorting) {
+ if (values.getField() == ChangeField.LEGACY_ID) {
+ int v = (Integer) getOnlyElement(values.getValues());
+ doc.add(new NumericDocValuesField(ID_SORT_FIELD, v));
+ } else if (values.getField() == ChangeField.UPDATED) {
+ long t = ((Timestamp) getOnlyElement(values.getValues())).getTime();
+ doc.add(new NumericDocValuesField(UPDATED_SORT_FIELD, t));
+ }
+ }
+
if (type == FieldType.INTEGER || type == FieldType.INTEGER_RANGE) {
for (Object value : values.getValues()) {
doc.add(new IntField(name, (Integer) value, store));
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
index 3c38225..109525a 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneVersionManager.java
@@ -20,6 +20,7 @@
import com.google.common.collect.Maps;
import com.google.common.primitives.Ints;
import com.google.gerrit.extensions.events.LifecycleListener;
+import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.index.ChangeSchemas;
import com.google.gerrit.server.index.IndexCollection;
@@ -93,9 +94,11 @@
private final LuceneChangeIndex.Factory indexFactory;
private final IndexCollection indexes;
private final OnlineReindexer.Factory reindexerFactory;
+ private final boolean onlineUpgrade;
@Inject
LuceneVersionManager(
+ @GerritServerConfig Config cfg,
SitePaths sitePaths,
LuceneChangeIndex.Factory indexFactory,
IndexCollection indexes,
@@ -104,6 +107,7 @@
this.indexFactory = indexFactory;
this.indexes = indexes;
this.reindexerFactory = reindexerFactory;
+ this.onlineUpgrade = cfg.getBoolean("index", null, "onlineUpgrade", true);
}
@Override
@@ -133,7 +137,7 @@
if (v.schema == null) {
continue;
}
- if (write.isEmpty()) {
+ if (write.isEmpty() && onlineUpgrade) {
write.add(v);
}
if (v.ready) {
@@ -162,7 +166,7 @@
}
int latest = write.get(0).version;
- if (latest != search.version) {
+ if (onlineUpgrade && latest != search.version) {
reindexerFactory.create(latest).start();
}
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
index f28bf05..5778008 100644
--- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/SubIndex.java
@@ -28,9 +28,9 @@
import org.apache.lucene.index.TrackingIndexWriter;
import org.apache.lucene.search.ControlledRealTimeReopenThread;
import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.ReferenceManager;
import org.apache.lucene.search.ReferenceManager.RefreshListener;
import org.apache.lucene.search.SearcherFactory;
-import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.store.AlreadyClosedException;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
@@ -52,17 +52,19 @@
private final Directory dir;
private final TrackingIndexWriter writer;
- private final SearcherManager searcherManager;
+ private final ReferenceManager<IndexSearcher> searcherManager;
private final ControlledRealTimeReopenThread<IndexSearcher> reopenThread;
private final Set<NrtFuture> notDoneNrtFutures;
- SubIndex(Path path, GerritIndexWriterConfig writerConfig) throws IOException {
- this(FSDirectory.open(path.toFile()), path.getFileName().toString(),
- writerConfig);
+ SubIndex(Path path, GerritIndexWriterConfig writerConfig,
+ SearcherFactory searcherFactory) throws IOException {
+ this(FSDirectory.open(path), path.getFileName().toString(), writerConfig,
+ searcherFactory);
}
SubIndex(Directory dir, final String dirName,
- GerritIndexWriterConfig writerConfig) throws IOException {
+ GerritIndexWriterConfig writerConfig,
+ SearcherFactory searcherFactory) throws IOException {
this.dir = dir;
IndexWriter delegateWriter;
long commitPeriod = writerConfig.getCommitWithinMs();
@@ -104,8 +106,8 @@
}, commitPeriod, commitPeriod, MILLISECONDS);
}
writer = new TrackingIndexWriter(delegateWriter);
- searcherManager = new SearcherManager(
- writer.getIndexWriter(), true, new SearcherFactory());
+ searcherManager = new WrappableSearcherManager(
+ writer.getIndexWriter(), true, searcherFactory);
notDoneNrtFutures = Sets.newConcurrentHashSet();
@@ -125,6 +127,8 @@
// searching generation being up to date when calling
// reopenThread.waitForGeneration(gen, 0), therefore the reopen thread's
// internal listener needs to be called first.
+ // TODO(dborowitz): This may have been fixed by
+ // http://issues.apache.org/jira/browse/LUCENE-5461
searcherManager.addListener(new RefreshListener() {
@Override
public void beforeRefresh() throws IOException {
@@ -158,12 +162,9 @@
}
try {
- writer.getIndexWriter().commit();
- try {
- writer.getIndexWriter().close();
- } catch (AlreadyClosedException e) {
- // Ignore.
- }
+ writer.getIndexWriter().close();
+ } catch (AlreadyClosedException e) {
+ // Ignore.
} catch (IOException e) {
log.warn("error closing Lucene writer", e);
}
diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java
new file mode 100644
index 0000000..fe45f1d
--- /dev/null
+++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/WrappableSearcherManager.java
@@ -0,0 +1,217 @@
+package com.google.gerrit.lucene;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+
+import java.io.IOException;
+
+import org.apache.lucene.index.DirectoryReader;
+import org.apache.lucene.index.FilterDirectoryReader;
+import org.apache.lucene.index.FilterLeafReader;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.ReferenceManager;
+import org.apache.lucene.search.SearcherFactory;
+import org.apache.lucene.store.Directory;
+
+/**
+ * Utility class to safely share {@link IndexSearcher} instances across multiple
+ * threads, while periodically reopening. This class ensures each searcher is
+ * closed only once all threads have finished using it.
+ *
+ * <p>
+ * Use {@link #acquire} to obtain the current searcher, and {@link #release} to
+ * release it, like this:
+ *
+ * <pre class="prettyprint">
+ * IndexSearcher s = manager.acquire();
+ * try {
+ * // Do searching, doc retrieval, etc. with s
+ * } finally {
+ * manager.release(s);
+ * }
+ * // Do not use s after this!
+ * s = null;
+ * </pre>
+ *
+ * <p>
+ * In addition you should periodically call {@link #maybeRefresh}. While it's
+ * possible to call this just before running each query, this is discouraged
+ * since it penalizes the unlucky queries that need to refresh. It's better to use
+ * a separate background thread, that periodically calls {@link #maybeRefresh}. Finally,
+ * be sure to call {@link #close} once you are done.
+ *
+ * @see SearcherFactory
+ *
+ * @lucene.experimental
+ */
+// This file was copied from:
+// https://github.com/apache/lucene-solr/blob/lucene_solr_5_0/lucene/core/src/java/org/apache/lucene/search/SearcherManager.java
+// The only change (other than class name and import fixes)
+// is to skip the check in getSearcher that searcherFactory.newSearcher wraps
+// the provided searcher exactly.
+final class WrappableSearcherManager extends ReferenceManager<IndexSearcher> {
+
+ private final SearcherFactory searcherFactory;
+
+ /**
+ * Creates and returns a new SearcherManager from the given
+ * {@link IndexWriter}.
+ *
+ * @param writer
+ * the IndexWriter to open the IndexReader from.
+ * @param applyAllDeletes
+ * If <code>true</code>, all buffered deletes will be applied (made
+ * visible) in the {@link IndexSearcher} / {@link DirectoryReader}.
+ * If <code>false</code>, the deletes may or may not be applied, but
+ * remain buffered (in IndexWriter) so that they will be applied in
+ * the future. Applying deletes can be costly, so if your app can
+ * tolerate deleted documents being returned you might gain some
+ * performance by passing <code>false</code>. See
+ * {@link DirectoryReader#openIfChanged(DirectoryReader, IndexWriter, boolean)}.
+ * @param searcherFactory
+ * An optional {@link SearcherFactory}. Pass <code>null</code> if you
+ * don't require the searcher to be warmed before going live or other
+ * custom behavior.
+ *
+ * @throws IOException if there is a low-level I/O error
+ */
+ public WrappableSearcherManager(IndexWriter writer, boolean applyAllDeletes, SearcherFactory searcherFactory) throws IOException {
+ if (searcherFactory == null) {
+ searcherFactory = new SearcherFactory();
+ }
+ this.searcherFactory = searcherFactory;
+ current = getSearcher(searcherFactory, DirectoryReader.open(writer, applyAllDeletes));
+ }
+
+ /**
+ * Creates and returns a new SearcherManager from the given {@link Directory}.
+ * @param dir the directory to open the DirectoryReader on.
+ * @param searcherFactory An optional {@link SearcherFactory}. Pass
+ * <code>null</code> if you don't require the searcher to be warmed
+ * before going live or other custom behavior.
+ *
+ * @throws IOException if there is a low-level I/O error
+ */
+ public WrappableSearcherManager(Directory dir, SearcherFactory searcherFactory) throws IOException {
+ if (searcherFactory == null) {
+ searcherFactory = new SearcherFactory();
+ }
+ this.searcherFactory = searcherFactory;
+ current = getSearcher(searcherFactory, DirectoryReader.open(dir));
+ }
+
+ /**
+ * Creates and returns a new SearcherManager from an existing {@link DirectoryReader}. Note that
+ * this steals the incoming reference.
+ *
+ * @param reader the DirectoryReader.
+ * @param searcherFactory An optional {@link SearcherFactory}. Pass
+ * <code>null</code> if you don't require the searcher to be warmed
+ * before going live or other custom behavior.
+ *
+ * @throws IOException if there is a low-level I/O error
+ */
+ public WrappableSearcherManager(DirectoryReader reader, SearcherFactory searcherFactory) throws IOException {
+ if (searcherFactory == null) {
+ searcherFactory = new SearcherFactory();
+ }
+ this.searcherFactory = searcherFactory;
+ this.current = getSearcher(searcherFactory, reader);
+ }
+
+ @Override
+ protected void decRef(IndexSearcher reference) throws IOException {
+ reference.getIndexReader().decRef();
+ }
+
+ @Override
+ protected IndexSearcher refreshIfNeeded(IndexSearcher referenceToRefresh) throws IOException {
+ final IndexReader r = referenceToRefresh.getIndexReader();
+ assert r instanceof DirectoryReader: "searcher's IndexReader should be a DirectoryReader, but got " + r;
+ final IndexReader newReader = DirectoryReader.openIfChanged((DirectoryReader) r);
+ if (newReader == null) {
+ return null;
+ } else {
+ return getSearcher(searcherFactory, newReader);
+ }
+ }
+
+ @Override
+ protected boolean tryIncRef(IndexSearcher reference) {
+ return reference.getIndexReader().tryIncRef();
+ }
+
+ @Override
+ protected int getRefCount(IndexSearcher reference) {
+ return reference.getIndexReader().getRefCount();
+ }
+
+ /**
+ * Returns <code>true</code> if no changes have occured since this searcher
+ * ie. reader was opened, otherwise <code>false</code>.
+ * @see DirectoryReader#isCurrent()
+ */
+ public boolean isSearcherCurrent() throws IOException {
+ final IndexSearcher searcher = acquire();
+ try {
+ final IndexReader r = searcher.getIndexReader();
+ assert r instanceof DirectoryReader: "searcher's IndexReader should be a DirectoryReader, but got " + r;
+ return ((DirectoryReader) r).isCurrent();
+ } finally {
+ release(searcher);
+ }
+ }
+
+ /** Expert: creates a searcher from the provided {@link
+ * IndexReader} using the provided {@link
+ * SearcherFactory}. NOTE: this decRefs incoming reader
+ * on throwing an exception. */
+ @SuppressWarnings("resource")
+ public static IndexSearcher getSearcher(SearcherFactory searcherFactory, IndexReader reader) throws IOException {
+ boolean success = false;
+ final IndexSearcher searcher;
+ try {
+ searcher = searcherFactory.newSearcher(reader);
+ // Modification for Gerrit: Allow searcherFactory to transitively wrap the
+ // provided reader.
+ IndexReader unwrapped = searcher.getIndexReader();
+ while (true) {
+ if (unwrapped == reader) {
+ break;
+ } else if (unwrapped instanceof FilterDirectoryReader) {
+ unwrapped = ((FilterDirectoryReader) unwrapped).getDelegate();
+ } else if (unwrapped instanceof FilterLeafReader) {
+ unwrapped = ((FilterLeafReader) unwrapped).getDelegate();
+ } else {
+ break;
+ }
+ }
+
+ if (unwrapped != reader) {
+ throw new IllegalStateException("SearcherFactory must wrap the provided reader (got " + searcher.getIndexReader() + " but expected " + reader + ")");
+ }
+ success = true;
+ } finally {
+ if (!success) {
+ reader.decRef();
+ }
+ }
+ return searcher;
+ }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
index 821343f..0040762 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/ChangeField.java
@@ -55,6 +55,10 @@
* {@link ChangeQueryBuilder} for querying that field, and a method on
* {@link ChangeData} used for populating the corresponding document fields in
* the secondary index.
+ * <p>
+ * Field names are all lowercase alphanumeric plus underscore; index
+ * implementations may create unambiguous derived field names containing other
+ * characters.
*/
public class ChangeField {
/** Legacy change ID. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
index 557faeb..cf3fd09 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/FieldDef.java
@@ -14,6 +14,9 @@
package com.google.gerrit.server.index;
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.base.CharMatcher;
import com.google.common.base.Preconditions;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.config.TrackingFooters;
@@ -78,11 +81,17 @@
private final boolean stored;
private FieldDef(String name, FieldType<?> type, boolean stored) {
- this.name = name;
+ this.name = checkName(name);
this.type = type;
this.stored = stored;
}
+ private static String checkName(String name) {
+ CharMatcher m = CharMatcher.anyOf("abcdefghijklmnopqrstuvwxyz0123456789_");
+ checkArgument(m.matchesAllOf(name), "illegal field name: %s", name);
+ return name;
+ }
+
/** @return name of the field. */
public final String getName() {
return name;
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java
new file mode 100644
index 0000000..42f5072
--- /dev/null
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/query/change/LuceneQueryChangesV14Test.java
@@ -0,0 +1,49 @@
+// Copyright (C) 2015 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.testutil.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+import org.eclipse.jgit.lib.Config;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class LuceneQueryChangesV14Test extends LuceneQueryChangesTest {
+
+ @Override
+ protected Injector createInjector() {
+ Config luceneConfig = new Config(config);
+ InMemoryModule.setDefaults(luceneConfig);
+ // Latest version with a Lucene 4 index.
+ luceneConfig.setInt("index", "lucene", "testVersion", 14);
+ return Guice.createInjector(new InMemoryModule(luceneConfig));
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void byCommentBy() {
+ // Ignore.
+ }
+
+ @Override
+ @Ignore
+ @Test
+ public void byFrom() {
+ // Ignore.
+ }
+}
diff --git a/lib/asciidoctor/java/DocIndexer.java b/lib/asciidoctor/java/DocIndexer.java
index 7eb70c1..f06c662 100644
--- a/lib/asciidoctor/java/DocIndexer.java
+++ b/lib/asciidoctor/java/DocIndexer.java
@@ -25,7 +25,6 @@
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.store.IndexInput;
import org.apache.lucene.store.RAMDirectory;
-import org.apache.lucene.util.Version;
import org.kohsuke.args4j.Argument;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
@@ -51,8 +50,6 @@
import java.util.zip.ZipOutputStream;
public class DocIndexer {
- @SuppressWarnings("deprecation")
- private static final Version LUCENE_VERSION = Version.LUCENE_4_10_1;
private static final Pattern SECTION_HEADER = Pattern.compile("^=+ (.*)");
@Option(name = "-o", usage = "output JAR file")
@@ -99,9 +96,9 @@
UnsupportedEncodingException, FileNotFoundException {
RAMDirectory directory = new RAMDirectory();
IndexWriterConfig config = new IndexWriterConfig(
- LUCENE_VERSION,
new StandardAnalyzer(CharArraySet.EMPTY_SET));
config.setOpenMode(OpenMode.CREATE);
+ config.setCommitOnClose(true);
IndexWriter iwriter = new IndexWriter(directory, config);
for (String inputFile : inputFiles) {
File file = new File(inputFile);
diff --git a/lib/lucene/BUCK b/lib/lucene/BUCK
index 9026f79..275f0bb 100644
--- a/lib/lucene/BUCK
+++ b/lib/lucene/BUCK
@@ -1,11 +1,11 @@
include_defs('//lib/maven.defs')
-VERSION = '4.10.2'
+VERSION = '5.0.0'
maven_jar(
name = 'core',
id = 'org.apache.lucene:lucene-core:' + VERSION,
- sha1 = 'c01e3d675d277e0a93e7890d03cc3246b2cdecaa',
+ sha1 = '4395e5ea987af804c4a9b96131e2ee75db061fdf',
license = 'Apache2.0',
exclude = [
'META-INF/LICENSE.txt',
@@ -16,8 +16,33 @@
maven_jar(
name = 'analyzers-common',
id = 'org.apache.lucene:lucene-analyzers-common:' + VERSION,
- sha1 = 'f977f8c443e8f4e9d1fd7fdfda80a6cf60b3e7c2',
+ sha1 = '6159cbc5c9631ef75e1f0e97b358ecdd8f1447a9',
license = 'Apache2.0',
+ deps = [':core'],
+ exclude = [
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE.txt',
+ ],
+)
+
+maven_jar(
+ name = 'backward-codecs',
+ id = 'org.apache.lucene:lucene-backward-codecs:' + VERSION,
+ sha1 = '5cd11fc1be436ff96b63f0f76f299a9d25543b0b',
+ license = 'Apache2.0',
+ deps = [':core'],
+ exclude = [
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE.txt',
+ ],
+)
+
+maven_jar(
+ name = 'misc',
+ id = 'org.apache.lucene:lucene-misc:' + VERSION,
+ sha1 = '06bd7cb030e598da81a8228f5c58630e5ce7b84a',
+ license = 'Apache2.0',
+ deps = [':core'],
exclude = [
'META-INF/LICENSE.txt',
'META-INF/NOTICE.txt',
@@ -27,6 +52,11 @@
maven_jar(
name = 'query-parser',
id = 'org.apache.lucene:lucene-queryparser:' + VERSION,
- sha1 = 'd70f54e1060d553ba7aeb4d49a71fd0c068499e8',
+ sha1 = 'f459326c0b58bb837612bfeb37f6015c1a8962db',
license = 'Apache2.0',
+ deps = [':core'],
+ exclude = [
+ 'META-INF/LICENSE.txt',
+ 'META-INF/NOTICE.txt',
+ ],
)