You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2023/04/06 11:13:52 UTC

[couchdb] 01/01: flatten to one project, one lucene

This is an automated email from the ASF dual-hosted git repository.

rnewson pushed a commit to branch import-nouveau-singlelucene
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit eed30eb907cf9b9fc53e22f871422f9df648fd0a
Author: Robert Newson <rn...@apache.org>
AuthorDate: Thu Apr 6 11:49:44 2023 +0100

    flatten to one project, one lucene
---
 dev/run                                            |  25 +-
 nouveau/{server => }/LICENSE                       |   0
 nouveau/{server => }/TODO                          |   0
 nouveau/base/pom.xml                               |  76 ----
 .../org/apache/couchdb/nouveau/LuceneBundle.java   |  28 --
 nouveau/lucene4/pom.xml                            | 143 -------
 nouveau/lucene4/src/main/assembly/dist.xml         |  26 --
 .../couchdb/nouveau/lucene4/Lucene4Bundle.java     |  51 ---
 .../lucene4/core/IndexableFieldDeserializer.java   |  96 -----
 .../lucene4/core/IndexableFieldSerializer.java     |  54 ---
 .../lucene4/core/Lucene4AnalyzerFactory.java       | 142 -------
 .../couchdb/nouveau/lucene4/core/Lucene4Index.java | 414 ---------------------
 .../nouveau/lucene4/core/Lucene4Module.java        |  36 --
 .../nouveau/lucene4/core/Lucene4QueryParser.java   | 155 --------
 .../lucene4/core/ParallelSearcherFactory.java      |  36 --
 .../nouveau/lucene4/core/PerFieldAnalyzer.java     |  39 --
 .../nouveau/lucene4/core/QueryDeserializer.java    |  41 --
 .../nouveau/lucene4/core/QuerySerializer.java      |  88 -----
 .../lucene4/core/SimpleAsciiFoldingAnalyzer.java   |  38 --
 .../apache/couchdb/nouveau/lucene4/core/Utils.java |  32 --
 .../nouveau/lucene4/health/AnalyzeHealthCheck.java |  25 --
 .../nouveau/lucene4/health/IndexHealthCheck.java   |  44 ---
 .../nouveau/lucene4/resources/AnalyzeResource.java |  76 ----
 .../nouveau/lucene4/resources/IndexResource.java   | 139 -------
 .../org.apache.couchdb.nouveau.LuceneBundle        |  16 -
 .../lucene4/core/Lucene4AnalyzerFactoryTest.java   | 255 -------------
 .../nouveau/lucene4/core/Lucene4IndexTest.java     | 111 ------
 nouveau/lucene4/src/test/resources/index.zip       | Bin 1853 -> 0 bytes
 nouveau/lucene9/pom.xml                            | 167 ---------
 nouveau/lucene9/src/main/assembly/dist.xml         |  26 --
 .../couchdb/nouveau/lucene9/Lucene9Bundle.java     |  52 ---
 .../org.apache.couchdb.nouveau.LuceneBundle        |  16 -
 .../nouveau/lucene9/core/CaffeineCacheTest.java    |  98 -----
 nouveau/{server => }/nouveau.yaml                  |   0
 nouveau/pom.xml                                    | 219 ++++++++---
 nouveau/server/pom.xml                             | 133 -------
 .../apache/couchdb/nouveau/NouveauApplication.java |  54 ++-
 .../nouveau/NouveauApplicationConfiguration.java   |   0
 .../java/org/apache/couchdb/nouveau/api/After.java |   0
 .../apache/couchdb/nouveau/api/AnalyzeRequest.java |   0
 .../couchdb/nouveau/api/AnalyzeResponse.java       |   0
 .../couchdb/nouveau/api/DocumentDeleteRequest.java |   0
 .../couchdb/nouveau/api/DocumentUpdateRequest.java |   0
 .../apache/couchdb/nouveau/api/DoubleRange.java    |   0
 .../couchdb/nouveau/api/IndexDefinition.java       |   0
 .../org/apache/couchdb/nouveau/api/IndexInfo.java  |   0
 .../java/org/apache/couchdb/nouveau/api/Range.java |   0
 .../org/apache/couchdb/nouveau/api/SearchHit.java  |   0
 .../apache/couchdb/nouveau/api/SearchRequest.java  |   0
 .../apache/couchdb/nouveau/api/SearchResults.java  |   0
 .../org/apache/couchdb/nouveau/core/IOUtils.java   |   0
 .../org/apache/couchdb/nouveau/core/Index.java     |   0
 .../couchdb/nouveau/core/IndexClosedException.java |   0
 .../apache/couchdb/nouveau/core/IndexFunction.java |   0
 .../apache/couchdb/nouveau/core/IndexLoader.java   |   0
 .../apache/couchdb/nouveau/core/IndexManager.java  |   0
 .../nouveau/core/UpdatesOutOfOrderException.java   |   0
 .../core/UpdatesOutOfOrderExceptionMapper.java     |   0
 .../nouveau/core/ser/AfterDeserializer.java        |   0
 .../couchdb/nouveau/core/ser/AfterSerializer.java  |   0
 .../nouveau/health/BaseAnalyzeHealthCheck.java     |   0
 .../nouveau/health/BaseIndexHealthCheck.java       |   0
 .../lucene9/core/IndexableFieldDeserializer.java   |   0
 .../lucene9/core/IndexableFieldSerializer.java     |   0
 .../lucene9/core/Lucene9AnalyzerFactory.java       |   0
 .../couchdb/nouveau/lucene9/core/Lucene9Index.java |   0
 .../nouveau/lucene9/core/Lucene9Module.java        |   0
 .../nouveau/lucene9/core/Lucene9QueryParser.java   |   0
 .../lucene9/core/NumericRangeQueryProcessor.java   |   0
 .../lucene9/core/ParallelSearcherFactory.java      |   0
 .../nouveau/lucene9/core/QueryDeserializer.java    |   0
 .../nouveau/lucene9/core/QuerySerializer.java      |   0
 .../lucene9/core/SimpleAsciiFoldingAnalyzer.java   |   0
 .../nouveau/lucene9/health/AnalyzeHealthCheck.java |   0
 .../nouveau/lucene9/health/IndexHealthCheck.java   |   0
 .../nouveau/lucene9/resources/AnalyzeResource.java |   0
 .../nouveau/lucene9/resources/IndexResource.java   |   0
 .../nouveau/resources/BaseAnalyzeResource.java     |   0
 .../nouveau/resources/BaseIndexResource.java       |   0
 .../couchdb/nouveau/tasks/CloseAllIndexesTask.java |   0
 nouveau/{server => }/src/main/resources/banner.txt |   0
 .../couchdb/nouveau/api/SearchRequestTest.java     |   0
 .../apache/couchdb/nouveau/core/BaseIndexTest.java |   0
 .../lucene9/core/Lucene9AnalyzerFactoryTest.java   |   0
 .../nouveau/lucene9/core/Lucene9IndexTest.java     |   0
 .../lucene9/core/QuerySerializationTest.java       |   0
 .../resources/fixtures/DocumentUpdateRequest.json  |   0
 .../src/test/resources/fixtures/SearchRequest.json |   0
 88 files changed, 203 insertions(+), 2748 deletions(-)

diff --git a/dev/run b/dev/run
index cb48c0be9..707dc709c 100755
--- a/dev/run
+++ b/dev/run
@@ -466,31 +466,12 @@ def boot_nouveau(ctx):
     if not ctx["with_nouveau"]:
         return
 
-    # generate classpath (the least stupid way Maven can do this apparently)
-    classpath_file = os.path.join(ctx["devdir"], "lib", "nouveau.classpath")
-    p = sp.Popen(
-        "mvn --quiet dependency:build-classpath -D mdep.outputFile=%s" % classpath_file,
-        cwd="nouveau/server",
-        shell=True,
-    )
-    p.wait()
-    with open(classpath_file, "r") as f:
-        classpath = f.read()
-    classpath += ":target/classes"
-
-    home = os.environ["HOME"]
     version = "1.0-SNAPSHOT"
-
     cmd = [
         "java",
         "-server",
-        "-Dnouveau.bundle.4=file://%s/.m2/repository/org/apache/couchdb/nouveau/lucene4/%s/lucene4-%s-dist.jar"
-        % (home, version, version),
-        "-Dnouveau.bundle.9=file://%s/.m2/repository/org/apache/couchdb/nouveau/lucene9/%s/lucene9-%s-dist.jar"
-        % (home, version, version),
-        "-classpath",
-        classpath,
-        "org.apache.couchdb.nouveau.NouveauApplication",
+        "-jar",
+        "target/server-%s-dist.jar" % version,
         "server",
         "nouveau.yaml",
     ]
