Skip to content

Commit b877fb9

Browse files
authored
Merge pull request #5628 from apache/lucene-10
nouveau: Upgrade to Lucene 10
2 parents 88fcdce + 0366623 commit b877fb9

37 files changed

Lines changed: 648 additions & 94 deletions

extra/nouveau/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
Nouveau is a modern replacement for dreyfus/clouseau and is built on;
44

55
1) the Dropwizard framework (https://dropwizard.io)
6-
2) Java 11+
7-
3) Lucene 9
6+
2) Java 21+
7+
3) Lucene 10
88

99
Nouveau transforms Apache CouchDB databases into Apache Lucene indexes at the shard level and then merges the results together.
1010

@@ -23,7 +23,7 @@ This work is currently EXPERIMENTAL and may change in ways that invalidate any e
2323
* integration with resharding
2424
* update=false
2525
* `_nouveau_info`
26-
* `_search_cleanup`
26+
* `_nouveau_cleanup`
2727
* /openapi.{json.yaml}
2828

2929
## What doesn't work yet?

extra/nouveau/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ dependencies {
2323
implementation 'io.dropwizard.metrics:metrics-jersey2'
2424
testImplementation 'io.dropwizard:dropwizard-testing'
2525

26-
def luceneVersion = '9.12.1'
26+
def luceneVersion = '10.3.2'
2727
implementation 'org.apache.lucene:lucene-core:' + luceneVersion
2828
implementation 'org.apache.lucene:lucene-queryparser:' + luceneVersion
2929
implementation 'org.apache.lucene:lucene-analysis-common:' + luceneVersion
@@ -46,7 +46,7 @@ group = 'org.apache.couchdb'
4646
version = '1.0-SNAPSHOT'
4747

4848
java {
49-
sourceCompatibility = "11"
49+
sourceCompatibility = "21"
5050
}
5151

5252
jar {

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,11 @@
2424
import org.apache.couchdb.nouveau.core.UserAgentFilter;
2525
import org.apache.couchdb.nouveau.health.AnalyzeHealthCheck;
2626
import org.apache.couchdb.nouveau.health.IndexHealthCheck;
27-
import org.apache.couchdb.nouveau.lucene9.Lucene9Module;
28-
import org.apache.couchdb.nouveau.lucene9.ParallelSearcherFactory;
27+
import org.apache.couchdb.nouveau.lucene.LuceneModule;
28+
import org.apache.couchdb.nouveau.lucene.ParallelSearcherFactory;
2929
import org.apache.couchdb.nouveau.resources.AnalyzeResource;
3030
import org.apache.couchdb.nouveau.resources.IndexResource;
31+
import org.apache.couchdb.nouveau.resources.WelcomeResource;
3132
import org.apache.couchdb.nouveau.tasks.CloseAllIndexesTask;
3233

3334
public class NouveauApplication extends Application<NouveauApplicationConfiguration> {
@@ -65,7 +66,11 @@ public void run(NouveauApplicationConfiguration configuration, Environment envir
6566
environment.lifecycle().manage(indexManager);
6667

6768
// Serialization classes
68-
environment.getObjectMapper().registerModule(new Lucene9Module());
69+
environment.getObjectMapper().registerModule(new LuceneModule());
70+
71+
// WelcomeResource
72+
final WelcomeResource welcomeResource = new WelcomeResource();
73+
environment.jersey().register(welcomeResource);
6974

7075
// AnalyzeResource
7176
final AnalyzeResource analyzeResource = new AnalyzeResource();

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,25 @@
1313

1414
package org.apache.couchdb.nouveau.api;
1515

16+
import com.fasterxml.jackson.annotation.JsonIgnore;
1617
import com.fasterxml.jackson.annotation.JsonProperty;
1718
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
1819
import com.fasterxml.jackson.databind.annotation.JsonNaming;
20+
import jakarta.validation.constraints.Max;
21+
import jakarta.validation.constraints.Min;
1922
import jakarta.validation.constraints.NotEmpty;
2023
import java.util.Map;
2124

2225
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
2326
public class IndexDefinition {
2427

28+
public static final int LEGACY_LUCENE_VERSION = 9;
29+
public static final int LATEST_LUCENE_VERSION = 10;
30+
31+
@Min(LEGACY_LUCENE_VERSION)
32+
@Max(LATEST_LUCENE_VERSION)
33+
private int luceneVersion = LEGACY_LUCENE_VERSION; // Legacy version if not set.
34+
2535
@NotEmpty
2636
private String defaultAnalyzer;
2737

@@ -31,11 +41,27 @@ public IndexDefinition() {
3141
// Jackson deserialization
3242
}
3343

34-
public IndexDefinition(final String defaultAnalyzer, final Map<String, String> fieldAnalyzers) {
44+
public IndexDefinition(
45+
final int luceneVersion, final String defaultAnalyzer, final Map<String, String> fieldAnalyzers) {
46+
this.luceneVersion = luceneVersion;
3547
this.defaultAnalyzer = defaultAnalyzer;
3648
this.fieldAnalyzers = fieldAnalyzers;
3749
}
3850

51+
@JsonProperty
52+
public int getLuceneVersion() {
53+
return luceneVersion;
54+
}
55+
56+
@JsonIgnore
57+
public boolean isLatestVersion() {
58+
return luceneVersion == LATEST_LUCENE_VERSION;
59+
}
60+
61+
public void setLuceneVersion(int luceneVersion) {
62+
this.luceneVersion = luceneVersion;
63+
}
64+
3965
@JsonProperty
4066
public String getDefaultAnalyzer() {
4167
return defaultAnalyzer;
@@ -62,6 +88,7 @@ public boolean hasFieldAnalyzers() {
6288
public int hashCode() {
6389
final int prime = 31;
6490
int result = 1;
91+
result = prime * result + luceneVersion;
6592
result = prime * result + ((defaultAnalyzer == null) ? 0 : defaultAnalyzer.hashCode());
6693
result = prime * result + ((fieldAnalyzers == null) ? 0 : fieldAnalyzers.hashCode());
6794
return result;
@@ -73,6 +100,7 @@ public boolean equals(Object obj) {
73100
if (obj == null) return false;
74101
if (getClass() != obj.getClass()) return false;
75102
IndexDefinition other = (IndexDefinition) obj;
103+
if (luceneVersion != other.luceneVersion) return false;
76104
if (defaultAnalyzer == null) {
77105
if (other.defaultAnalyzer != null) return false;
78106
} else if (!defaultAnalyzer.equals(other.defaultAnalyzer)) return false;
@@ -84,6 +112,7 @@ public boolean equals(Object obj) {
84112

85113
@Override
86114
public String toString() {
87-
return "IndexDefinition [defaultAnalyzer=" + defaultAnalyzer + ", fieldAnalyzers=" + fieldAnalyzers + "]";
115+
return "IndexDefinition [luceneVersion=" + luceneVersion + ", defaultAnalyzer=" + defaultAnalyzer
116+
+ ", fieldAnalyzers=" + fieldAnalyzers + "]";
88117
}
89118
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//
2+
// Licensed under the Apache License, Version 2.0 (the "License");
3+
// you may not use this file except in compliance with the License.
4+
// You may obtain a copy of the License at
5+
//
6+
// http://www.apache.org/licenses/LICENSE-2.0
7+
//
8+
// Unless required by applicable law or agreed to in writing, software
9+
// distributed under the License is distributed on an "AS IS" BASIS,
10+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
// See the License for the specific language governing permissions and
12+
// limitations under the License.
13+
14+
package org.apache.couchdb.nouveau.api;
15+
16+
import com.fasterxml.jackson.annotation.JsonProperty;
17+
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
18+
import com.fasterxml.jackson.databind.annotation.JsonNaming;
19+
20+
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
21+
public final class WelcomeResponse {
22+
23+
public static final WelcomeResponse INSTANCE = new WelcomeResponse();
24+
25+
private final int[] supportedLuceneVersions =
26+
new int[] {IndexDefinition.LEGACY_LUCENE_VERSION, IndexDefinition.LATEST_LUCENE_VERSION};
27+
28+
private WelcomeResponse() {}
29+
30+
@JsonProperty
31+
public int[] getSupportedLuceneVersions() {
32+
return supportedLuceneVersions;
33+
}
34+
}

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,12 @@
3434
import java.util.concurrent.locks.Lock;
3535
import java.util.concurrent.locks.ReentrantReadWriteLock;
3636
import org.apache.couchdb.nouveau.api.IndexDefinition;
37-
import org.apache.couchdb.nouveau.lucene9.Lucene9AnalyzerFactory;
38-
import org.apache.couchdb.nouveau.lucene9.Lucene9Index;
37+
import org.apache.couchdb.nouveau.lucene.LuceneAnalyzerFactory;
38+
import org.apache.couchdb.nouveau.lucene.LuceneIndex;
3939
import org.apache.lucene.analysis.Analyzer;
4040
import org.apache.lucene.index.IndexWriter;
4141
import org.apache.lucene.index.IndexWriterConfig;
42+
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
4243
import org.apache.lucene.misc.store.DirectIODirectory;
4344
import org.apache.lucene.search.SearcherFactory;
4445
import org.apache.lucene.search.SearcherManager;
@@ -218,7 +219,6 @@ public void create(final String name, IndexDefinition indexDefinition) throws IO
218219
assertSame(indexDefinition, loadIndexDefinition(name));
219220
return;
220221
}
221-
222222
final Lock lock = this.createLock.writeLock(name);
223223
lock.lock();
224224
try {
@@ -392,15 +392,19 @@ private Index load(final String name) throws IOException {
392392
LOGGER.info("opening {}", name);
393393
final Path path = indexPath(name);
394394
final IndexDefinition indexDefinition = loadIndexDefinition(name);
395-
final Analyzer analyzer = Lucene9AnalyzerFactory.fromDefinition(indexDefinition);
396-
final Directory dir = new DirectIODirectory(FSDirectory.open(path.resolve("9")));
395+
final Analyzer analyzer = LuceneAnalyzerFactory.fromDefinition(indexDefinition);
396+
final int luceneVersion = indexDefinition.getLuceneVersion();
397+
final Directory dir = new DirectIODirectory(FSDirectory.open(path.resolve(Integer.toString(luceneVersion))));
397398
final IndexWriterConfig config = new IndexWriterConfig(analyzer);
399+
if (luceneVersion != IndexDefinition.LATEST_LUCENE_VERSION) {
400+
config.setOpenMode(OpenMode.APPEND);
401+
}
398402
config.setUseCompoundFile(false);
399403
final IndexWriter writer = new IndexWriter(dir, config);
400404
final long updateSeq = getSeq(writer, "update_seq");
401405
final long purgeSeq = getSeq(writer, "purge_seq");
402406
final SearcherManager searcherManager = new SearcherManager(writer, searcherFactory);
403-
return new Lucene9Index(analyzer, writer, updateSeq, purgeSeq, searcherManager);
407+
return new LuceneIndex(analyzer, writer, updateSeq, purgeSeq, searcherManager);
404408
}
405409

406410
private long getSeq(final IndexWriter writer, final String key) throws IOException {

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public IndexHealthCheck(final IndexResource indexResource) {
3232

3333
@Override
3434
protected Result check() throws Exception {
35-
final String name = "___test9";
35+
final String name = "___test";
3636
try {
3737
indexResource.deletePath(name, null);
3838
} catch (IOException e) {
3939
// Ignored, index might not exist yet.
4040
}
4141

42-
indexResource.createIndex(name, new IndexDefinition("standard", null));
42+
indexResource.createIndex(name, new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null));
4343
try {
4444
final DocumentUpdateRequest documentUpdateRequest =
4545
new DocumentUpdateRequest(0, 1, null, Collections.emptyList());

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9AnalyzerFactory.java renamed to extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactory.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
package org.apache.couchdb.nouveau.lucene9;
14+
package org.apache.couchdb.nouveau.lucene;
1515

1616
import jakarta.ws.rs.WebApplicationException;
1717
import jakarta.ws.rs.core.Response.Status;
@@ -60,9 +60,9 @@
6060
import org.apache.lucene.analysis.th.ThaiAnalyzer;
6161
import org.apache.lucene.analysis.tr.TurkishAnalyzer;
6262

63-
public final class Lucene9AnalyzerFactory {
63+
public final class LuceneAnalyzerFactory {
6464

65-
private Lucene9AnalyzerFactory() {}
65+
private LuceneAnalyzerFactory() {}
6666

6767
public static Analyzer fromDefinition(final IndexDefinition indexDefinition) {
6868
final Analyzer defaultAnalyzer = newAnalyzer(indexDefinition.getDefaultAnalyzer());

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java renamed to extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
package org.apache.couchdb.nouveau.lucene9;
14+
package org.apache.couchdb.nouveau.lucene;
1515

1616
import jakarta.ws.rs.WebApplicationException;
1717
import jakarta.ws.rs.core.Response.Status;
@@ -97,7 +97,7 @@
9797
import org.apache.lucene.store.Directory;
9898
import org.apache.lucene.util.BytesRef;
9999

100-
public class Lucene9Index extends Index {
100+
public class LuceneIndex extends Index {
101101

102102
private static final Sort DEFAULT_SORT =
103103
new Sort(SortField.FIELD_SCORE, new SortField("_id", SortField.Type.STRING));
@@ -106,9 +106,9 @@ public class Lucene9Index extends Index {
106106
private final Analyzer analyzer;
107107
private final IndexWriter writer;
108108
private final SearcherManager searcherManager;
109-
private final Lucene9IndexSchema schema;
109+
private final LuceneIndexSchema schema;
110110

111-
public Lucene9Index(
111+
public LuceneIndex(
112112
final Analyzer analyzer,
113113
final IndexWriter writer,
114114
final long updateSeq,
@@ -290,8 +290,8 @@ private void collectHits(final IndexSearcher searcher, final TopDocs topDocs, fi
290290
hits.add(new SearchHit(doc.get("_id"), after, fields));
291291
}
292292

293-
searchResults.setTotalHits(topDocs.totalHits.value);
294-
searchResults.setTotalHitsRelation(topDocs.totalHits.relation);
293+
searchResults.setTotalHits(topDocs.totalHits.value());
294+
searchResults.setTotalHitsRelation(topDocs.totalHits.relation());
295295
searchResults.setHits(hits);
296296
}
297297

@@ -537,22 +537,22 @@ private Query parse(final SearchRequest request) {
537537
return result;
538538
}
539539

540-
private Lucene9IndexSchema initSchema(IndexWriter writer) {
540+
private LuceneIndexSchema initSchema(IndexWriter writer) {
541541
var commitData = writer.getLiveCommitData();
542542
if (commitData == null) {
543-
return Lucene9IndexSchema.emptySchema();
543+
return LuceneIndexSchema.emptySchema();
544544
}
545545
for (var entry : commitData) {
546546
if (entry.getKey().equals("_schema")) {
547-
return Lucene9IndexSchema.fromString(entry.getValue());
547+
return LuceneIndexSchema.fromString(entry.getValue());
548548
}
549549
}
550-
return Lucene9IndexSchema.emptySchema();
550+
return LuceneIndexSchema.emptySchema();
551551
}
552552

553553
@Override
554554
public String toString() {
555-
return "Lucene9Index [analyzer=" + analyzer + ", writer=" + writer + ", searcherManager=" + searcherManager
555+
return "LuceneIndex [analyzer=" + analyzer + ", writer=" + writer + ", searcherManager=" + searcherManager
556556
+ "]";
557557
}
558558

extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9IndexSchema.java renamed to extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndexSchema.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
// See the License for the specific language governing permissions and
1212
// limitations under the License.
1313

14-
package org.apache.couchdb.nouveau.lucene9;
14+
package org.apache.couchdb.nouveau.lucene;
1515

1616
import jakarta.ws.rs.WebApplicationException;
1717
import jakarta.ws.rs.core.Response.Status;
@@ -32,7 +32,7 @@
3232
import org.apache.couchdb.nouveau.api.TextField;
3333
import org.apache.lucene.queryparser.flexible.standard.config.PointsConfig;
3434

35-
final class Lucene9IndexSchema {
35+
final class LuceneIndexSchema {
3636

3737
public enum Type {
3838
STRING,
@@ -56,23 +56,23 @@ private static Type fromField(final Field field) {
5656

5757
private final ConcurrentMap<String, Type> map;
5858

59-
private Lucene9IndexSchema(Map<String, Type> map) {
59+
private LuceneIndexSchema(Map<String, Type> map) {
6060
this.map = new ConcurrentHashMap<>(map);
6161
this.map.put("_id", Type.STRING);
6262
}
6363

64-
public static Lucene9IndexSchema emptySchema() {
65-
return new Lucene9IndexSchema(new HashMap<String, Type>());
64+
public static LuceneIndexSchema emptySchema() {
65+
return new LuceneIndexSchema(new HashMap<String, Type>());
6666
}
6767

68-
public static Lucene9IndexSchema fromString(final String schemaStr) {
68+
public static LuceneIndexSchema fromString(final String schemaStr) {
6969
Objects.requireNonNull(schemaStr);
7070
if (schemaStr.isEmpty()) {
7171
return emptySchema();
7272
}
7373
var map = Arrays.stream(schemaStr.split(","))
7474
.collect(Collectors.toMap(i -> i.split(":")[0], i -> Type.valueOf(i.split(":")[1])));
75-
return new Lucene9IndexSchema(map);
75+
return new LuceneIndexSchema(map);
7676
}
7777

7878
public void update(final Collection<Field> fields) {

0 commit comments

Comments
 (0)