@@ -499,7 +480,7 @@ def boot_nouveau(ctx):
     log = open(logfname, "w")
     return sp.Popen(
         " ".join(cmd),
-        cwd="nouveau/server",
+        cwd="nouveau",
         shell=True,
         stdin=sp.PIPE,
         stdout=log,
diff --git a/nouveau/server/LICENSE b/nouveau/LICENSE
similarity index 100%
rename from nouveau/server/LICENSE
rename to nouveau/LICENSE
diff --git a/nouveau/server/TODO b/nouveau/TODO
similarity index 100%
rename from nouveau/server/TODO
rename to nouveau/TODO
diff --git a/nouveau/base/pom.xml b/nouveau/base/pom.xml
deleted file mode 100644
index d14e49275..000000000
--- a/nouveau/base/pom.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.couchdb.nouveau</groupId>
-  <artifactId>base</artifactId>
-  <name>${project.artifactId}</name>
-  <version>1.0-SNAPSHOT</version>
-  <description>Nouveau Base classes</description>
-  <inceptionYear>2023</inceptionYear>
-
-  <parent>
-    <groupId>org.apache.couchdb.nouveau</groupId>
-    <artifactId>parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <dependencies>
-    <!-- Dropwizard -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.dropwizard.metrics</groupId>
-      <artifactId>metrics-caffeine</artifactId>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-testing</artifactId>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>junit</groupId>
-          <artifactId>junit</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-	  <groupId>org.junit.jupiter</groupId>
-	  <artifactId>junit-jupiter</artifactId>
-	  <scope>test</scope>
-	</dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter-engine</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <version>3.22.0</version>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <defaultGoal>install</defaultGoal>
-    <plugins>
-     <plugin>
-       <groupId>org.apache.maven.plugins</groupId>
-       <artifactId>maven-jar-plugin</artifactId>
-       <version>3.0.2</version>
-       <executions>
-         <execution>
-           <goals>
-             <goal>test-jar</goal>
-           </goals>
-         </execution>
-       </executions>
-     </plugin>
-    </plugins>
-  </build>
-
-</project>
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/LuceneBundle.java b/nouveau/base/src/main/java/org/apache/couchdb/nouveau/LuceneBundle.java
deleted file mode 100644
index f8408d6fb..000000000
--- a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/LuceneBundle.java
+++ /dev/null
@@ -1,28 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau;
-
-import org.apache.couchdb.nouveau.core.IndexManager;
-
-import io.dropwizard.core.ConfiguredBundle;
-
-public class LuceneBundle implements ConfiguredBundle<NouveauApplicationConfiguration> {
-
-    protected IndexManager indexManager;
-
-    public void setIndexManager(final IndexManager indexManager) {
-        this.indexManager = indexManager;
-    }
-
-}
diff --git a/nouveau/lucene4/pom.xml b/nouveau/lucene4/pom.xml
deleted file mode 100644
index c04bc541e..000000000
--- a/nouveau/lucene4/pom.xml
+++ /dev/null
@@ -1,143 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.couchdb.nouveau</groupId>
-  <artifactId>lucene4</artifactId>
-  <name>${project.artifactId}</name>
-  <version>1.0-SNAPSHOT</version>
-  <description>Lucene 4 index classes</description>
-  <inceptionYear>2023</inceptionYear>
-
-  <parent>
-    <groupId>org.apache.couchdb.nouveau</groupId>
-    <artifactId>parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <properties>
-    <lucene.version>4.6.1</lucene.version>
-  </properties>
-
-  <dependencies>
-
-    <!-- Base -->
-    <dependency>
-      <groupId>org.apache.couchdb.nouveau</groupId>
-      <artifactId>base</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-      <!-- Base tests -->
-    <dependency>
-      <groupId>org.apache.couchdb.nouveau</groupId>
-      <artifactId>base</artifactId>
-      <version>${project.version}</version>
-      <classifier>tests</classifier>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-
-    <!-- Dropwizard -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-core</artifactId>
-    </dependency>
-
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-
-    <!-- Lucene -->
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-core</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-queryparser</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analyzers-common</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analyzers-stempel</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analyzers-smartcn</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analyzers-kuromoji</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-facet</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-misc</artifactId>
-      <version>${lucene.version}</version>
-      <scope>provided</scope>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-	  <groupId>org.junit.jupiter</groupId>
-	  <artifactId>junit-jupiter</artifactId>
-	  <scope>test</scope>
-	</dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter-engine</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <version>3.22.0</version>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-
-  <build>
-    <defaultGoal>install</defaultGoal>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-assembly-plugin</artifactId>
-        <configuration>
-          <descriptors>
-            <descriptor>src/main/assembly/dist.xml</descriptor>
-          </descriptors>
-        </configuration>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>single</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/nouveau/lucene4/src/main/assembly/dist.xml b/nouveau/lucene4/src/main/assembly/dist.xml
deleted file mode 100644
index 68eaeaff0..000000000
--- a/nouveau/lucene4/src/main/assembly/dist.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-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.
--->
-<assembly>
-  <id>dist</id>
-  <formats>
-    <format>jar</format>
-  </formats>
-  <includeBaseDirectory>false</includeBaseDirectory>
-  <dependencySets>
-    <dependencySet>
-      <outputDirectory>/</outputDirectory>
-      <useProjectArtifact>true</useProjectArtifact>
-      <unpack>true</unpack>
-      <scope>provided</scope>
-    </dependencySet>
-  </dependencySets>
-</assembly>
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/Lucene4Bundle.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/Lucene4Bundle.java
deleted file mode 100644
index 7c89d9c8c..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/Lucene4Bundle.java
+++ /dev/null
@@ -1,51 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4;
-
-import java.util.concurrent.ForkJoinPool;
-
-import org.apache.couchdb.nouveau.LuceneBundle;
-import org.apache.couchdb.nouveau.NouveauApplicationConfiguration;
-import org.apache.couchdb.nouveau.lucene4.core.Lucene4Module;
-import org.apache.couchdb.nouveau.lucene4.core.ParallelSearcherFactory;
-import org.apache.couchdb.nouveau.lucene4.health.AnalyzeHealthCheck;
-import org.apache.couchdb.nouveau.lucene4.health.IndexHealthCheck;
-import org.apache.couchdb.nouveau.lucene4.resources.AnalyzeResource;
-import org.apache.couchdb.nouveau.lucene4.resources.IndexResource;
-import org.apache.lucene.search.SearcherFactory;
-
-import io.dropwizard.core.setup.Environment;
-
-public final class Lucene4Bundle extends LuceneBundle {
-
-    @Override
-    public void run(final NouveauApplicationConfiguration configuration, final Environment environment) throws Exception {
-
-        // Serialization classes
-        environment.getObjectMapper().registerModule(new Lucene4Module());
-
-        // AnalyzeResource
-        environment.jersey().register(new AnalyzeResource());
-
-        // IndexResource
-        final SearcherFactory searcherFactory = new ParallelSearcherFactory(ForkJoinPool.commonPool());
-        final IndexResource indexResource = new IndexResource(indexManager, searcherFactory);
-        environment.jersey().register(indexResource);
-
-        // Health checks
-        environment.healthChecks().register("analyze4", new AnalyzeHealthCheck());
-        environment.healthChecks().register("index4", new IndexHealthCheck(indexResource));
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldDeserializer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldDeserializer.java
deleted file mode 100644
index 3bb72450b..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldDeserializer.java
+++ /dev/null
@@ -1,96 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.IOException;
-
-import org.apache.lucene.document.DoubleField;
-import org.apache.lucene.document.SortedDocValuesField;
-import org.apache.lucene.document.SortedSetDocValuesField;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.StoredField;
-import org.apache.lucene.document.StringField;
-import org.apache.lucene.document.TextField;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.util.BytesRef;
-
-import com.fasterxml.jackson.core.JsonParseException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-
-class IndexableFieldDeserializer extends StdDeserializer<IndexableField> {
-
-    IndexableFieldDeserializer() {
-        this(null);
-    }
-
-    IndexableFieldDeserializer(Class<?> vc) {
-        super(vc);
-    }
-
-    @Override
-    public IndexableField deserialize(final JsonParser parser, final DeserializationContext context)
-            throws IOException, JsonProcessingException {
-        JsonNode node = parser.getCodec().readTree(parser);
-
-        final String type = node.get("@type").asText();
-        final String name = node.get("name").asText();
-
-        switch (type) {
-            case "double":
-                return new DoubleField(name, node.get("value").doubleValue(), asStore(node));
-            case "string":
-                return new StringField(name, node.get("value").asText(), asStore(node));
-            case "text":
-                return new TextField(name, node.get("value").asText(), asStore(node));
-            case "sorted_dv":
-                return new SortedDocValuesField(name, bytesRef(node));
-            case "sorted_set_dv":
-                return new SortedSetDocValuesField(name, bytesRef(node));
-            case "stored":
-                if (node.get("value").isDouble()) {
-                    return new StoredField(name, node.get("value").asDouble());
-                }
-                if (node.get("value").isTextual()) {
-                    final JsonNode value = node.get("value");
-                    if (node.has("encoded") && node.get("encoded").asBoolean()) {
-                        return new StoredField(name, new BytesRef(value.binaryValue()));
-                    } else {
-                        return new StoredField(name, value.asText());
-                    }
-                }
-                throw new JsonParseException(parser, node.get("value") + " not a valid value for a stored field");
-        }
-        throw new JsonParseException(parser, type + " not a valid type of field");
-    }
-
-    private Store asStore(JsonNode node) {
-        if (node.has("stored")) {
-            return node.get("stored").asBoolean() ? Store.YES : Store.NO;
-        }
-        return Store.NO;
-    }
-
-    private BytesRef bytesRef(final JsonNode node) throws IOException {
-        final JsonNode value = node.get("value");
-        if (node.has("encoded") && node.get("encoded").asBoolean()) {
-            return new BytesRef(value.binaryValue());
-        }
-        return new BytesRef(value.asText());
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldSerializer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldSerializer.java
deleted file mode 100644
index 13b83549a..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/IndexableFieldSerializer.java
+++ /dev/null
@@ -1,54 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.util.BytesRef;
-
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-
-class IndexableFieldSerializer extends StdSerializer<IndexableField> {
-
-    IndexableFieldSerializer() {
-        this(null);
-    }
-
-    IndexableFieldSerializer(Class<IndexableField> vc) {
-        super(vc);
-    }
-
-    @Override
-    public void serialize(final IndexableField field, final JsonGenerator gen, final SerializerProvider provider)
-            throws IOException {
-        gen.writeStartObject();
-        gen.writeStringField("@type", "stored");
-        gen.writeStringField("name", field.name());
-        if (field.numericValue() != null) {
-            gen.writeNumberField("value", (double) field.numericValue());
-        } else if (field.stringValue() != null) {
-            gen.writeStringField("value", field.stringValue());
-        } else if (field.binaryValue() != null) {
-            final BytesRef bytesRef = field.binaryValue();
-            gen.writeFieldName("value");
-            gen.writeBinary(bytesRef.bytes, bytesRef.offset, bytesRef.length);
-            gen.writeBooleanField("encoded", true);
-        }
-        gen.writeEndObject();
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactory.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactory.java
deleted file mode 100644
index f41734329..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactory.java
+++ /dev/null
@@ -1,142 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.function.Supplier;
-
-import jakarta.ws.rs.WebApplicationException;
-import jakarta.ws.rs.core.Response.Status;
-
-import org.apache.couchdb.nouveau.api.IndexDefinition;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.ar.ArabicAnalyzer;
-import org.apache.lucene.analysis.bg.BulgarianAnalyzer;
-import org.apache.lucene.analysis.ca.CatalanAnalyzer;
-import org.apache.lucene.analysis.cjk.CJKAnalyzer;
-import org.apache.lucene.analysis.standard.ClassicAnalyzer;
-import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
-import org.apache.lucene.analysis.core.KeywordAnalyzer;
-import org.apache.lucene.analysis.core.SimpleAnalyzer;
-import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
-import org.apache.lucene.analysis.cz.CzechAnalyzer;
-import org.apache.lucene.analysis.da.DanishAnalyzer;
-import org.apache.lucene.analysis.de.GermanAnalyzer;
-import org.apache.lucene.analysis.standard.UAX29URLEmailAnalyzer;
-import org.apache.lucene.analysis.en.EnglishAnalyzer;
-import org.apache.lucene.analysis.es.SpanishAnalyzer;
-import org.apache.lucene.analysis.eu.BasqueAnalyzer;
-import org.apache.lucene.analysis.fa.PersianAnalyzer;
-import org.apache.lucene.analysis.fi.FinnishAnalyzer;
-import org.apache.lucene.analysis.fr.FrenchAnalyzer;
-import org.apache.lucene.analysis.ga.IrishAnalyzer;
-import org.apache.lucene.analysis.gl.GalicianAnalyzer;
-import org.apache.lucene.analysis.hi.HindiAnalyzer;
-import org.apache.lucene.analysis.hu.HungarianAnalyzer;
-import org.apache.lucene.analysis.hy.ArmenianAnalyzer;
-import org.apache.lucene.analysis.id.IndonesianAnalyzer;
-import org.apache.lucene.analysis.it.ItalianAnalyzer;
-import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
-import org.apache.lucene.analysis.lv.LatvianAnalyzer;
-import org.apache.lucene.analysis.nl.DutchAnalyzer;
-import org.apache.lucene.analysis.no.NorwegianAnalyzer;
-import org.apache.lucene.analysis.pl.PolishAnalyzer;
-import org.apache.lucene.analysis.pt.PortugueseAnalyzer;
-import org.apache.lucene.analysis.ro.RomanianAnalyzer;
-import org.apache.lucene.analysis.ru.RussianAnalyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.analysis.sv.SwedishAnalyzer;
-import org.apache.lucene.analysis.th.ThaiAnalyzer;
-import org.apache.lucene.analysis.tr.TurkishAnalyzer;
-
-public final class Lucene4AnalyzerFactory {
-
-    public Lucene4AnalyzerFactory() {
-    }
-
-    public static Analyzer fromDefinition(final IndexDefinition indexDefinition) {
-        final Analyzer defaultAnalyzer = newAnalyzer(indexDefinition.getDefaultAnalyzer());
-        if (!indexDefinition.hasFieldAnalyzers()) {
-            return defaultAnalyzer;
-        }
-        final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
-        for (Map.Entry<String, String> entry : indexDefinition.getFieldAnalyzers().entrySet()) {
-            fieldAnalyzers.put(entry.getKey(), newAnalyzer(entry.getValue()));
-        }
-        return new PerFieldAnalyzer(defaultAnalyzer, fieldAnalyzers);
-    }
-
-    private enum KnownAnalyzer {
-        // TODO support stopword list configuration
-        arabic(() -> new ArabicAnalyzer(Utils.LUCENE_VERSION)),
-        armenian(() -> new ArmenianAnalyzer(Utils.LUCENE_VERSION)),
-        basque(() -> new BasqueAnalyzer(Utils.LUCENE_VERSION)),
-        bulgarian(() -> new BulgarianAnalyzer(Utils.LUCENE_VERSION)),
-        catalan(() -> new CatalanAnalyzer(Utils.LUCENE_VERSION)),
-        chinese(() -> new SmartChineseAnalyzer(Utils.LUCENE_VERSION)),
-        cjk(() -> new CJKAnalyzer(Utils.LUCENE_VERSION)),
-        classic(() -> new ClassicAnalyzer(Utils.LUCENE_VERSION)),
-        czech(() -> new CzechAnalyzer(Utils.LUCENE_VERSION)),
-        danish(() -> new DanishAnalyzer(Utils.LUCENE_VERSION)),
-        dutch(() -> new DutchAnalyzer(Utils.LUCENE_VERSION)),
-        email(() -> new UAX29URLEmailAnalyzer(Utils.LUCENE_VERSION)),
-        english(() -> new EnglishAnalyzer(Utils.LUCENE_VERSION)),
-        finnish(() -> new FinnishAnalyzer(Utils.LUCENE_VERSION)),
-        french(() -> new FrenchAnalyzer(Utils.LUCENE_VERSION)),
-        galician(() -> new GalicianAnalyzer(Utils.LUCENE_VERSION)),
-        german(() -> new GermanAnalyzer(Utils.LUCENE_VERSION)),
-        hindi(() -> new HindiAnalyzer(Utils.LUCENE_VERSION)),
-        hungarian(() -> new HungarianAnalyzer(Utils.LUCENE_VERSION)),
-        indonesian(() -> new IndonesianAnalyzer(Utils.LUCENE_VERSION)),
-        irish(() -> new IrishAnalyzer(Utils.LUCENE_VERSION)),
-        italian(() -> new ItalianAnalyzer(Utils.LUCENE_VERSION)),
-        japanese(() -> new JapaneseAnalyzer(Utils.LUCENE_VERSION)),
-        keyword(() -> new KeywordAnalyzer()),
-        latvian(() -> new LatvianAnalyzer(Utils.LUCENE_VERSION)),
-        norwegian(() -> new NorwegianAnalyzer(Utils.LUCENE_VERSION)),
-        persian(() -> new PersianAnalyzer(Utils.LUCENE_VERSION)),
-        polish(() -> new PolishAnalyzer(Utils.LUCENE_VERSION)),
-        portugese(() -> new PortugueseAnalyzer(Utils.LUCENE_VERSION)),
-        romanian(() -> new RomanianAnalyzer(Utils.LUCENE_VERSION)),
-        russian(() -> new RussianAnalyzer(Utils.LUCENE_VERSION)),
-        simple(() -> new SimpleAnalyzer(Utils.LUCENE_VERSION)),
-        simple_asciifolding(() -> new SimpleAsciiFoldingAnalyzer(Utils.LUCENE_VERSION)),
-        spanish(() -> new SpanishAnalyzer(Utils.LUCENE_VERSION)),
-        standard(() -> new StandardAnalyzer(Utils.LUCENE_VERSION)),
-        swedish(() -> new SwedishAnalyzer(Utils.LUCENE_VERSION)),
-        thai(() -> new ThaiAnalyzer(Utils.LUCENE_VERSION)),
-        turkish(() -> new TurkishAnalyzer(Utils.LUCENE_VERSION)),
-        whitespace(() -> new WhitespaceAnalyzer(Utils.LUCENE_VERSION));
-
-        private final Supplier<? extends Analyzer> supplier;
-
-        private KnownAnalyzer(final Supplier<? extends Analyzer> supplier) {
-            this.supplier = supplier;
-        }
-
-        private Analyzer newInstance() {
-            return supplier.get();
-        }
-    }
-
-    public static Analyzer newAnalyzer(final String name) {
-        try {
-            return KnownAnalyzer.valueOf(name).newInstance();
-        } catch (IllegalArgumentException e) {
-            throw new WebApplicationException(name + " is not a valid analyzer name", Status.BAD_REQUEST);
-        }
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Index.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Index.java
deleted file mode 100644
index 19412f88e..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Index.java
+++ /dev/null
@@ -1,414 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.nio.file.NoSuchFileException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Map.Entry;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import jakarta.ws.rs.WebApplicationException;
-import jakarta.ws.rs.core.Response.Status;
-
-import org.apache.couchdb.nouveau.api.After;
-import org.apache.couchdb.nouveau.api.DocumentDeleteRequest;
-import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
-import org.apache.couchdb.nouveau.api.DoubleRange;
-import org.apache.couchdb.nouveau.api.SearchHit;
-import org.apache.couchdb.nouveau.api.SearchRequest;
-import org.apache.couchdb.nouveau.api.SearchResults;
-import org.apache.couchdb.nouveau.core.IOUtils;
-import org.apache.couchdb.nouveau.core.Index;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.StringField;
-import org.apache.lucene.facet.params.FacetSearchParams;
-import org.apache.lucene.facet.range.RangeAccumulator;
-import org.apache.lucene.facet.range.RangeFacetRequest;
-import org.apache.lucene.facet.search.CountFacetRequest;
-import org.apache.lucene.facet.search.FacetRequest;
-import org.apache.lucene.facet.search.FacetResult;
-import org.apache.lucene.facet.search.FacetResultNode;
-import org.apache.lucene.facet.search.FacetsCollector;
-import org.apache.lucene.facet.sortedset.SortedSetDocValuesAccumulator;
-import org.apache.lucene.facet.sortedset.SortedSetDocValuesReaderState;
-import org.apache.lucene.facet.taxonomy.CategoryPath;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.index.Term;
-import org.apache.lucene.queryparser.classic.ParseException;
-import org.apache.lucene.search.FieldDoc;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.MultiCollector;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.ScoreDoc;
-import org.apache.lucene.search.SearcherManager;
-import org.apache.lucene.search.Sort;
-import org.apache.lucene.search.SortField;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.TopDocs;
-import org.apache.lucene.search.TopFieldCollector;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.util.BytesRef;
-
-public class Lucene4Index extends Index<IndexableField> {
-
-    private static final Sort DEFAULT_SORT = new Sort(SortField.FIELD_SCORE,
-            new SortField("_id", SortField.Type.STRING));
-    private static final Pattern SORT_FIELD_RE = Pattern.compile("^([-+])?([\\.\\w]+)(?:<(\\w+)>)?$");
-
-    private final Analyzer analyzer;
-    private final IndexWriter writer;
-    private final SearcherManager searcherManager;
-
-    public Lucene4Index(final Analyzer analyzer, final IndexWriter writer, final long updateSeq,
-            final SearcherManager searcherManager) {
-        super(updateSeq);
-        this.analyzer = Objects.requireNonNull(analyzer);
-        this.writer = Objects.requireNonNull(writer);
-        this.searcherManager = Objects.requireNonNull(searcherManager);
-    }
-
-    @Override
-    public int doNumDocs() throws IOException {
-        return writer.numDocs();
-    }
-
-    @Override
-    public long doDiskSize() throws IOException {
-        final Directory dir = writer.getDirectory();
-        long result = 0;
-        for (final String name : dir.listAll()) {
-            try {
-                result += dir.fileLength(name);
-            } catch (final FileNotFoundException | NoSuchFileException e) {
-                // deleted while we were looping.
-            }
-        }
-        return result;
-    }
-
-    @Override
-    public void doUpdate(final String docId, final DocumentUpdateRequest<IndexableField> request) throws IOException {
-        final Term docIdTerm = docIdTerm(docId);
-        final Document doc = toDocument(docId, request);
-        writer.updateDocument(docIdTerm, doc);
-    }
-
-    @Override
-    public void doDelete(final String docId, final DocumentDeleteRequest request) throws IOException {
-        final Query query = docIdQuery(docId);
-        writer.deleteDocuments(query);
-    }
-
-    @Override
-    public boolean doCommit(final long updateSeq) throws IOException {
-        if (!writer.hasUncommittedChanges()) {
-            return false;
-        }
-        writer.setCommitData(Collections.singletonMap("update_seq", Long.toString(updateSeq)));
-        writer.commit();
-        return true;
-    }
-
-    @Override
-    public void doClose() throws IOException {
-        IOUtils.runAll(
-                () -> {
-                    searcherManager.close();
-                },
-                () -> {
-                    writer.rollback();
-                },
-                () -> {
-                    if (isDeleteOnClose()) {
-                        var dir = writer.getDirectory();
-                        for (final String name : dir.listAll()) {
-                            dir.deleteFile(name);
-                        }
-                    }
-                });
-    }
-
-    @Override
-    public SearchResults<IndexableField> doSearch(final SearchRequest request) throws IOException {
-        final Query query;
-        try {
-            query = newQueryParser().parse(request);
-        } catch (final ParseException e) {
-            throw new WebApplicationException(e.getMessage(), e, Status.BAD_REQUEST);
-        }
-
-        // Construct Collectors.
-        var hitCollector = hitCollector(request);
-
-        searcherManager.maybeRefreshBlocking();
-        final IndexSearcher searcher = searcherManager.acquire();
-        try {
-            FacetsCollector countsCollector = null;
-            if (request.hasCounts()) {
-                countsCollector = countsCollector(searcher.getIndexReader(), request.getCounts());
-            }
-            FacetsCollector rangesCollector = null;
-            if (request.hasRanges()) {
-                rangesCollector = rangesCollector(searcher.getIndexReader(), request.getRanges());
-            }
-
-            searcher.search(query, MultiCollector.wrap(hitCollector, countsCollector, rangesCollector));
-            return toSearchResults(request, searcher, hitCollector, countsCollector, rangesCollector);
-        } catch (IllegalStateException e) {
-            throw new WebApplicationException(e.getMessage(), e, Status.BAD_REQUEST);
-        } finally {
-            searcherManager.release(searcher);
-        }
-    }
-
-    private TopFieldCollector hitCollector(final SearchRequest searchRequest) throws IOException {
-        final Sort sort = toSort(searchRequest);
-
-        final After after = searchRequest.getAfter();
-        final FieldDoc fieldDoc;
-        if (after != null) {
-            fieldDoc = toFieldDoc(after);
-            if (getLastSortField(sort).getReverse()) {
-                fieldDoc.doc = 0;
-            } else {
-                fieldDoc.doc = Integer.MAX_VALUE;
-            }
-        } else {
-            fieldDoc = null;
-        }
-
-        return TopFieldCollector.create(
-                sort,
-                searchRequest.getLimit(),
-                fieldDoc,
-                true,
-                false,
-                false,
-                false);
-    }
-
-    private FacetsCollector countsCollector(final IndexReader reader, List<String> counts) throws IOException {
-        final SortedSetDocValuesReaderState state;
-        try {
-            state = new SortedSetDocValuesReaderState(reader);
-        } catch (final IllegalArgumentException e) {
-            if (e.getMessage().contains("was not indexed with SortedSetDocValues")) {
-                return null;
-            }
-            throw e;
-        }
-        final FacetRequest[] facetRequests = new FacetRequest[counts.size()];
-        for (int i = 0; i < facetRequests.length; i++) {
-            facetRequests[i] = new CountFacetRequest(new CategoryPath(counts.get(i)), Integer.MAX_VALUE);
-        }
-        var facetSearchParams = new FacetSearchParams(facetRequests);
-        var acc = new SortedSetDocValuesAccumulator(state, facetSearchParams);
-        return FacetsCollector.create(acc);
-    }
-
-    private FacetsCollector rangesCollector(final IndexReader reader, Map<String, List<DoubleRange>> ranges)
-            throws IOException {
-        final List<FacetRequest> facetRequests = new ArrayList<FacetRequest>(ranges.size());
-        for (final Entry<String, List<DoubleRange>> e : ranges.entrySet()) {
-            final org.apache.lucene.facet.range.DoubleRange[] out = new org.apache.lucene.facet.range.DoubleRange[e
-                    .getValue().size()];
-            for (int i = 0; i < out.length; i++) {
-                final DoubleRange in = e.getValue().get(i);
-                out[i] = new org.apache.lucene.facet.range.DoubleRange(
-                        in.getLabel(),
-                        in.getMin(),
-                        in.isMinInclusive(),
-                        in.getMax(),
-                        in.isMaxInclusive());
-            }
-            facetRequests.add(new RangeFacetRequest<org.apache.lucene.facet.range.DoubleRange>(
-                    e.getKey(), out));
-        }
-        var acc = new RangeAccumulator(facetRequests);
-        return FacetsCollector.create(acc);
-    }
-
-    private SortField getLastSortField(final Sort sort) {
-        final SortField[] sortFields = sort.getSort();
-        return sortFields[sortFields.length - 1];
-    }
-
-    private SearchResults<IndexableField> toSearchResults(final SearchRequest searchRequest, final IndexSearcher searcher,
-        TopFieldCollector hitCollector, FacetsCollector countsCollector, FacetsCollector rangesCollector) throws IOException {
-        final SearchResults<IndexableField> result = new SearchResults<IndexableField>();
-        collectHits(searcher, hitCollector.topDocs(), result);
-        if (searchRequest.hasCounts()) {
-            result.setCounts(convertFacets(countsCollector));
-        }
-        if (searchRequest.hasRanges()) {
-            result.setRanges(convertFacets(rangesCollector));
-        }
-        return result;
-    }
-
-    private void collectHits(final IndexSearcher searcher, final TopDocs topDocs, final SearchResults<IndexableField> searchResults)
-            throws IOException {
-        final List<SearchHit<IndexableField>> hits = new ArrayList<SearchHit<IndexableField>>(topDocs.scoreDocs.length);
-
-        for (final ScoreDoc scoreDoc : topDocs.scoreDocs) {
-            final Document doc = searcher.doc(scoreDoc.doc);
-
-            final List<IndexableField> fields = new ArrayList<IndexableField>(doc.getFields().size());
-            for (IndexableField field : doc.getFields()) {
-                if (!field.name().equals("_id")) {
-                    fields.add(field);
-                }
-            }
-
-            final After after = toAfter(((FieldDoc)scoreDoc));
-            hits.add(new SearchHit<IndexableField>(doc.get("_id"), after, fields));
-        }
-
-        searchResults.setTotalHits(topDocs.totalHits);
-        searchResults.setHits(hits);
-    }
-
-    private Map<String,Map<String,Number>> convertFacets(final FacetsCollector fc) throws IOException {
-        if (fc == null) {
-            return null;
-        }
-        final Map<String,Map<String,Number>> result = new HashMap<String,Map<String,Number>>();
-        for (final FacetResult facetResult : fc.getFacetResults()) {
-            final FacetResultNode node = facetResult.getFacetResultNode();
-            final Map<String, Number> m = result.computeIfAbsent(node.label.components[0], (k) -> new HashMap<String, Number>());
-            for (final FacetResultNode n : node.subResults) {
-                m.put(n.label.components[1], n.value);
-            }
-        }
-        return result;
-    }
-
-    // Ensure _id is final sort field so we can paginate.
-    private Sort toSort(final SearchRequest searchRequest) {
-        if (!searchRequest.hasSort()) {
-            return DEFAULT_SORT;
-        }
-
-        final List<String> sort = new ArrayList<String>(searchRequest.getSort());
-        final String last = sort.get(sort.size() - 1);
-        // Append _id field if not already present.
-        switch (last) {
-            case "-_id<string>":
-            case "_id<string>":
-                break;
-            default:
-                sort.add("_id<string>");
-        }
-        return convertSort(sort);
-    }
-
-    private Sort convertSort(final List<String> sort) {
-        final SortField[] fields = new SortField[sort.size()];
-        for (int i = 0; i < sort.size(); i++) {
-            fields[i] = convertSortField(sort.get(i));
-        }
-        return new Sort(fields);
-    }
-
-    private SortField convertSortField(final String sortString) {
-        final Matcher m = SORT_FIELD_RE.matcher(sortString);
-        if (!m.matches()) {
-            throw new WebApplicationException(
-                    sortString + " is not a valid sort parameter", Status.BAD_REQUEST);
-        }
-        final boolean reverse = "-".equals(m.group(1));
-        SortField.Type type = SortField.Type.DOUBLE;
-        if ("string".equals(m.group(3))) {
-            type = SortField.Type.STRING;
-        }
-        return new SortField(m.group(2), type, reverse);
-    }
-
-    private static Document toDocument(final String docId, final DocumentUpdateRequest<IndexableField> request) throws IOException {
-        final Document result = new Document();
-
-        // id
-        result.add(new StringField("_id", docId, Store.YES));
-
-        // partition (optional)
-        if (request.hasPartition()) {
-            result.add(new StringField("_partition", request.getPartition(), Store.NO));
-        }
-
-        for (IndexableField field : request.getFields()) {
-            // Underscore-prefix is reserved.
-            if (field.name().startsWith("_")) {
-                continue;
-            }
-            result.add(field);
-        }
-
-        return result;
-    }
-
-    private FieldDoc toFieldDoc(final After after) {
-        final Object[] fields = Arrays.copyOf(after.getFields(), after.getFields().length);
-        for (int i = 0; i < fields.length; i++) {
-            if (fields[i] instanceof byte[]) {
-                fields[i] = new BytesRef((byte[])fields[i]);
-            }
-        }
-        return new FieldDoc(0, Float.NaN, fields);
-    }
-
-    private After toAfter(final FieldDoc fieldDoc) {
-        final Object[] fields = Arrays.copyOf(fieldDoc.fields, fieldDoc.fields.length);
-        for (int i = 0; i < fields.length; i++) {
-            if (fields[i] instanceof BytesRef) {
-                fields[i] = toBytes((BytesRef)fields[i]);
-            }
-        }
-        return new After(fields);
-    }
-
-    private static byte[] toBytes(final BytesRef bytesRef) {
-        return Arrays.copyOfRange(bytesRef.bytes, bytesRef.offset, bytesRef.offset + bytesRef.length);
-    }
-
-    private static Query docIdQuery(final String docId) {
-        return new TermQuery(docIdTerm(docId));
-    }
-
-    private static Term docIdTerm(final String docId) {
-        return new Term("_id", docId);
-    }
-
-    public Lucene4QueryParser newQueryParser() {
-        return new Lucene4QueryParser("default", analyzer);
-    }
-
-    @Override
-    public String toString() {
-        return "Lucene4Index [analyzer=" + analyzer + ", writer=" + writer + ", searcherManager=" + searcherManager + "]";
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Module.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Module.java
deleted file mode 100644
index c080f16b4..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4Module.java
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.search.Query;
-
-import com.fasterxml.jackson.core.Version;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-public class Lucene4Module extends SimpleModule {
-
-    public Lucene4Module() {
-        super("lucene4", Version.unknownVersion());
-
-        // IndexableField
-        addSerializer(IndexableField.class, new IndexableFieldSerializer());
-        addDeserializer(IndexableField.class, new IndexableFieldDeserializer());
-
-        // Query
-        addSerializer(Query.class, new QuerySerializer());
-        addDeserializer(Query.class, new QueryDeserializer());
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4QueryParser.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4QueryParser.java
deleted file mode 100644
index 3753973a3..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4QueryParser.java
+++ /dev/null
@@ -1,155 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.util.regex.Pattern;
-
-import org.apache.couchdb.nouveau.api.SearchRequest;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.queryparser.classic.ParseException;
-import org.apache.lucene.queryparser.classic.QueryParser;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.NumericRangeQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TermQuery;
-import org.apache.lucene.search.BooleanClause.Occur;
-
-import org.apache.lucene.analysis.core.KeywordAnalyzer;
-
-class Lucene4QueryParser extends QueryParser {
-
-    private static final Pattern DOUBLE_REGEX;
-
-    static {
-        final String Digits = "(\\p{Digit}+)";
-        final String HexDigits = "(\\p{XDigit}+)";
-        // an exponent is 'e' or 'E' followed by an optionally
-        // signed decimal integer.
-        final String Exp = "[eE][+-]?" + Digits;
-        final String fpRegex = ("[\\x00-\\x20]*" + // Optional leading "whitespace"
-                "[+-]?(" + // Optional sign character
-                "NaN|" + // "NaN" string
-                "Infinity|" + // "Infinity" string
-
-                // A decimal floating-point string representing a finite positive
-                // number without a leading sign has at most five basic pieces:
-                // Digits . Digits ExponentPart FloatTypeSuffix
-                //
-                // Since this method allows integer-only strings as input
-                // in addition to strings of floating-point literals, the
-                // two sub-patterns below are simplifications of the grammar
-                // productions from section 3.10.2 of
-                // The Java Language Specification.
-
-                // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt
-                "(((" + Digits + "(\\.)?(" + Digits + "?)(" + Exp + ")?)|" +
-
-                // . Digits ExponentPart_opt FloatTypeSuffix_opt
-                "(\\.(" + Digits + ")(" + Exp + ")?)|" +
-
-                // Hexadecimal strings
-                "((" +
-                // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt
-                "(0[xX]" + HexDigits + "(\\.)?)|" +
-
-                // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt
-                "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" +
-
-                ")[pP][+-]?" + Digits + "))" +
-                "[fFdD]?))" +
-                "[\\x00-\\x20]*");// Optional trailing "whitespace"
-
-        DOUBLE_REGEX = Pattern.compile(fpRegex);
-    }
-
-    Lucene4QueryParser(final String defaultField, final Analyzer analyzer) {
-        super(Utils.LUCENE_VERSION, defaultField, analyzer);
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getFieldQuery(String field, String queryText, boolean quoted)
-            throws ParseException {
-        if (!quoted && isNumber(queryText)) {
-            return new TermQuery(Utils.doubleToTerm(field, Double.parseDouble(queryText)));
-        }
-        return super.getFieldQuery(field, queryText, quoted);
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getFuzzyQuery(String field, String termStr, float minSimilarity)
-            throws ParseException {
-        setLowercaseExpandedTerms(field);
-        return super.getFuzzyQuery(field, termStr, minSimilarity);
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getPrefixQuery(String field, String termStr) throws ParseException {
-        setLowercaseExpandedTerms(field);
-        return super.getPrefixQuery(field, termStr);
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getRangeQuery(String field, String lower, String upper,
-            boolean startInclusive, boolean endInclusive) throws ParseException {
-        if (isNumber(lower) && isNumber(upper)) {
-            return NumericRangeQuery.newDoubleRange(field, 8, Double.parseDouble(lower),
-                    Double.parseDouble(upper), startInclusive, endInclusive);
-        }
-        setLowercaseExpandedTerms(field);
-        return super.getRangeQuery(field, lower, upper, startInclusive, endInclusive);
-
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getRegexpQuery(String field, String termStr) throws ParseException {
-        setLowercaseExpandedTerms(field);
-        return super.getRegexpQuery(field, termStr);
-    }
-
-    @Override
-    protected org.apache.lucene.search.Query getWildcardQuery(String field, String termStr) throws ParseException {
-        setLowercaseExpandedTerms(field);
-        return super.getWildcardQuery(field, termStr);
-    }
-
-    private static boolean isNumber(String str) {
-        return DOUBLE_REGEX.matcher(str).matches();
-    }
-
-    private void setLowercaseExpandedTerms(String field) {
-        Analyzer analyzer = getAnalyzer();
-        if (analyzer instanceof PerFieldAnalyzer) {
-            setLowercaseExpandedTerms(((PerFieldAnalyzer) analyzer).getWrappedAnalyzer(field));
-        } else {
-            setLowercaseExpandedTerms(analyzer);
-        }
-    }
-
-    private void setLowercaseExpandedTerms(Analyzer analyzer) {
-        setLowercaseExpandedTerms(!(analyzer instanceof KeywordAnalyzer));
-    }
-
-    public Query parse(SearchRequest searchRequest) throws ParseException {
-        final Query q = (Query) parse(searchRequest.getQuery());
-        if (searchRequest.hasPartition()) {
-            final BooleanQuery result = new BooleanQuery();
-            result.add(new TermQuery(new org.apache.lucene.index.Term("_partition",
-                    searchRequest.getPartition())), Occur.MUST);
-            result.add(q, Occur.MUST);
-            return result;
-        }
-        return q;
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/ParallelSearcherFactory.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/ParallelSearcherFactory.java
deleted file mode 100644
index cd1334494..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/ParallelSearcherFactory.java
+++ /dev/null
@@ -1,36 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.IOException;
-import java.util.concurrent.ExecutorService;
-
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.SearcherFactory;
-
-public class ParallelSearcherFactory extends SearcherFactory {
-
-    private ExecutorService executorService;
-
-    public ParallelSearcherFactory(ExecutorService executorService) {
-        this.executorService = executorService;
-    }
-
-    @Override
-    public IndexSearcher newSearcher(final IndexReader reader) throws IOException {
-        return new IndexSearcher(reader, executorService);
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/PerFieldAnalyzer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/PerFieldAnalyzer.java
deleted file mode 100644
index 1952d9e32..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/PerFieldAnalyzer.java
+++ /dev/null
@@ -1,39 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.util.Map;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.AnalyzerWrapper;
-
-// This only exists because PerFieldAnalyzerWrapper#getWrappedAnalyzer is protected
-public class PerFieldAnalyzer extends AnalyzerWrapper {
-
-    private final Analyzer defaultAnalyzer;
-    private final Map<String, Analyzer> fieldAnalyzers;
-
-    public PerFieldAnalyzer(Analyzer defaultAnalyzer,
-            Map<String, Analyzer> fieldAnalyzers) {
-        super(Analyzer.PER_FIELD_REUSE_STRATEGY);
-        this.defaultAnalyzer = defaultAnalyzer;
-        this.fieldAnalyzers = fieldAnalyzers;
-    }
-
-    @Override
-    protected Analyzer getWrappedAnalyzer(String fieldName) {
-        return fieldAnalyzers.getOrDefault(fieldName, defaultAnalyzer);
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QueryDeserializer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QueryDeserializer.java
deleted file mode 100644
index ca222605c..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QueryDeserializer.java
+++ /dev/null
@@ -1,41 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.IOException;
-
-import org.apache.lucene.search.Query;
-
-import com.fasterxml.jackson.core.JacksonException;
-import com.fasterxml.jackson.core.JsonParser;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
-
-class QueryDeserializer extends StdDeserializer<Query> {
-
-    QueryDeserializer() {
-        this(null);
-    }
-
-    QueryDeserializer(Class<?> vc) {
-        super(vc);
-    }
-
-    @Override
-    public Query deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
-        // TODO Auto-generated method stub
-        throw new UnsupportedOperationException("Unimplemented method 'deserialize'");
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QuerySerializer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QuerySerializer.java
deleted file mode 100644
index 7c814f3ed..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/QuerySerializer.java
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.IOException;
-
-import org.apache.lucene.index.Term;
-import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
-import org.apache.lucene.search.MatchAllDocsQuery;
-import org.apache.lucene.search.PhraseQuery;
-import org.apache.lucene.search.Query;
-import org.apache.lucene.search.TermQuery;
-
-import com.fasterxml.jackson.core.JsonGenerationException;
-import com.fasterxml.jackson.core.JsonGenerator;
-import com.fasterxml.jackson.databind.SerializerProvider;
-import com.fasterxml.jackson.databind.ser.std.StdSerializer;
-
-class QuerySerializer extends StdSerializer<Query> {
-
-    QuerySerializer() {
-        this(null);
-    }
-
-    QuerySerializer(Class<Query> vc) {
-        super(vc);
-    }
-
-    @Override
-    public void serialize(final Query query, final JsonGenerator gen, final SerializerProvider provider)
-            throws IOException {
-        if (query instanceof TermQuery) {
-            final TermQuery termQuery = (TermQuery) query;
-            gen.writeStartObject();
-            gen.writeStringField("@type", "term");
-            gen.writeStringField("field", termQuery.getTerm().field());
-            gen.writeStringField("term", termQuery.getTerm().text());
-            gen.writeEndObject();
-        } else if (query instanceof MatchAllDocsQuery) {
-            gen.writeStartObject();
-            gen.writeStringField("@type", "match_all");
-            gen.writeEndObject();
-        } else if (query instanceof PhraseQuery) {
-            final PhraseQuery phraseQuery = (PhraseQuery) query;
-            gen.writeStartObject();
-            gen.writeStringField("@type", "phrase");
-            final Term[] terms = phraseQuery.getTerms();
-            gen.writeStringField("field", terms[0].field());
-            gen.writeFieldName("terms");
-            gen.writeStartArray();
-            for (final Term term : terms) {
-                gen.writeString(term.text());
-            }
-            gen.writeEndArray();
-            gen.writeEndObject();
-        } else if (query instanceof BooleanQuery) {
-            final BooleanQuery booleanQuery = (BooleanQuery) query;
-            gen.writeStartObject();
-            gen.writeStringField("@type", "boolean");
-            gen.writeFieldName("clauses");
-            gen.writeStartArray();
-            for (final BooleanClause clause : booleanQuery.clauses()) {
-                gen.writeStartObject();
-                gen.writeFieldName("query");
-                serialize(clause.getQuery(), gen, provider);
-                gen.writeStringField("occur", clause.getOccur().name().toLowerCase());
-                gen.writeEndObject();
-            }
-            gen.writeEndArray();
-            gen.writeEndObject();
-        } else {
-            throw new JsonGenerationException(query.getClass() + " not supported", gen);
-        }
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/SimpleAsciiFoldingAnalyzer.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/SimpleAsciiFoldingAnalyzer.java
deleted file mode 100644
index 568e6f3ff..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/SimpleAsciiFoldingAnalyzer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import java.io.Reader;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.core.LetterTokenizer;
-import org.apache.lucene.analysis.core.LowerCaseFilter;
-import org.apache.lucene.analysis.miscellaneous.ASCIIFoldingFilter;
-import org.apache.lucene.util.Version;
-
-class SimpleAsciiFoldingAnalyzer extends Analyzer {
-
-    private Version version;
-
-    SimpleAsciiFoldingAnalyzer(final Version version) {
-        this.version = version;
-    }
-
-    @Override
-    protected TokenStreamComponents createComponents(String fieldName, Reader reader) {
-        var tokenizer = new LetterTokenizer(version, reader);
-        return new TokenStreamComponents(tokenizer, new ASCIIFoldingFilter(new LowerCaseFilter(version, tokenizer)));
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Utils.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Utils.java
deleted file mode 100644
index 2fdc2cf02..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/core/Utils.java
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import org.apache.lucene.index.Term;
-import org.apache.lucene.util.BytesRef;
-import org.apache.lucene.util.NumericUtils;
-import org.apache.lucene.util.Version;
-
-public class Utils {
-
-    public static final Version LUCENE_VERSION = Version.LUCENE_46;
-
-    static Term doubleToTerm(String field, Double value) {
-        var bytesRef = new BytesRef();
-        var asLong = NumericUtils.doubleToSortableLong(value);
-        NumericUtils.longToPrefixCoded(asLong, 0, bytesRef);
-        return new Term(field, bytesRef);
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/AnalyzeHealthCheck.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/AnalyzeHealthCheck.java
deleted file mode 100644
index 6f2cfc424..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/AnalyzeHealthCheck.java
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.health;
-
-import org.apache.couchdb.nouveau.health.BaseAnalyzeHealthCheck;
-import org.apache.couchdb.nouveau.lucene4.resources.AnalyzeResource;
-
-public class AnalyzeHealthCheck extends BaseAnalyzeHealthCheck {
-
-    public AnalyzeHealthCheck() {
-        super(new AnalyzeResource());
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/IndexHealthCheck.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/IndexHealthCheck.java
deleted file mode 100644
index 6fa5657d4..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/health/IndexHealthCheck.java
+++ /dev/null
@@ -1,44 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.health;
-
-import java.util.Collections;
-
-import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
-import org.apache.couchdb.nouveau.health.BaseIndexHealthCheck;
-import org.apache.couchdb.nouveau.lucene4.resources.IndexResource;
-import org.apache.lucene.index.IndexableField;
-
-public class IndexHealthCheck extends BaseIndexHealthCheck<IndexableField> {
-
-    public IndexHealthCheck(IndexResource indexResource) {
-        super(indexResource);
-    }
-
-    @Override
-    protected String generateIndexName() {
-        return "___test4";
-    }
-
-    @Override
-    protected DocumentUpdateRequest<IndexableField>  generateDocumentUpdateRequest() {
-        return new DocumentUpdateRequest<IndexableField>(1, null, Collections.emptyList());
-    }
-
-    @Override
-    protected int getLuceneMajor() {
-        return 4;
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/AnalyzeResource.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/AnalyzeResource.java
deleted file mode 100644
index eebb3c090..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/AnalyzeResource.java
+++ /dev/null
@@ -1,76 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.resources;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.ws.rs.Consumes;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.WebApplicationException;
-import jakarta.ws.rs.core.Response.Status;
-import jakarta.ws.rs.core.MediaType;
-
-import org.apache.couchdb.nouveau.api.AnalyzeRequest;
-import org.apache.couchdb.nouveau.api.AnalyzeResponse;
-import org.apache.couchdb.nouveau.lucene4.core.Lucene4AnalyzerFactory;
-import org.apache.couchdb.nouveau.resources.BaseAnalyzeResource;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
-
-import com.codahale.metrics.annotation.ExceptionMetered;
-import com.codahale.metrics.annotation.Metered;
-import com.codahale.metrics.annotation.ResponseMetered;
-
-@Path("/4/analyze")
-@Metered
-@ResponseMetered
-@ExceptionMetered(cause = IOException.class)
-@Consumes(MediaType.APPLICATION_JSON)
-@Produces(MediaType.APPLICATION_JSON)
-public class AnalyzeResource extends BaseAnalyzeResource {
-
-    @Override
-    @POST
-    public AnalyzeResponse analyzeText(@NotNull @Valid AnalyzeRequest request) throws IOException {
-        try {
-            final List<String> tokens = tokenize(Lucene4AnalyzerFactory.newAnalyzer(request.getAnalyzer()),
-                    request.getText());
-            return new AnalyzeResponse(tokens);
-        } catch (IllegalArgumentException e) {
-            throw new WebApplicationException(request.getAnalyzer() + " not a valid analyzer",
-                    Status.BAD_REQUEST);
-        }
-    }
-
-    private List<String> tokenize(final Analyzer analyzer, final String text) throws IOException {
-        final List<String> result = new ArrayList<String>(10);
-        try (final TokenStream tokenStream = analyzer.tokenStream("default", text)) {
-            tokenStream.reset();
-            while (tokenStream.incrementToken()) {
-                final CharTermAttribute term = tokenStream.getAttribute(CharTermAttribute.class);
-                result.add(term.toString());
-            }
-            tokenStream.end();
-        }
-        return result;
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/IndexResource.java b/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/IndexResource.java
deleted file mode 100644
index 02abd203c..000000000
--- a/nouveau/lucene4/src/main/java/org/apache/couchdb/nouveau/lucene4/resources/IndexResource.java
+++ /dev/null
@@ -1,139 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.resources;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.Map;
-
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotNull;
-import jakarta.ws.rs.Consumes;
-import jakarta.ws.rs.DELETE;
-import jakarta.ws.rs.GET;
-import jakarta.ws.rs.POST;
-import jakarta.ws.rs.PUT;
-import jakarta.ws.rs.Path;
-import jakarta.ws.rs.PathParam;
-import jakarta.ws.rs.Produces;
-import jakarta.ws.rs.core.MediaType;
-
-import org.apache.couchdb.nouveau.api.DocumentDeleteRequest;
-import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
-import org.apache.couchdb.nouveau.api.IndexDefinition;
-import org.apache.couchdb.nouveau.api.IndexInfo;
-import org.apache.couchdb.nouveau.api.SearchRequest;
-import org.apache.couchdb.nouveau.api.SearchResults;
-import org.apache.couchdb.nouveau.core.IndexLoader;
-import org.apache.couchdb.nouveau.core.IndexManager;
-import org.apache.couchdb.nouveau.lucene4.core.Lucene4AnalyzerFactory;
-import org.apache.couchdb.nouveau.lucene4.core.Lucene4Index;
-import org.apache.couchdb.nouveau.lucene4.core.Utils;
-import org.apache.couchdb.nouveau.resources.BaseIndexResource;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.search.SearcherFactory;
-import org.apache.lucene.search.SearcherManager;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.FSDirectory;
-
-import com.codahale.metrics.annotation.ExceptionMetered;
-import com.codahale.metrics.annotation.Metered;
-import com.codahale.metrics.annotation.ResponseMetered;;
-
-@Path("/4/index/{name}")
-@Metered
-@ResponseMetered
-@ExceptionMetered(cause = IOException.class)
-@Consumes(MediaType.APPLICATION_JSON)
-@Produces(MediaType.APPLICATION_JSON)
-public class IndexResource extends BaseIndexResource<IndexableField> {
-
-    private final SearcherFactory searcherFactory;
-
-    public IndexResource(final IndexManager indexManager, final SearcherFactory searcherFactory) {
-        super(indexManager);
-        this.searcherFactory = searcherFactory;
-    }
-
-    @PUT
-    @Override
-    public void createIndex(@PathParam("name") String name, @NotNull @Valid IndexDefinition indexDefinition)
-            throws IOException {
-        super.createIndex(name, indexDefinition);
-    }
-
-    @DELETE
-    @Path("/doc/{docId}")
-    @Override
-    public void deleteDoc(@PathParam("name") String name, @PathParam("docId") String docId,
-            @NotNull @Valid DocumentDeleteRequest request) throws Exception {
-        super.deleteDoc(name, docId, request);
-    }
-
-    @DELETE
-    @Override
-    public void deletePath(@PathParam("name") String path, @Valid List<String> exclusions) throws IOException {
-        super.deletePath(path, exclusions);
-    }
-
-    @GET
-    @Override
-    public IndexInfo indexInfo(@PathParam("name") String name) throws Exception {
-        return super.indexInfo(name);
-    }
-
-    @POST
-    @Path("/search")
-    @Override
-    public SearchResults<IndexableField> searchIndex(@PathParam("name") String name,
-            @NotNull @Valid SearchRequest request)
-            throws Exception {
-        return super.searchIndex(name, request);
-    }
-
-    @PUT
-    @Path("/doc/{docId}")
-    @Override
-    public void updateDoc(@PathParam("name") String name, @PathParam("docId") String docId,
-            @NotNull @Valid DocumentUpdateRequest<IndexableField> request)
-            throws Exception {
-        super.updateDoc(name, docId, request);
-    }
-
-    @Override
-    protected IndexLoader<IndexableField> indexLoader() {
-        return (path, indexDefinition) -> {
-            final Analyzer analyzer = Lucene4AnalyzerFactory.fromDefinition(indexDefinition);
-            final Directory dir = FSDirectory.open(path.toFile());
-            final IndexWriterConfig config = new IndexWriterConfig(Utils.LUCENE_VERSION, analyzer);
-            config.setUseCompoundFile(false);
-            final IndexWriter writer = new IndexWriter(dir, config);
-            final long updateSeq = getUpdateSeq(writer);
-            final SearcherManager searcherManager = new SearcherManager(writer, true, searcherFactory);
-            return new Lucene4Index(analyzer, writer, updateSeq, searcherManager);
-        };
-    }
-
-    private static long getUpdateSeq(final IndexWriter writer) throws IOException {
-        final Map<String, String> commitData = writer.getCommitData();
-        if (commitData == null) {
-            return 0L;
-        }
-        return Long.parseLong(commitData.getOrDefault("update_seq", "0"));
-    }
-
-}
diff --git a/nouveau/lucene4/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle b/nouveau/lucene4/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle
deleted file mode 100644
index 8fe744dd9..000000000
--- a/nouveau/lucene4/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle
+++ /dev/null
@@ -1,16 +0,0 @@
-#  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.
-
-org.apache.couchdb.nouveau.lucene4.Lucene4Bundle
diff --git a/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactoryTest.java b/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactoryTest.java
deleted file mode 100644
index 3a41189bd..000000000
--- a/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4AnalyzerFactoryTest.java
+++ /dev/null
@@ -1,255 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.ar.ArabicAnalyzer;
-import org.apache.lucene.analysis.bg.BulgarianAnalyzer;
-import org.apache.lucene.analysis.ca.CatalanAnalyzer;
-import org.apache.lucene.analysis.cjk.CJKAnalyzer;
-import org.apache.lucene.analysis.standard.ClassicAnalyzer;
-import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
-import org.apache.lucene.analysis.core.KeywordAnalyzer;
-import org.apache.lucene.analysis.core.SimpleAnalyzer;
-import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
-import org.apache.lucene.analysis.cz.CzechAnalyzer;
-import org.apache.lucene.analysis.da.DanishAnalyzer;
-import org.apache.lucene.analysis.de.GermanAnalyzer;
-import org.apache.lucene.analysis.standard.UAX29URLEmailAnalyzer;
-import org.apache.lucene.analysis.en.EnglishAnalyzer;
-import org.apache.lucene.analysis.es.SpanishAnalyzer;
-import org.apache.lucene.analysis.eu.BasqueAnalyzer;
-import org.apache.lucene.analysis.fa.PersianAnalyzer;
-import org.apache.lucene.analysis.fi.FinnishAnalyzer;
-import org.apache.lucene.analysis.fr.FrenchAnalyzer;
-import org.apache.lucene.analysis.ga.IrishAnalyzer;
-import org.apache.lucene.analysis.gl.GalicianAnalyzer;
-import org.apache.lucene.analysis.hi.HindiAnalyzer;
-import org.apache.lucene.analysis.hu.HungarianAnalyzer;
-import org.apache.lucene.analysis.hy.ArmenianAnalyzer;
-import org.apache.lucene.analysis.id.IndonesianAnalyzer;
-import org.apache.lucene.analysis.it.ItalianAnalyzer;
-import org.apache.lucene.analysis.ja.JapaneseAnalyzer;
-import org.apache.lucene.analysis.lv.LatvianAnalyzer;
-import org.apache.lucene.analysis.nl.DutchAnalyzer;
-import org.apache.lucene.analysis.no.NorwegianAnalyzer;
-import org.apache.lucene.analysis.pl.PolishAnalyzer;
-import org.apache.lucene.analysis.pt.PortugueseAnalyzer;
-import org.apache.lucene.analysis.ro.RomanianAnalyzer;
-import org.apache.lucene.analysis.ru.RussianAnalyzer;
-import org.apache.lucene.analysis.standard.StandardAnalyzer;
-import org.apache.lucene.analysis.sv.SwedishAnalyzer;
-import org.apache.lucene.analysis.th.ThaiAnalyzer;
-import org.apache.lucene.analysis.tr.TurkishAnalyzer;
-import org.junit.jupiter.api.Test;
-
-public class Lucene4AnalyzerFactoryTest {
-
-    @Test
-    public void testkeyword() throws Exception {
-        assertAnalyzer("keyword", KeywordAnalyzer.class);
-    }
-
-    @Test
-    public void testsimple() throws Exception {
-        assertAnalyzer("simple", SimpleAnalyzer.class);
-    }
-
-    @Test
-    public void testwhitespace() throws Exception {
-        assertAnalyzer("whitespace", WhitespaceAnalyzer.class);
-    }
-
-    @Test
-    public void testarabic() throws Exception {
-        assertAnalyzer("arabic", ArabicAnalyzer.class);
-    }
-
-    @Test
-    public void testbulgarian() throws Exception {
-        assertAnalyzer("bulgarian", BulgarianAnalyzer.class);
-    }
-
-    @Test
-    public void testcatalan() throws Exception {
-        assertAnalyzer("catalan", CatalanAnalyzer.class);
-    }
-
-    @Test
-    public void testcjk() throws Exception {
-        assertAnalyzer("cjk", CJKAnalyzer.class);
-    }
-
-    @Test
-    public void testchinese() throws Exception {
-        assertAnalyzer("chinese", SmartChineseAnalyzer.class);
-    }
-
-    @Test
-    public void testczech() throws Exception {
-        assertAnalyzer("czech", CzechAnalyzer.class);
-    }
-
-    @Test
-    public void testdanish() throws Exception {
-        assertAnalyzer("danish", DanishAnalyzer.class);
-    }
-
-    @Test
-    public void testgerman() throws Exception {
-        assertAnalyzer("german", GermanAnalyzer.class);
-    }
-
-    @Test
-    public void testenglish() throws Exception {
-        assertAnalyzer("english", EnglishAnalyzer.class);
-    }
-
-    @Test
-    public void testspanish() throws Exception {
-        assertAnalyzer("spanish", SpanishAnalyzer.class);
-    }
-
-    @Test
-    public void testbasque() throws Exception {
-        assertAnalyzer("basque", BasqueAnalyzer.class);
-    }
-
-    @Test
-    public void testpersian() throws Exception {
-        assertAnalyzer("persian", PersianAnalyzer.class);
-    }
-
-    @Test
-    public void testfinnish() throws Exception {
-        assertAnalyzer("finnish", FinnishAnalyzer.class);
-    }
-
-    @Test
-    public void testfrench() throws Exception {
-        assertAnalyzer("french", FrenchAnalyzer.class);
-    }
-
-    @Test
-    public void testirish() throws Exception {
-        assertAnalyzer("irish", IrishAnalyzer.class);
-    }
-
-    @Test
-    public void testgalician() throws Exception {
-        assertAnalyzer("galician", GalicianAnalyzer.class);
-    }
-
-    @Test
-    public void testhindi() throws Exception {
-        assertAnalyzer("hindi", HindiAnalyzer.class);
-    }
-
-    @Test
-    public void testhungarian() throws Exception {
-        assertAnalyzer("hungarian", HungarianAnalyzer.class);
-    }
-
-    @Test
-    public void testarmenian() throws Exception {
-        assertAnalyzer("armenian", ArmenianAnalyzer.class);
-    }
-
-    @Test
-    public void testindonesian() throws Exception {
-        assertAnalyzer("indonesian", IndonesianAnalyzer.class);
-    }
-
-    @Test
-    public void testitalian() throws Exception {
-        assertAnalyzer("italian", ItalianAnalyzer.class);
-    }
-
-    @Test
-    public void testjapanese() throws Exception {
-        assertAnalyzer("japanese", JapaneseAnalyzer.class);
-    }
-
-    @Test
-    public void testlatvian() throws Exception {
-        assertAnalyzer("latvian", LatvianAnalyzer.class);
-    }
-
-    @Test
-    public void testdutch() throws Exception {
-        assertAnalyzer("dutch", DutchAnalyzer.class);
-    }
-
-    @Test
-    public void testnorwegian() throws Exception {
-        assertAnalyzer("norwegian", NorwegianAnalyzer.class);
-    }
-
-    @Test
-    public void testpolish() throws Exception {
-        assertAnalyzer("polish", PolishAnalyzer.class);
-    }
-
-    @Test
-    public void testportugese() throws Exception {
-        assertAnalyzer("portugese", PortugueseAnalyzer.class);
-    }
-
-    @Test
-    public void testromanian() throws Exception {
-        assertAnalyzer("romanian", RomanianAnalyzer.class);
-    }
-
-    @Test
-    public void testrussian() throws Exception {
-        assertAnalyzer("russian", RussianAnalyzer.class);
-    }
-
-    @Test
-    public void testclassic() throws Exception {
-        assertAnalyzer("classic", ClassicAnalyzer.class);
-    }
-
-    @Test
-    public void teststandard() throws Exception {
-        assertAnalyzer("standard", StandardAnalyzer.class);
-    }
-
-    @Test
-    public void testemail() throws Exception {
-        assertAnalyzer("email", UAX29URLEmailAnalyzer.class);
-    }
-
-    @Test
-    public void testswedish() throws Exception {
-        assertAnalyzer("swedish", SwedishAnalyzer.class);
-    }
-
-    @Test
-    public void testthai() throws Exception {
-        assertAnalyzer("thai", ThaiAnalyzer.class);
-    }
-
-    @Test
-    public void testturkish() throws Exception {
-        assertAnalyzer("turkish", TurkishAnalyzer.class);
-    }
-
-    private void assertAnalyzer(final String name, final Class<? extends Analyzer> clazz) throws Exception {
-        assertThat(Lucene4AnalyzerFactory.newAnalyzer(name)).isInstanceOf(clazz);
-    }
-
-}
diff --git a/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4IndexTest.java b/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4IndexTest.java
deleted file mode 100644
index 519c6411d..000000000
--- a/nouveau/lucene4/src/test/java/org/apache/couchdb/nouveau/lucene4/core/Lucene4IndexTest.java
+++ /dev/null
@@ -1,111 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene4.core;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
-import org.apache.couchdb.nouveau.api.DoubleRange;
-import org.apache.couchdb.nouveau.api.SearchRequest;
-import org.apache.couchdb.nouveau.api.SearchResults;
-import org.apache.couchdb.nouveau.core.BaseIndexTest;
-import org.apache.couchdb.nouveau.core.Index;
-import org.apache.couchdb.nouveau.core.IndexLoader;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.document.DoubleDocValuesField;
-import org.apache.lucene.document.Field.Store;
-import org.apache.lucene.document.SortedSetDocValuesField;
-import org.apache.lucene.document.StringField;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.IndexableField;
-import org.apache.lucene.search.SearcherManager;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.store.FSDirectory;
-import org.apache.lucene.util.BytesRef;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-public class Lucene4IndexTest extends BaseIndexTest<IndexableField> {
-
-    @Override
-    protected IndexLoader<IndexableField> indexLoader() {
-        return (path, indexDefinition) -> {
-            final Analyzer analyzer = Lucene4AnalyzerFactory.fromDefinition(indexDefinition);
-            final Directory dir = FSDirectory.open(path.toFile());
-            final IndexWriterConfig config = new IndexWriterConfig(Utils.LUCENE_VERSION, analyzer);
-            config.setUseCompoundFile(false);
-            final IndexWriter writer = new IndexWriter(dir, config);
-            final SearcherManager searcherManager = new SearcherManager(writer, true, null);
-            return new Lucene4Index(analyzer, writer, 0L, searcherManager);
-        };
-    }
-
-    protected IndexableField stringField(final String name, final String value) {
-        return new StringField(name, value, Store.NO);
-    }
-
-    @Test
-    public void testCounts(@TempDir Path path) throws IOException {
-        Index<IndexableField> index = setup(path);
-        try {
-            final int count = 100;
-            for (int i = 1; i <= count; i++) {
-                final Collection<IndexableField> fields = List
-                        .of(new SortedSetDocValuesField("$facets_sorted_doc_values", new BytesRef("bar\u001Fbaz")));
-                final DocumentUpdateRequest<IndexableField> request = new DocumentUpdateRequest<IndexableField>(i, null,
-                        fields);
-                index.update("doc" + i, request);
-            }
-            final SearchRequest request = new SearchRequest();
-            request.setQuery("*:*");
-            request.setCounts(List.of("bar"));
-            final SearchResults<IndexableField> results = index.search(request);
-            assertThat(results.getCounts()).isEqualTo(Map.of("bar", Map.of("baz", (double) count)));
-        } finally {
-            cleanup(index);
-        }
-    }
-
-    @Test
-    public void testRanges(@TempDir Path path) throws IOException {
-        Index<IndexableField> index = setup(path);
-        try {
-            final int count = 100;
-            for (int i = 1; i <= count; i++) {
-                final Collection<IndexableField> fields = List.of(new DoubleDocValuesField("bar", i));
-                final DocumentUpdateRequest<IndexableField> request = new DocumentUpdateRequest<IndexableField>(i, null,
-                        fields);
-                index.update("doc" + i, request);
-            }
-            final SearchRequest request = new SearchRequest();
-            request.setQuery("*:*");
-            request.setRanges(Map.of("bar",
-                    List.of(new DoubleRange("low", 0.0, true, (double) count / 2, true),
-                            new DoubleRange("high", (double) count / 2, true, (double) count, true))));
-            final SearchResults<IndexableField> results = index.search(request);
-            assertThat(results.getRanges()).isEqualTo(
-                    Map.of("bar", Map.of("low", (double) count / 2, "high", (double) count / 2 + 1)));
-        } finally {
-            cleanup(index);
-        }
-    }
-
-}
diff --git a/nouveau/lucene4/src/test/resources/index.zip b/nouveau/lucene4/src/test/resources/index.zip
deleted file mode 100644
index 86fd09b65..000000000
Binary files a/nouveau/lucene4/src/test/resources/index.zip and /dev/null differ
diff --git a/nouveau/lucene9/pom.xml b/nouveau/lucene9/pom.xml
deleted file mode 100644
index 9047633aa..000000000
--- a/nouveau/lucene9/pom.xml
+++ /dev/null
@@ -1,167 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.couchdb.nouveau</groupId>
-  <artifactId>lucene9</artifactId>
-  <name>${project.artifactId}</name>
-  <version>1.0-SNAPSHOT</version>
-  <description>Lucene 9 index classes</description>
-  <inceptionYear>2023</inceptionYear>
-
-  <parent>
-    <groupId>org.apache.couchdb.nouveau</groupId>
-    <artifactId>parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <properties>
-    <lucene.version>9.5.0</lucene.version>
-  </properties>
-
-  <dependencies>
-
-    <!-- Base -->
-    <dependency>
-      <groupId>org.apache.couchdb.nouveau</groupId>
-      <artifactId>base</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <!-- Base tests -->
-    <dependency>
-      <groupId>org.apache.couchdb.nouveau</groupId>
-      <artifactId>base</artifactId>
-      <version>${project.version}</version>
-      <classifier>tests</classifier>
-      <type>test-jar</type>
-      <scope>test</scope>
-    </dependency>
-
-    <!-- Dropwizard -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-core</artifactId>
-    </dependency>
-
-    <dependency>
-      <groupId>com.fasterxml.jackson.core</groupId>
-      <artifactId>jackson-databind</artifactId>
-    </dependency>
-
-    <!-- Lucene -->
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-core</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-queryparser</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analysis-common</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analysis-stempel</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analysis-smartcn</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-analysis-kuromoji</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-facet</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.lucene</groupId>
-      <artifactId>lucene-misc</artifactId>
-      <version>${lucene.version}</version>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-	  <groupId>org.junit.jupiter</groupId>
-	  <artifactId>junit-jupiter</artifactId>
-	  <scope>test</scope>
-	</dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter-engine</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <version>3.22.0</version>
-      <scope>test</scope>
-    </dependency>
-
-  </dependencies>
-
-  <build>
-    <defaultGoal>install</defaultGoal>
-    <plugins>
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-shade-plugin</artifactId>
-        <configuration>
-          <shadedArtifactAttached>true</shadedArtifactAttached>
-          <shadedClassifierName>dist</shadedClassifierName>
-          <createSourcesJar>true</createSourcesJar>
-          <shadeSourcesContent>true</shadeSourcesContent>
-          <filters>
-            <filter>
-              <artifact>*:*</artifact>
-              <excludes>
-                <exclude>module-info.class</exclude>
-                <exclude>module-info.java</exclude>
-                <exclude>META-INF/LICENSE.txt</exclude>
-                <exclude>META-INF/NOTICE.txt</exclude>
-                <exclude>META-INF/MANIFEST.MF</exclude>
-                <exclude>overview.html</exclude>
-                <exclude>**/package.html</exclude>
-              </excludes>
-            </filter>
-          </filters>
-          <artifactSet>
-            <includes>
-              <include>org.apache.lucene:*</include>
-              <include>com.carrotsearch:hppc</include>
-            </includes>
-          </artifactSet>
-          <transformers>
-            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
-            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
-              <manifestEntries>
-                <Multi-Release>true</Multi-Release>
-              </manifestEntries>
-            </transformer>
-            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
-              <resource>META-INF/versions</resource>
-            </transformer>
-          </transformers>
-        </configuration>
-        <executions>
-          <execution>
-            <phase>package</phase>
-            <goals>
-              <goal>shade</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
-    </plugins>
-  </build>
-</project>
diff --git a/nouveau/lucene9/src/main/assembly/dist.xml b/nouveau/lucene9/src/main/assembly/dist.xml
deleted file mode 100644
index 68eaeaff0..000000000
--- a/nouveau/lucene9/src/main/assembly/dist.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<!--
-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.
--->
-<assembly>
-  <id>dist</id>
-  <formats>
-    <format>jar</format>
-  </formats>
-  <includeBaseDirectory>false</includeBaseDirectory>
-  <dependencySets>
-    <dependencySet>
-      <outputDirectory>/</outputDirectory>
-      <useProjectArtifact>true</useProjectArtifact>
-      <unpack>true</unpack>
-      <scope>provided</scope>
-    </dependencySet>
-  </dependencySets>
-</assembly>
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Bundle.java b/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Bundle.java
deleted file mode 100644
index 4aec993aa..000000000
--- a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Bundle.java
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene9;
-
-import java.util.concurrent.ForkJoinPool;
-
-import org.apache.couchdb.nouveau.LuceneBundle;
-import org.apache.couchdb.nouveau.NouveauApplicationConfiguration;
-import org.apache.couchdb.nouveau.lucene9.core.Lucene9Module;
-import org.apache.couchdb.nouveau.lucene9.core.ParallelSearcherFactory;
-import org.apache.couchdb.nouveau.lucene9.health.AnalyzeHealthCheck;
-import org.apache.couchdb.nouveau.lucene9.health.IndexHealthCheck;
-import org.apache.couchdb.nouveau.lucene9.resources.AnalyzeResource;
-import org.apache.couchdb.nouveau.lucene9.resources.IndexResource;
-import org.apache.lucene.search.SearcherFactory;
-
-import io.dropwizard.core.setup.Environment;
-
-public final class Lucene9Bundle extends LuceneBundle {
-
-    @Override
-    public void run(final NouveauApplicationConfiguration configuration, final Environment environment)
-            throws Exception {
-
-        // Serialization classes
-        environment.getObjectMapper().registerModule(new Lucene9Module());
-
-        // AnalyzeResource
-        environment.jersey().register(new AnalyzeResource());
-
-        // IndexResource
-        final SearcherFactory searcherFactory = new ParallelSearcherFactory(ForkJoinPool.commonPool());
-        final IndexResource indexResource = new IndexResource(indexManager, searcherFactory);
-        environment.jersey().register(indexResource);
-
-        // Health checks
-        environment.healthChecks().register("analyze9", new AnalyzeHealthCheck());
-        environment.healthChecks().register("index9", new IndexHealthCheck(indexResource));
-    }
-
-}
diff --git a/nouveau/lucene9/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle b/nouveau/lucene9/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle
deleted file mode 100644
index 64ad96f64..000000000
--- a/nouveau/lucene9/src/main/resources/META-INF/services/org.apache.couchdb.nouveau.LuceneBundle
+++ /dev/null
@@ -1,16 +0,0 @@
-#  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.
-
-org.apache.couchdb.nouveau.lucene9.Lucene9Bundle
diff --git a/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/CaffeineCacheTest.java b/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/CaffeineCacheTest.java
deleted file mode 100644
index cfc5ded86..000000000
--- a/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/CaffeineCacheTest.java
+++ /dev/null
@@ -1,98 +0,0 @@
-//
-// 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 org.apache.couchdb.nouveau.lucene9.core;
-
-import static org.assertj.core.api.Assertions.*;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Collections;
-import java.util.Random;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import org.apache.lucene.document.Document;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.store.FSDirectory;
-import org.junit.jupiter.api.Tag;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-import com.github.benmanes.caffeine.cache.CacheLoader;
-import com.github.benmanes.caffeine.cache.Caffeine;
-import com.github.benmanes.caffeine.cache.LoadingCache;
-import com.github.benmanes.caffeine.cache.RemovalListener;
-import com.github.benmanes.caffeine.cache.Scheduler;
-
-public class CaffeineCacheTest {
-
-    @Test
-    public void testCaffeine(@TempDir Path tmpDir) throws Exception {
-        final AtomicBoolean failed = new AtomicBoolean();
-
-        final CacheLoader<Integer, IndexWriter> loader = (n) -> {
-            var dir = FSDirectory.open(tmpDir.resolve("index-" + n));
-            var conf = new IndexWriterConfig();
-            var writer = new IndexWriter(dir, conf);
-            writer.setLiveCommitData(Collections.singletonMap("foo", "bar").entrySet());
-            writer.commit();
-            return writer;
-        };
-
-        RemovalListener<Integer, IndexWriter> evictor = (n, writer, cause) -> {
-            try {
-                writer.close();
-            } catch (IOException e) {
-                failed.set(true);
-                e.printStackTrace();
-            }
-        };
-
-        final LoadingCache<Integer, IndexWriter> cache = Caffeine.newBuilder()
-                .expireAfterAccess(1, TimeUnit.SECONDS)
-                .maximumSize(10)
-                .evictionListener(evictor)
-                .scheduler(Scheduler.systemScheduler())
-                .build(loader);
-
-        final int nThreads = 20;
-        final Thread[] threads = new Thread[nThreads];
-        for (int i = 0; i < nThreads; i++) {
-            threads[i] = new Thread(() -> {
-                final Random testRandom = new Random();
-                for (int j = 0; j < 10000; j++) {
-                    final IndexWriter writer = cache.get(testRandom.nextInt(15));
-                    try {
-                        writer.addDocument(new Document());
-                        writer.commit();
-                    } catch (IOException e) {
-                        failed.set(true);
-                        return;
-                    }
-                }
-            });
-            threads[i].start();
-        }
-
-        for (int i = 0; i < nThreads; i++) {
-            threads[i].join();
-        }
-
-        cache.invalidateAll();
-
-        assertThat(failed).isFalse();
-    }
-}
diff --git a/nouveau/server/nouveau.yaml b/nouveau/nouveau.yaml
similarity index 100%
rename from nouveau/server/nouveau.yaml
rename to nouveau/nouveau.yaml
diff --git a/nouveau/pom.xml b/nouveau/pom.xml
index cf0c08a57..cf8a3be69 100644
--- a/nouveau/pom.xml
+++ b/nouveau/pom.xml
@@ -14,23 +14,23 @@
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>
   <groupId>org.apache.couchdb.nouveau</groupId>
-  <artifactId>parent</artifactId>
+  <artifactId>server</artifactId>
   <version>1.0-SNAPSHOT</version>
-  <packaging>pom</packaging>
   <name>${project.artifactId}</name>
   <description>Full-text indexing for CouchDB</description>
   <inceptionYear>2022</inceptionYear>
 
   <properties>
     <argLine>-Duser.language=en -Duser.region=US -Duser.timezone=UTC</argLine>
+    <dropwizard.version>4.0.0</dropwizard.version>
+    <junit5.version>5.8.2</junit5.version>
+    <lucene.version>9.5.0</lucene.version>
     <maven.compiler.source>11</maven.compiler.source>
     <maven.compiler.target>11</maven.compiler.target>
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
-    <dropwizard.version>4.0.0</dropwizard.version>
-    <junit5.version>5.8.2</junit5.version>
-    <slf4j.version>1.7.32</slf4j.version>
     <project.tests.exclude>SlowTest</project.tests.exclude>
+    <slf4j.version>1.7.32</slf4j.version>
   </properties>
 
   <dependencyManagement>
@@ -52,46 +52,179 @@
 	</dependencies>
   </dependencyManagement>
 
-  <modules>
-    <module>base</module>
-    <module>lucene4</module>
-    <module>lucene9</module>
-    <module>server</module>
-  </modules>
+  <dependencies>
+
+    <!-- Dropwizard -->
+    <dependency>
+      <groupId>io.dropwizard</groupId>
+      <artifactId>dropwizard-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard</groupId>
+      <artifactId>dropwizard-http2</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.fasterxml.jackson.module</groupId>
+      <artifactId>jackson-module-afterburner</artifactId>
+    </dependency>
+
+    <!-- Dropwizard metrics -->
+    <dependency>
+        <groupId>io.dropwizard.metrics</groupId>
+        <artifactId>metrics-core</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-caffeine</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>io.dropwizard.metrics</groupId>
+      <artifactId>metrics-jersey2</artifactId>
+    </dependency>
+
+    <!-- Lucene -->
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-core</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-queryparser</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analysis-common</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analysis-stempel</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analysis-smartcn</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-analysis-kuromoji</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-facet</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.lucene</groupId>
+      <artifactId>lucene-misc</artifactId>
+      <version>${lucene.version}</version>
+    </dependency>
+
+    <!-- Test -->
+    <dependency>
+      <groupId>io.dropwizard</groupId>
+      <artifactId>dropwizard-testing</artifactId>
+      <scope>test</scope>
+      <exclusions>
+        <exclusion>
+          <groupId>junit</groupId>
+          <artifactId>junit</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+    <dependency>
+	  <groupId>org.junit.jupiter</groupId>
+	  <artifactId>junit-jupiter</artifactId>
+	  <scope>test</scope>
+	</dependency>
+    <dependency>
+      <groupId>org.junit.jupiter</groupId>
+      <artifactId>junit-jupiter-engine</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.assertj</groupId>
+      <artifactId>assertj-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.mockito</groupId>
+      <artifactId>mockito-core</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
 
   <build>
-    <pluginManagement>
-      <plugins>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-		  <artifactId>maven-surefire-plugin</artifactId>
-		  <version>2.22.2</version>
-          <configuration>
-            <excludedGroups>${project.tests.exclude}</excludedGroups>
-          </configuration>
-	    </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-assembly-plugin</artifactId>
-          <version>3.4.2</version>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-dependency-plugin</artifactId>
-          <version>3.5.0</version>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-jar-plugin</artifactId>
-          <version>3.3.0</version>
-        </plugin>
-        <plugin>
-          <groupId>org.apache.maven.plugins</groupId>
-          <artifactId>maven-shade-plugin</artifactId>
-          <version>3.4.1</version>
-        </plugin>
-      </plugins>
-    </pluginManagement>
+    <defaultGoal>package</defaultGoal>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-shade-plugin</artifactId>
+        <version>3.4.1</version>
+        <configuration>
+          <shadedArtifactAttached>true</shadedArtifactAttached>
+          <shadedClassifierName>dist</shadedClassifierName>
+          <createDependencyReducedPom>true</createDependencyReducedPom>
+          <filters>
+            <filter>
+              <artifact>*:*</artifact>
+              <excludes>
+                <exclude>META-INF/*.DSA</exclude>
+                <exclude>META-INF/*.RSA</exclude>
+                <exclude>META-INF/*.SF</exclude>
+              </excludes>
+            </filter>
+          </filters>
+          <transformers>
+            <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+            <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
+              <mainClass>org.apache.couchdb.nouveau.NouveauApplication</mainClass>
+              <manifestEntries>
+                <Multi-Release>true</Multi-Release>
+              </manifestEntries>
+            </transformer>
+            <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
+              <resource>META-INF/versions</resource>
+            </transformer>
+          </transformers>
+        </configuration>
+        <executions>
+          <execution>
+            <phase>package</phase>
+            <goals>
+              <goal>shade</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+		<artifactId>maven-surefire-plugin</artifactId>
+		<version>2.22.2</version>
+        <configuration>
+          <excludedGroups>${project.tests.exclude}</excludedGroups>
+        </configuration>
+	  </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-assembly-plugin</artifactId>
+        <version>3.4.2</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-dependency-plugin</artifactId>
+        <version>3.5.0</version>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <version>3.3.0</version>
+      </plugin>
+    </plugins>
   </build>
 
   <profiles>
diff --git a/nouveau/server/pom.xml b/nouveau/server/pom.xml
deleted file mode 100644
index 4c8c343ec..000000000
--- a/nouveau/server/pom.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<!--
- 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.
--->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-  <modelVersion>4.0.0</modelVersion>
-  <groupId>org.apache.couchdb.nouveau</groupId>
-  <artifactId>server</artifactId>
-  <version>1.0-SNAPSHOT</version>
-  <name>${project.artifactId}</name>
-  <description>Full-text indexing for CouchDB</description>
-  <inceptionYear>2022</inceptionYear>
-
-  <parent>
-    <groupId>org.apache.couchdb.nouveau</groupId>
-    <artifactId>parent</artifactId>
-    <version>1.0-SNAPSHOT</version>
-  </parent>
-
-  <dependencies>
-
-    <!-- Base -->
-    <dependency>
-      <groupId>org.apache.couchdb.nouveau</groupId>
-      <artifactId>base</artifactId>
-      <version>${project.version}</version>
-    </dependency>
-
-    <!-- Dropwizard -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-http2</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>com.fasterxml.jackson.module</groupId>
-      <artifactId>jackson-module-afterburner</artifactId>
-    </dependency>
-
-    <!-- Dropwizard metrics -->
-    <dependency>
-        <groupId>io.dropwizard.metrics</groupId>
-        <artifactId>metrics-core</artifactId>
-    </dependency>
-    <dependency>
-      <groupId>io.dropwizard.metrics</groupId>
-      <artifactId>metrics-jersey2</artifactId>
-    </dependency>
-
-    <!-- Test -->
-    <dependency>
-      <groupId>io.dropwizard</groupId>
-      <artifactId>dropwizard-testing</artifactId>
-      <scope>test</scope>
-      <exclusions>
-        <exclusion>
-          <groupId>junit</groupId>
-          <artifactId>junit</artifactId>
-        </exclusion>
-      </exclusions>
-    </dependency>
-    <dependency>
-	  <groupId>org.junit.jupiter</groupId>
-	  <artifactId>junit-jupiter</artifactId>
-	  <scope>test</scope>
-	</dependency>
-    <dependency>
-      <groupId>org.junit.jupiter</groupId>
-      <artifactId>junit-jupiter-engine</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.assertj</groupId>
-      <artifactId>assertj-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-    <dependency>
-      <groupId>org.mockito</groupId>
-      <artifactId>mockito-core</artifactId>
-      <scope>test</scope>
-    </dependency>
-  </dependencies>
-
-  <build>
-    <defaultGoal>package</defaultGoal>
-    <plugins>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-release-plugin</artifactId>
-        <version>3.0.0-M5</version>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-jar-plugin</artifactId>
-        <version>2.4</version>
-        <configuration>
-          <archive>
-            <manifest>
-              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
-            </manifest>
-          </archive>
-        </configuration>
-      </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-		<artifactId>maven-surefire-plugin</artifactId>
-	  </plugin>
-
-      <plugin>
-        <groupId>org.apache.maven.plugins</groupId>
-        <artifactId>maven-javadoc-plugin</artifactId>
-        <version>3.4.0</version>
-      </plugin>
-
-    </plugins>
-  </build>
-
-</project>
diff --git a/nouveau/server/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java
similarity index 63%
rename from nouveau/server/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java
index 82bbbdc7c..d553d72b3 100644
--- a/nouveau/server/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java
+++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplication.java
@@ -13,23 +13,24 @@
 
 package org.apache.couchdb.nouveau;
 
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.util.ServiceLoader;
+import java.util.concurrent.ForkJoinPool;
 
 import org.apache.couchdb.nouveau.core.IndexManager;
 import org.apache.couchdb.nouveau.core.UpdatesOutOfOrderExceptionMapper;
+import org.apache.couchdb.nouveau.lucene9.core.Lucene9Module;
+import org.apache.couchdb.nouveau.lucene9.core.ParallelSearcherFactory;
+import org.apache.couchdb.nouveau.lucene9.health.AnalyzeHealthCheck;
+import org.apache.couchdb.nouveau.lucene9.health.IndexHealthCheck;
+import org.apache.couchdb.nouveau.lucene9.resources.AnalyzeResource;
+import org.apache.couchdb.nouveau.lucene9.resources.IndexResource;
 import org.apache.couchdb.nouveau.tasks.CloseAllIndexesTask;
+import org.apache.lucene.search.SearcherFactory;
 
 import io.dropwizard.core.Application;
-import io.dropwizard.core.setup.Bootstrap;
 import io.dropwizard.core.setup.Environment;
 
 public class NouveauApplication extends Application<NouveauApplicationConfiguration> {
 
-    private IndexManager indexManager;
-
     public static void main(String[] args) throws Exception {
         new NouveauApplication().run(args);
     }
@@ -39,34 +40,12 @@ public class NouveauApplication extends Application<NouveauApplicationConfigurat
         return "Nouveau";
     }
 
-    @Override
-    public void initialize(Bootstrap<NouveauApplicationConfiguration> bootstrap) {
-        indexManager = new IndexManager();
-
-        // Find Lucene bundles
-        for (String name : System.getProperties().stringPropertyNames()) {
-            if (name.startsWith("nouveau.bundle.")) {
-                try {
-                    ClassLoader classLoader = URLClassLoader
-                            .newInstance(new URL[] { new URL(System.getProperty(name)) });
-                    final ServiceLoader<LuceneBundle> bundleLoader = ServiceLoader.load(LuceneBundle.class,
-                            classLoader);
-                    for (final LuceneBundle bundle : bundleLoader) {
-                        bundle.setIndexManager(indexManager);
-                        bootstrap.addBundle(bundle);
-                    }
-                } catch (final MalformedURLException e) {
-                    throw new Error(e);
-                }
-            }
-        }
-    }
-
     @Override
     public void run(NouveauApplicationConfiguration configuration, Environment environment) throws Exception {
         environment.jersey().register(new UpdatesOutOfOrderExceptionMapper());
 
         // configure index manager
+        final IndexManager indexManager = new IndexManager();
         indexManager.setCommitIntervalSeconds(configuration.getCommitIntervalSeconds());
         indexManager.setIdleSeconds(configuration.getIdleSeconds());
         indexManager.setLockCount(configuration.getLockCount());
@@ -77,6 +56,21 @@ public class NouveauApplication extends Application<NouveauApplicationConfigurat
         indexManager.setRootDir(configuration.getRootDir());
         environment.lifecycle().manage(indexManager);
 
+        // Serialization classes
+        environment.getObjectMapper().registerModule(new Lucene9Module());
+
+        // AnalyzeResource
+        environment.jersey().register(new AnalyzeResource());
+
+        // IndexResource
+        final SearcherFactory searcherFactory = new ParallelSearcherFactory(ForkJoinPool.commonPool());
+        final IndexResource indexResource = new IndexResource(indexManager, searcherFactory);
+        environment.jersey().register(indexResource);
+
+        // Health checks
+        environment.healthChecks().register("analyze9", new AnalyzeHealthCheck());
+        environment.healthChecks().register("index9", new IndexHealthCheck(indexResource));
+
         // configure tasks
         environment.admin().addTask(new CloseAllIndexesTask(indexManager));
     }
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/NouveauApplicationConfiguration.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplicationConfiguration.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/NouveauApplicationConfiguration.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/NouveauApplicationConfiguration.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/After.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/After.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/After.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/After.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/Range.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Range.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/Range.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/Range.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IOUtils.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IOUtils.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IOUtils.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/IOUtils.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/Index.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/Index.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexClosedException.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexClosedException.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexClosedException.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexClosedException.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexFunction.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexFunction.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexFunction.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexFunction.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexLoader.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexLoader.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexLoader.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexLoader.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderException.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderException.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderException.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderException.java
diff --git a/nouveau/server/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderExceptionMapper.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderExceptionMapper.java
similarity index 100%
rename from nouveau/server/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderExceptionMapper.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/UpdatesOutOfOrderExceptionMapper.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterDeserializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterDeserializer.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterDeserializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterDeserializer.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterSerializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterSerializer.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterSerializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/core/ser/AfterSerializer.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/health/BaseAnalyzeHealthCheck.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/health/BaseAnalyzeHealthCheck.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/health/BaseAnalyzeHealthCheck.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/health/BaseAnalyzeHealthCheck.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/health/BaseIndexHealthCheck.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/health/BaseIndexHealthCheck.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/health/BaseIndexHealthCheck.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/health/BaseIndexHealthCheck.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldDeserializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldDeserializer.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldDeserializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldDeserializer.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldSerializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldSerializer.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldSerializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/IndexableFieldSerializer.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactory.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactory.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactory.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactory.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Index.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Index.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Index.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Index.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Module.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Module.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Module.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9Module.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9QueryParser.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9QueryParser.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9QueryParser.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9QueryParser.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/NumericRangeQueryProcessor.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/NumericRangeQueryProcessor.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/NumericRangeQueryProcessor.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/NumericRangeQueryProcessor.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/ParallelSearcherFactory.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/ParallelSearcherFactory.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/ParallelSearcherFactory.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/ParallelSearcherFactory.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QueryDeserializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QueryDeserializer.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QueryDeserializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QueryDeserializer.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializer.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializer.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/SimpleAsciiFoldingAnalyzer.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/SimpleAsciiFoldingAnalyzer.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/core/SimpleAsciiFoldingAnalyzer.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/core/SimpleAsciiFoldingAnalyzer.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/health/AnalyzeHealthCheck.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/health/AnalyzeHealthCheck.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/health/AnalyzeHealthCheck.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/health/AnalyzeHealthCheck.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/health/IndexHealthCheck.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/health/IndexHealthCheck.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/health/IndexHealthCheck.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/health/IndexHealthCheck.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/AnalyzeResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/AnalyzeResource.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/AnalyzeResource.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/AnalyzeResource.java
diff --git a/nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/IndexResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/IndexResource.java
similarity index 100%
rename from nouveau/lucene9/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/IndexResource.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/resources/IndexResource.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/resources/BaseAnalyzeResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/BaseAnalyzeResource.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/resources/BaseAnalyzeResource.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/resources/BaseAnalyzeResource.java
diff --git a/nouveau/base/src/main/java/org/apache/couchdb/nouveau/resources/BaseIndexResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/BaseIndexResource.java
similarity index 100%
rename from nouveau/base/src/main/java/org/apache/couchdb/nouveau/resources/BaseIndexResource.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/resources/BaseIndexResource.java
diff --git a/nouveau/server/src/main/java/org/apache/couchdb/nouveau/tasks/CloseAllIndexesTask.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/tasks/CloseAllIndexesTask.java
similarity index 100%
rename from nouveau/server/src/main/java/org/apache/couchdb/nouveau/tasks/CloseAllIndexesTask.java
rename to nouveau/src/main/java/org/apache/couchdb/nouveau/tasks/CloseAllIndexesTask.java
diff --git a/nouveau/server/src/main/resources/banner.txt b/nouveau/src/main/resources/banner.txt
similarity index 100%
rename from nouveau/server/src/main/resources/banner.txt
rename to nouveau/src/main/resources/banner.txt
diff --git a/nouveau/base/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
similarity index 100%
rename from nouveau/base/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
rename to nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
diff --git a/nouveau/base/src/test/java/org/apache/couchdb/nouveau/core/BaseIndexTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/core/BaseIndexTest.java
similarity index 100%
rename from nouveau/base/src/test/java/org/apache/couchdb/nouveau/core/BaseIndexTest.java
rename to nouveau/src/test/java/org/apache/couchdb/nouveau/core/BaseIndexTest.java
diff --git a/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactoryTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactoryTest.java
similarity index 100%
rename from nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactoryTest.java
rename to nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9AnalyzerFactoryTest.java
diff --git a/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9IndexTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9IndexTest.java
similarity index 100%
rename from nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9IndexTest.java
rename to nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/Lucene9IndexTest.java
diff --git a/nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializationTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializationTest.java
similarity index 100%
rename from nouveau/lucene9/src/test/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializationTest.java
rename to nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/core/QuerySerializationTest.java
diff --git a/nouveau/base/src/test/resources/fixtures/DocumentUpdateRequest.json b/nouveau/src/test/resources/fixtures/DocumentUpdateRequest.json
similarity index 100%
rename from nouveau/base/src/test/resources/fixtures/DocumentUpdateRequest.json
rename to nouveau/src/test/resources/fixtures/DocumentUpdateRequest.json
diff --git a/nouveau/base/src/test/resources/fixtures/SearchRequest.json b/nouveau/src/test/resources/fixtures/SearchRequest.json
similarity index 100%
rename from nouveau/base/src/test/resources/fixtures/SearchRequest.json
rename to nouveau/src/test/resources/fixtures/SearchRequest.json