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

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

commit ce0b2fd4c820eda6ff5ed01bf37b6177be68dc26
Author: Robert Newson <[email protected]>
AuthorDate: Mon Mar 16 23:03:19 2026 +0000

    use record classes more
---
 .../apache/couchdb/nouveau/api/AnalyzeRequest.java |  31 +---
 .../couchdb/nouveau/api/AnalyzeResponse.java       |  22 +--
 .../couchdb/nouveau/api/DocumentDeleteRequest.java |  44 +-----
 .../couchdb/nouveau/api/DocumentRequest.java       |   2 +-
 .../apache/couchdb/nouveau/api/DocumentUpdate.java |   5 +-
 .../couchdb/nouveau/api/DocumentUpdateRequest.java |  57 ++-----
 .../apache/couchdb/nouveau/api/DoubleField.java    |  37 +----
 .../apache/couchdb/nouveau/api/DoubleRange.java    |  26 +++-
 .../java/org/apache/couchdb/nouveau/api/Field.java |  17 +--
 .../couchdb/nouveau/api/IndexDefinition.java       |  86 +----------
 .../org/apache/couchdb/nouveau/api/IndexInfo.java  |  52 +------
 .../couchdb/nouveau/api/IndexInfoRequest.java      |  53 ++-----
 .../java/org/apache/couchdb/nouveau/api/Ok.java    |   9 +-
 .../java/org/apache/couchdb/nouveau/api/Range.java | 130 ----------------
 .../org/apache/couchdb/nouveau/api/SearchHit.java  |  42 +-----
 .../apache/couchdb/nouveau/api/SearchRequest.java  | 163 +++------------------
 .../couchdb/nouveau/api/SearchRequestBuilder.java  |  93 ++++++++++++
 .../apache/couchdb/nouveau/api/SearchResults.java  |  73 +--------
 .../couchdb/nouveau/api/SearchResultsBuilder.java  |  49 +++++++
 .../apache/couchdb/nouveau/api/StoredField.java    |  30 +---
 .../apache/couchdb/nouveau/api/StringField.java    |  37 +----
 .../org/apache/couchdb/nouveau/api/TextField.java  |  37 +----
 .../couchdb/nouveau/api/WelcomeResponse.java       |  17 +--
 .../org/apache/couchdb/nouveau/core/Index.java     |  14 +-
 .../apache/couchdb/nouveau/core/IndexManager.java  |   2 +-
 .../couchdb/nouveau/health/AnalyzeHealthCheck.java |   2 +-
 .../couchdb/nouveau/health/IndexHealthCheck.java   |  14 +-
 .../nouveau/lucene/LuceneAnalyzerFactory.java      |   5 +-
 .../apache/couchdb/nouveau/lucene/LuceneIndex.java |  79 +++++-----
 .../couchdb/nouveau/lucene/LuceneIndexSchema.java  |   8 +-
 .../couchdb/nouveau/resources/AnalyzeResource.java |   5 +-
 .../couchdb/nouveau/resources/IndexResource.java   |  22 ++-
 .../couchdb/nouveau/api/IndexDefinitionTest.java   |   4 +-
 .../couchdb/nouveau/api/SearchRequestTest.java     |  14 +-
 .../couchdb/nouveau/core/IndexManagerTest.java     |  10 +-
 .../couchdb/nouveau/lucene/LuceneIndexTest.java    |  94 ++++++------
 36 files changed, 389 insertions(+), 996 deletions(-)

diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
index 61cbe4244..a97572fca 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeRequest.java
@@ -14,35 +14,6 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotEmpty;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class AnalyzeRequest {
-
-    @NotEmpty
-    private String analyzer;
-
-    @NotEmpty
-    private String text;
-
-    public AnalyzeRequest() {
-        // Jackson deserialization
-    }
-
-    public AnalyzeRequest(final String analyzer, final String text) {
-        this.analyzer = analyzer;
-        this.text = text;
-    }
-
-    @JsonProperty
-    public String getAnalyzer() {
-        return analyzer;
-    }
-
-    @JsonProperty
-    public String getText() {
-        return text;
-    }
-}
+public record AnalyzeRequest(@JsonProperty @NotEmpty String analyzer, 
@JsonProperty @NotEmpty String text) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
index 8687f652a..9de71b69b 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/AnalyzeResponse.java
@@ -14,28 +14,8 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 import java.util.List;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class AnalyzeResponse {
-
-    @NotNull
-    private List<@NotEmpty String> tokens;
-
-    public AnalyzeResponse() {
-        // Jackson deserialization
-    }
-
-    public AnalyzeResponse(List<String> tokens) {
-        this.tokens = tokens;
-    }
-
-    @JsonProperty
-    public List<String> getTokens() {
-        return tokens;
-    }
-}
+public record AnalyzeResponse(@JsonProperty @NotNull List<@NotEmpty String> 
tokens) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
index 36f70bf78..e88ff25c6 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentDeleteRequest.java
@@ -14,49 +14,17 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.Positive;
 import jakarta.validation.constraints.PositiveOrZero;
 
-public final class DocumentDeleteRequest extends DocumentRequest {
-
-    @PositiveOrZero
-    private final long matchSeq;
-
-    @Positive
-    private final long seq;
-
-    private final boolean purge;
-
-    public DocumentDeleteRequest(
-            @JsonProperty("match_seq") final long matchSeq,
-            @JsonProperty("seq") final long seq,
-            @JsonProperty("purge") final boolean purge) {
-        if (matchSeq < 0) {
-            throw new IllegalArgumentException("matchSeq must be 0 or 
greater");
-        }
-
-        if (seq < 1) {
-            throw new IllegalArgumentException("seq must be 1 or greater");
-        }
-        this.matchSeq = matchSeq;
-        this.seq = seq;
-        this.purge = purge;
-    }
-
-    public long getMatchSeq() {
-        return matchSeq;
-    }
-
-    public long getSeq() {
-        return seq;
-    }
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record DocumentDeleteRequest(
+        @PositiveOrZero @JsonProperty long matchSeq, @Positive @JsonProperty 
long seq, @JsonProperty boolean purge)
+        implements DocumentRequest {
 
     public boolean isPurge() {
         return purge;
     }
-
-    @Override
-    public String toString() {
-        return "DocumentDeleteRequest [matchSeq=" + matchSeq + ", seq=" + seq 
+ ", purge=" + purge + "]";
-    }
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentRequest.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentRequest.java
index 7dc8aa269..0cf87d136 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentRequest.java
@@ -21,4 +21,4 @@ import com.fasterxml.jackson.annotation.JsonTypeInfo;
     @JsonSubTypes.Type(value = DocumentDeleteRequest.class, name = "delete"),
     @JsonSubTypes.Type(value = DocumentUpdateRequest.class, name = "update"),
 })
-public abstract class DocumentRequest {}
+public interface DocumentRequest {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdate.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdate.java
index 141860f3a..0383a8378 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdate.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdate.java
@@ -14,5 +14,8 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
 
-public record DocumentUpdate(@JsonProperty("doc_id") String docId, 
@JsonProperty("update") DocumentRequest request) {}
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record DocumentUpdate(@JsonProperty String docId, @JsonProperty 
DocumentRequest update) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
index 9cbed3ac0..f82de05c6 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DocumentUpdateRequest.java
@@ -14,60 +14,21 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import jakarta.validation.Valid;
-import jakarta.validation.constraints.NotEmpty;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.Positive;
 import jakarta.validation.constraints.PositiveOrZero;
 import java.util.Collection;
 
-public final class DocumentUpdateRequest extends DocumentRequest {
-
-    @PositiveOrZero
-    private final long matchSeq;
-
-    @Positive
-    private final long seq;
-
-    private final String partition;
-
-    @NotEmpty
-    @Valid
-    private final Collection<Field> fields;
-
-    public DocumentUpdateRequest(
-            @JsonProperty("match_seq") final long matchSeq,
-            @JsonProperty("seq") final long seq,
-            @JsonProperty("partition") final String partition,
-            @JsonProperty("fields") final Collection<Field> fields) {
-        this.matchSeq = matchSeq;
-        this.seq = seq;
-        this.partition = partition;
-        this.fields = fields;
-    }
-
-    public long getMatchSeq() {
-        return matchSeq;
-    }
-
-    public long getSeq() {
-        return seq;
-    }
-
-    public String getPartition() {
-        return partition;
-    }
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record DocumentUpdateRequest(
+        @PositiveOrZero @JsonProperty long matchSeq,
+        @Positive @JsonProperty long seq,
+        @JsonProperty String partition,
+        @JsonProperty Collection<Field> fields)
+        implements DocumentRequest {
 
     public boolean hasPartition() {
         return partition != null;
     }
-
-    public Collection<Field> getFields() {
-        return fields;
-    }
-
-    @Override
-    public String toString() {
-        return "DocumentUpdateRequest [matchSeq=" + matchSeq + ", seq=" + seq 
+ ", partition=" + partition + ", fields="
-                + fields + "]";
-    }
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleField.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleField.java
index 79a968786..e1a38d662 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleField.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleField.java
@@ -14,39 +14,6 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
-import jakarta.validation.constraints.NotNull;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class DoubleField extends Field {
-
-    @NotNull
-    private final Double value;
-
-    private final boolean store;
-
-    public DoubleField(
-            @JsonProperty("name") final String name,
-            @JsonProperty("value") final Double value,
-            @JsonProperty("store") final boolean store) {
-        super(name);
-        this.value = value;
-        this.store = store;
-    }
-
-    @JsonProperty
-    public Double getValue() {
-        return value;
-    }
-
-    @JsonProperty
-    public boolean isStore() {
-        return store;
-    }
-
-    @Override
-    public String toString() {
-        return "DoubleField [name=" + name + ", value=" + value + ", store=" + 
store + "]";
-    }
-}
+public record DoubleField(@JsonProperty String name, @JsonProperty double 
value, @JsonProperty boolean store)
+        implements Field {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
index b1a48a25a..cdc67df52 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/DoubleRange.java
@@ -13,15 +13,33 @@
 
 package org.apache.couchdb.nouveau.api;
 
+import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import jakarta.validation.constraints.NotEmpty;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class DoubleRange extends Range<Double> {
+public record DoubleRange(
+        @JsonProperty @NotEmpty String label,
+        @JsonProperty Double min,
+        @JsonProperty Boolean minInclusive,
+        @JsonProperty Double max,
+        @JsonProperty Boolean maxInclusive) {
 
-    public DoubleRange() {}
+    public DoubleRange {
+        if (minInclusive == null) {
+            minInclusive = true;
+        }
+        if (maxInclusive == null) {
+            maxInclusive = true;
+        }
+    }
+
+    public boolean isMinInclusive() {
+        return minInclusive;
+    }
 
-    public DoubleRange(String name, Double min, boolean minInclusive, Double 
max, boolean maxInclusive) {
-        super(name, min, minInclusive, max, maxInclusive);
+    public boolean isMaxInclusive() {
+        return maxInclusive;
     }
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Field.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Field.java
index 8b8f109d2..7d00675f8 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Field.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Field.java
@@ -13,14 +13,10 @@
 
 package org.apache.couchdb.nouveau.api;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.annotation.JsonSubTypes;
 import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.Pattern;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
 @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, 
property = "@type")
 @JsonSubTypes({
     @JsonSubTypes.Type(value = DoubleField.class, name = "double"),
@@ -28,17 +24,8 @@ import jakarta.validation.constraints.Pattern;
     @JsonSubTypes.Type(value = StringField.class, name = "string"),
     @JsonSubTypes.Type(value = TextField.class, name = "text"),
 })
-public abstract class Field {
+public interface Field {
 
     @Pattern(regexp = "^\\$?[a-zA-Z][a-zA-Z0-9_]*$")
-    protected final String name;
-
-    protected Field(final String name) {
-        this.name = name;
-    }
-
-    @JsonProperty
-    public String getName() {
-        return name;
-    }
+    String name();
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
index 11e14082b..29ec964b9 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexDefinition.java
@@ -23,34 +23,18 @@ import jakarta.validation.constraints.NotEmpty;
 import java.util.Map;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class IndexDefinition {
+public record IndexDefinition(
+        @Min(LEGACY_LUCENE_VERSION) @Max(LATEST_LUCENE_VERSION) @JsonProperty 
Integer luceneVersion,
+        @NotEmpty @JsonProperty String defaultAnalyzer,
+        @JsonProperty Map<@NotEmpty String, @NotEmpty String> fieldAnalyzers) {
 
     public static final int LEGACY_LUCENE_VERSION = 9;
     public static final int LATEST_LUCENE_VERSION = 10;
 
-    @Min(LEGACY_LUCENE_VERSION)
-    @Max(LATEST_LUCENE_VERSION)
-    private int luceneVersion = LEGACY_LUCENE_VERSION; // Legacy version if 
not set.
-
-    @NotEmpty
-    private String defaultAnalyzer;
-
-    private Map<@NotEmpty String, @NotEmpty String> fieldAnalyzers;
-
-    public IndexDefinition() {
-        // Jackson deserialization
-    }
-
-    public IndexDefinition(
-            final int luceneVersion, final String defaultAnalyzer, final 
Map<String, String> fieldAnalyzers) {
-        this.luceneVersion = luceneVersion;
-        this.defaultAnalyzer = defaultAnalyzer;
-        this.fieldAnalyzers = fieldAnalyzers;
-    }
-
-    @JsonProperty
-    public int getLuceneVersion() {
-        return luceneVersion;
+    public IndexDefinition {
+        if (luceneVersion == null) {
+            luceneVersion = LEGACY_LUCENE_VERSION;
+        }
     }
 
     @JsonIgnore
@@ -58,61 +42,7 @@ public class IndexDefinition {
         return luceneVersion == LATEST_LUCENE_VERSION;
     }
 
-    public void setLuceneVersion(int luceneVersion) {
-        this.luceneVersion = luceneVersion;
-    }
-
-    @JsonProperty
-    public String getDefaultAnalyzer() {
-        return defaultAnalyzer;
-    }
-
-    public void setDefaultAnalyzer(String defaultAnalyzer) {
-        this.defaultAnalyzer = defaultAnalyzer;
-    }
-
-    @JsonProperty
-    public Map<String, String> getFieldAnalyzers() {
-        return fieldAnalyzers;
-    }
-
-    public void setFieldAnalyzers(Map<String, String> fieldAnalyzers) {
-        this.fieldAnalyzers = fieldAnalyzers;
-    }
-
     public boolean hasFieldAnalyzers() {
         return fieldAnalyzers != null && !fieldAnalyzers.isEmpty();
     }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + luceneVersion;
-        result = prime * result + ((defaultAnalyzer == null) ? 0 : 
defaultAnalyzer.hashCode());
-        result = prime * result + ((fieldAnalyzers == null) ? 0 : 
fieldAnalyzers.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        IndexDefinition other = (IndexDefinition) obj;
-        if (luceneVersion != other.luceneVersion) return false;
-        if (defaultAnalyzer == null) {
-            if (other.defaultAnalyzer != null) return false;
-        } else if (!defaultAnalyzer.equals(other.defaultAnalyzer)) return 
false;
-        if (fieldAnalyzers == null) {
-            if (other.fieldAnalyzers != null) return false;
-        } else if (!fieldAnalyzers.equals(other.fieldAnalyzers)) return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "IndexDefinition [luceneVersion=" + luceneVersion + ", 
defaultAnalyzer=" + defaultAnalyzer
-                + ", fieldAnalyzers=" + fieldAnalyzers + "]";
-    }
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
index 9958c7780..9c3f0fc93 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
@@ -19,50 +19,8 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.PositiveOrZero;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public final class IndexInfo {
-
-    @PositiveOrZero
-    private final long updateSeq;
-
-    @PositiveOrZero
-    private final long purgeSeq;
-
-    @PositiveOrZero
-    private final int numDocs;
-
-    @PositiveOrZero
-    private final long diskSize;
-
-    public IndexInfo(
-            @JsonProperty("update_seq") final long updateSeq,
-            @JsonProperty("purge_seq") final long purgeSeq,
-            @JsonProperty("num_docs") final int numDocs,
-            @JsonProperty("disk_size") final long diskSize) {
-        this.updateSeq = updateSeq;
-        this.purgeSeq = purgeSeq;
-        this.numDocs = numDocs;
-        this.diskSize = diskSize;
-    }
-
-    public int getNumDocs() {
-        return numDocs;
-    }
-
-    public long getDiskSize() {
-        return diskSize;
-    }
-
-    public long getUpdateSeq() {
-        return updateSeq;
-    }
-
-    public long getPurgeSeq() {
-        return purgeSeq;
-    }
-
-    @Override
-    public String toString() {
-        return "IndexInfo [updateSeq=" + updateSeq + ", purgeSeq=" + purgeSeq 
+ ", numDocs=" + numDocs + ", diskSize="
-                + diskSize + "]";
-    }
-}
+public record IndexInfo(
+        @JsonProperty @PositiveOrZero long updateSeq,
+        @JsonProperty @PositiveOrZero long purgeSeq,
+        @JsonProperty @PositiveOrZero int numDocs,
+        @JsonProperty @PositiveOrZero long diskSize) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoRequest.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoRequest.java
index cc008231c..01804717c 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoRequest.java
@@ -14,49 +14,14 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import jakarta.validation.constraints.Positive;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import jakarta.validation.constraints.PositiveOrZero;
 import java.util.OptionalLong;
 
-public final class IndexInfoRequest {
-
-    private final OptionalLong matchUpdateSeq;
-
-    private final OptionalLong updateSeq;
-
-    private final OptionalLong matchPurgeSeq;
-
-    private final OptionalLong purgeSeq;
-
-    public IndexInfoRequest(
-            @JsonProperty("match_update_seq") @Positive final OptionalLong 
matchUpdateSeq,
-            @JsonProperty("update_seq") @Positive final OptionalLong updateSeq,
-            @JsonProperty("match_purge_seq") @Positive final OptionalLong 
matchPurgeSeq,
-            @JsonProperty("purge_seq") @Positive final OptionalLong purgeSeq) {
-        this.matchUpdateSeq = matchUpdateSeq;
-        this.updateSeq = updateSeq;
-        this.matchPurgeSeq = matchPurgeSeq;
-        this.purgeSeq = purgeSeq;
-    }
-
-    public OptionalLong getMatchUpdateSeq() {
-        return matchUpdateSeq;
-    }
-
-    public OptionalLong getUpdateSeq() {
-        return updateSeq;
-    }
-
-    public OptionalLong getMatchPurgeSeq() {
-        return matchPurgeSeq;
-    }
-
-    public OptionalLong getPurgeSeq() {
-        return purgeSeq;
-    }
-
-    @Override
-    public String toString() {
-        return "IndexInfoRequest [matchUpdateSeq=" + matchUpdateSeq + ", 
updateSeq=" + updateSeq + ", matchPurgeSeq="
-                + matchPurgeSeq + ", purgeSeq=" + purgeSeq + "]";
-    }
-}
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record IndexInfoRequest(
+        @JsonProperty @PositiveOrZero OptionalLong matchUpdateSeq,
+        @JsonProperty @PositiveOrZero OptionalLong updateSeq,
+        @JsonProperty @PositiveOrZero OptionalLong matchPurgeSeq,
+        @JsonProperty @PositiveOrZero OptionalLong purgeSeq) {}
diff --git a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Ok.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Ok.java
index b393e1978..bcc37de46 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Ok.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Ok.java
@@ -15,12 +15,7 @@ package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 
-public class Ok {
+public record Ok(@JsonProperty boolean ok) {
 
-    public static final Ok INSTANCE = new Ok();
-
-    @JsonProperty
-    public boolean ok() {
-        return true;
-    }
+    public static final Ok INSTANCE = new Ok(true);
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Range.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Range.java
deleted file mode 100644
index 34689f1aa..000000000
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/Range.java
+++ /dev/null
@@ -1,130 +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.api;
-
-import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
-import jakarta.validation.constraints.NotEmpty;
-import jakarta.validation.constraints.NotNull;
-
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class Range<T> {
-
-    @NotEmpty
-    private String label;
-
-    @NotNull
-    private T min;
-
-    private boolean minInclusive = true;
-
-    @NotNull
-    private T max;
-
-    private boolean maxInclusive = true;
-
-    public Range() {}
-
-    public Range(String label, T min, boolean minInclusive, T max, boolean 
maxInclusive) {
-        this.label = label;
-        this.min = min;
-        this.minInclusive = minInclusive;
-        this.max = max;
-        this.maxInclusive = maxInclusive;
-    }
-
-    @JsonProperty
-    public String getLabel() {
-        return label;
-    }
-
-    public void setLabel(String label) {
-        this.label = label;
-    }
-
-    @JsonProperty
-    public T getMin() {
-        return min;
-    }
-
-    public void setMin(T min) {
-        this.min = min;
-    }
-
-    @JsonProperty("min_inclusive")
-    public boolean isMinInclusive() {
-        return minInclusive;
-    }
-
-    public void setMinInclusive(boolean minInclusive) {
-        this.minInclusive = minInclusive;
-    }
-
-    @JsonProperty
-    public T getMax() {
-        return max;
-    }
-
-    public void setMax(T max) {
-        this.max = max;
-    }
-
-    @JsonProperty("max_inclusive")
-    public boolean isMaxInclusive() {
-        return maxInclusive;
-    }
-
-    public void setMaxInclusive(boolean maxInclusive) {
-        this.maxInclusive = maxInclusive;
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((label == null) ? 0 : label.hashCode());
-        result = prime * result + ((min == null) ? 0 : min.hashCode());
-        result = prime * result + (minInclusive ? 1231 : 1237);
-        result = prime * result + ((max == null) ? 0 : max.hashCode());
-        result = prime * result + (maxInclusive ? 1231 : 1237);
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) return true;
-        if (obj == null) return false;
-        if (getClass() != obj.getClass()) return false;
-        Range<?> other = (Range<?>) obj;
-        if (label == null) {
-            if (other.label != null) return false;
-        } else if (!label.equals(other.label)) return false;
-        if (min == null) {
-            if (other.min != null) return false;
-        } else if (!min.equals(other.min)) return false;
-        if (minInclusive != other.minInclusive) return false;
-        if (max == null) {
-            if (other.max != null) return false;
-        } else if (!max.equals(other.max)) return false;
-        if (maxInclusive != other.maxInclusive) return false;
-        return true;
-    }
-
-    @Override
-    public String toString() {
-        return "Range [label=" + label + ", min=" + min + ", minInclusive=" + 
minInclusive + ", max=" + max
-                + ", maxInclusive=" + maxInclusive + "]";
-    }
-}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
index 2e575fef1..98296bd87 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchHit.java
@@ -13,48 +13,10 @@
 
 package org.apache.couchdb.nouveau.api;
 
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 import java.util.Collection;
-import java.util.Objects;
 import org.apache.couchdb.nouveau.core.ser.PrimitiveWrapper;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class SearchHit {
-
-    @NotEmpty
-    private String id;
-
-    @NotNull
-    private PrimitiveWrapper<?>[] order;
-
-    @NotNull
-    private Collection<@NotNull StoredField> fields;
-
-    public SearchHit() {}
-
-    public SearchHit(final String id, final PrimitiveWrapper<?>[] order, final 
Collection<StoredField> fields) {
-        this.id = id;
-        this.order = Objects.requireNonNull(order);
-        this.fields = Objects.requireNonNull(fields);
-    }
-
-    public String getId() {
-        return id;
-    }
-
-    public PrimitiveWrapper<?>[] getOrder() {
-        return order;
-    }
-
-    public Collection<StoredField> getFields() {
-        return fields;
-    }
-
-    @Override
-    public String toString() {
-        return "SearchHit [id=" + id + ", order=" + order + ", fields=" + 
fields + "]";
-    }
-}
+public record SearchHit(
+        @NotEmpty String id, @NotNull PrimitiveWrapper<?>[] order, @NotNull 
Collection<StoredField> fields) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
index 8e4ebbf8d..1cb665ffa 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java
@@ -16,8 +16,6 @@ package org.apache.couchdb.nouveau.api;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import com.fasterxml.jackson.databind.PropertyNamingStrategies;
 import com.fasterxml.jackson.databind.annotation.JsonNaming;
-import jakarta.validation.constraints.Max;
-import jakarta.validation.constraints.Min;
 import jakarta.validation.constraints.NotEmpty;
 import jakarta.validation.constraints.NotNull;
 import jakarta.validation.constraints.Positive;
@@ -28,159 +26,44 @@ import java.util.Map;
 import org.apache.couchdb.nouveau.core.ser.PrimitiveWrapper;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class SearchRequest {
-
-    @NotNull
-    private String query;
-
-    @PositiveOrZero
-    private long minUpdateSeq;
-
-    @PositiveOrZero
-    private long minPurgeSeq;
-
-    private Locale locale;
-
-    private String partition;
-
-    @Positive
-    private int limit = 25;
-
-    private List<@NotEmpty String> sort;
-
-    private List<@NotEmpty String> counts;
-
-    private Map<@NotEmpty String, List<@NotNull DoubleRange>> ranges;
-
-    private PrimitiveWrapper<?>[] after;
-
-    @Min(1)
-    @Max(1000)
-    private int topN = 10;
-
-    public SearchRequest() {
-        // Jackson deserialization
-    }
-
-    public void setQuery(final String query) {
-        this.query = query;
-    }
-
-    @JsonProperty
-    public String getQuery() {
-        return query;
-    }
-
-    public void setMinUpdateSeq(final long minUpdateSeq) {
-        this.minUpdateSeq = minUpdateSeq;
-    }
-
-    @JsonProperty
-    public long getMinUpdateSeq() {
-        return minUpdateSeq;
-    }
-
-    public void setMinPurgeSeq(final long minPurgeSeq) {
-        this.minPurgeSeq = minPurgeSeq;
-    }
-
-    @JsonProperty
-    public long getMinPurgeSeq() {
-        return minPurgeSeq;
-    }
-
-    public void setLocale(final Locale locale) {
-        this.locale = locale;
-    }
-
-    @JsonProperty
-    public Locale getLocale() {
-        return locale;
-    }
-
-    public void setPartition(final String partition) {
-        this.partition = partition;
-    }
-
-    @JsonProperty
-    public String getPartition() {
-        return partition;
+public record SearchRequest(
+        @JsonProperty @NotNull String query,
+        @JsonProperty @PositiveOrZero long minUpdateSeq,
+        @JsonProperty @PositiveOrZero long minPurgeSeq,
+        @JsonProperty Locale locale,
+        @JsonProperty String partition,
+        @JsonProperty @Positive Integer limit,
+        @JsonProperty List<@NotEmpty String> sort,
+        @JsonProperty List<@NotEmpty String> counts,
+        @JsonProperty Map<@NotEmpty String, List<@NotNull DoubleRange>> ranges,
+        @JsonProperty PrimitiveWrapper<?>[] after,
+        @JsonProperty Integer topN) {
+
+    public SearchRequest {
+        if (topN == null) {
+            topN = 10;
+        }
+        if (limit == null) {
+            limit = 25;
+        }
+        if (locale == null) {
+            locale = Locale.getDefault();
+        }
     }
 
     public boolean hasPartition() {
         return partition != null;
     }
 
-    public void setLimit(final int limit) {
-        this.limit = limit;
-    }
-
-    @JsonProperty
-    public int getLimit() {
-        return limit;
-    }
-
     public boolean hasSort() {
         return sort != null;
     }
 
-    @JsonProperty
-    public List<String> getSort() {
-        return sort;
-    }
-
-    public void setSort(List<String> sort) {
-        this.sort = sort;
-    }
-
     public boolean hasCounts() {
         return counts != null;
     }
 
-    public void setCounts(final List<String> counts) {
-        this.counts = counts;
-    }
-
-    @JsonProperty
-    public List<String> getCounts() {
-        return counts;
-    }
-
     public boolean hasRanges() {
         return ranges != null;
     }
-
-    public void setRanges(final Map<String, List<DoubleRange>> ranges) {
-        this.ranges = ranges;
-    }
-
-    @JsonProperty
-    public Map<String, List<DoubleRange>> getRanges() {
-        return ranges;
-    }
-
-    public void setTopN(final int topN) {
-        this.topN = topN;
-    }
-
-    @JsonProperty
-    public int getTopN() {
-        return topN;
-    }
-
-    public void setAfter(final PrimitiveWrapper<?>[] after) {
-        this.after = after;
-    }
-
-    @JsonProperty
-    public PrimitiveWrapper<?>[] getAfter() {
-        return after;
-    }
-
-    @Override
-    public String toString() {
-        return "SearchRequest [query=" + query + ", min_update_seq=" + 
minUpdateSeq + ", min_purge_seq=" + minPurgeSeq
-                + ", locale=" + locale + ", sort=" + sort + ", limit=" + limit 
+ ", after=" + after + ", counts="
-                + counts + ", ranges=" + ranges + "]";
-    }
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequestBuilder.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequestBuilder.java
new file mode 100644
index 000000000..b30827862
--- /dev/null
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequestBuilder.java
@@ -0,0 +1,93 @@
+//
+// 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.api;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.couchdb.nouveau.core.ser.PrimitiveWrapper;
+
+public final class SearchRequestBuilder {
+
+    private String query;
+
+    private long minUpdateSeq;
+
+    private long minPurgeSeq;
+
+    private Locale locale;
+
+    private String partition;
+
+    private Integer limit;
+
+    private List<String> sort;
+
+    private List<String> counts;
+
+    private Map<String, List<DoubleRange>> ranges;
+
+    private PrimitiveWrapper<?>[] after;
+
+    private Integer topN;
+
+    public void setQuery(final String query) {
+        this.query = query;
+    }
+
+    public void setMinUpdateSeq(final long minUpdateSeq) {
+        this.minUpdateSeq = minUpdateSeq;
+    }
+
+    public void setMinPurgeSeq(final long minPurgeSeq) {
+        this.minPurgeSeq = minPurgeSeq;
+    }
+
+    public void setPartition(final String partition) {
+        this.partition = partition;
+    }
+
+    public void setLimit(final Integer limit) {
+        this.limit = limit;
+    }
+
+    public void setSort(List<String> sort) {
+        this.sort = sort;
+    }
+
+    public boolean hasCounts() {
+        return counts != null;
+    }
+
+    public void setCounts(final List<String> counts) {
+        this.counts = counts;
+    }
+
+    public void setRanges(final Map<String, List<DoubleRange>> ranges) {
+        this.ranges = ranges;
+    }
+
+    public void setTopN(final Integer topN) {
+        this.topN = topN;
+    }
+
+    public void setAfter(final PrimitiveWrapper<?>[] after) {
+        this.after = after;
+    }
+
+    public SearchRequest build() {
+        return new SearchRequest(
+                query, minUpdateSeq, minPurgeSeq, locale, partition, limit, 
sort, counts, ranges, after, topN);
+    }
+}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
index a273e6ef2..798b98863 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResults.java
@@ -23,70 +23,9 @@ import java.util.Map;
 import org.apache.lucene.search.TotalHits.Relation;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class SearchResults {
-
-    @PositiveOrZero
-    private long totalHits;
-
-    @NotNull
-    private Relation totalHitsRelation;
-
-    @NotNull
-    private List<@NotNull SearchHit> hits;
-
-    private Map<@NotNull String, Map<@NotNull String, Number>> counts;
-
-    private Map<@NotNull String, Map<@NotNull String, Number>> ranges;
-
-    public SearchResults() {}
-
-    public void setTotalHits(final long totalHits) {
-        this.totalHits = totalHits;
-    }
-
-    @JsonProperty
-    public long getTotalHits() {
-        return totalHits;
-    }
-
-    public Relation getTotalHitsRelation() {
-        return totalHitsRelation;
-    }
-
-    public void setTotalHitsRelation(Relation relation) {
-        this.totalHitsRelation = relation;
-    }
-
-    public void setHits(final List<SearchHit> hits) {
-        this.hits = hits;
-    }
-
-    @JsonProperty
-    public List<SearchHit> getHits() {
-        return hits;
-    }
-
-    public void setCounts(final Map<String, Map<String, Number>> counts) {
-        this.counts = counts;
-    }
-
-    @JsonProperty
-    public Map<String, Map<String, Number>> getCounts() {
-        return counts;
-    }
-
-    public void setRanges(final Map<String, Map<String, Number>> ranges) {
-        this.ranges = ranges;
-    }
-
-    @JsonProperty
-    public Map<String, Map<String, Number>> getRanges() {
-        return ranges;
-    }
-
-    @Override
-    public String toString() {
-        return "SearchResults [hits=" + hits + ", totalHits=" + totalHits + ", 
counts=" + counts + ", ranges=" + ranges
-                + "]";
-    }
-}
+public record SearchResults(
+        @JsonProperty @PositiveOrZero long totalHits,
+        @JsonProperty @NotNull Relation totalHitsRelation,
+        @JsonProperty @NotNull List<@NotNull SearchHit> hits,
+        @JsonProperty Map<@NotNull String, Map<@NotNull String, Number>> 
counts,
+        @JsonProperty Map<@NotNull String, Map<@NotNull String, Number>> 
ranges) {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResultsBuilder.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResultsBuilder.java
new file mode 100644
index 000000000..2fcacbedc
--- /dev/null
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchResultsBuilder.java
@@ -0,0 +1,49 @@
+//
+// 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.api;
+
+import java.util.List;
+import java.util.Map;
+import org.apache.lucene.search.TotalHits;
+
+public final class SearchResultsBuilder {
+
+    private TotalHits totalHits;
+
+    private List<SearchHit> hits;
+
+    private Map<String, Map<String, Number>> counts;
+
+    private Map<String, Map<String, Number>> ranges;
+
+    public void setTotalHits(final TotalHits totalHits) {
+        this.totalHits = totalHits;
+    }
+
+    public void setHits(final List<SearchHit> hits) {
+        this.hits = hits;
+    }
+
+    public void setCounts(final Map<String, Map<String, Number>> counts) {
+        this.counts = counts;
+    }
+
+    public void setRanges(final Map<String, Map<String, Number>> ranges) {
+        this.ranges = ranges;
+    }
+
+    public SearchResults build() {
+        return new SearchResults(totalHits.value(), totalHits.relation(), 
hits, counts, ranges);
+    }
+}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StoredField.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StoredField.java
index 697fc4f93..a3f8d453c 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StoredField.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StoredField.java
@@ -14,32 +14,10 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import io.swagger.v3.oas.annotations.media.Schema;
 import jakarta.validation.constraints.NotNull;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public final class StoredField extends Field {
-
-    @NotNull
-    @Schema(oneOf = {String.class, Double.class, byte[].class})
-    private final Object value;
-
-    public StoredField(@JsonProperty("name") final String name, 
@JsonProperty("value") final Object value) {
-        super(name);
-        if (!(value instanceof String || value instanceof Number || value 
instanceof byte[])) {
-            throw new IllegalArgumentException(value + " must be a string, 
number or byte array");
-        }
-        this.value = value;
-    }
-
-    public Object getValue() {
-        return value;
-    }
-
-    @Override
-    public String toString() {
-        return "StoredField [name=" + name + ", value=" + value + "]";
-    }
-}
+public record StoredField(
+        @JsonProperty String name,
+        @JsonProperty @NotNull @Schema(oneOf = {String.class, Double.class, 
byte[].class}) Object value)
+        implements Field {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StringField.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StringField.java
index 32f38063c..a5e665901 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StringField.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StringField.java
@@ -14,40 +14,7 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotNull;
-import java.util.Objects;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public final class StringField extends Field {
-
-    @NotNull
-    private final String value;
-
-    private final boolean store;
-
-    public StringField(
-            @JsonProperty("name") final String name,
-            @JsonProperty("value") final String value,
-            @JsonProperty("store") final boolean store) {
-        super(name);
-        this.value = Objects.requireNonNull(value);
-        this.store = store;
-    }
-
-    @JsonProperty
-    public String getValue() {
-        return value;
-    }
-
-    @JsonProperty
-    public boolean isStore() {
-        return store;
-    }
-
-    @Override
-    public String toString() {
-        return "StringField [name=" + name + ", value=" + value + ", store=" + 
store + "]";
-    }
-}
+public record StringField(@JsonProperty String name, @JsonProperty @NotNull 
String value, @JsonProperty boolean store)
+        implements Field {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/TextField.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/TextField.java
index a82231a2d..1f281f40a 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/TextField.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/TextField.java
@@ -14,40 +14,7 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotNull;
-import java.util.Objects;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public final class TextField extends Field {
-
-    @NotNull
-    private final String value;
-
-    private final boolean store;
-
-    public TextField(
-            @JsonProperty("name") final String name,
-            @JsonProperty("value") final String value,
-            @JsonProperty("store") final boolean store) {
-        super(name);
-        this.value = Objects.requireNonNull(value);
-        this.store = store;
-    }
-
-    @JsonProperty
-    public String getValue() {
-        return value;
-    }
-
-    @JsonProperty
-    public boolean isStore() {
-        return store;
-    }
-
-    @Override
-    public String toString() {
-        return "TextField [name=" + name + ", value=" + value + ", store=" + 
store + "]";
-    }
-}
+public record TextField(@JsonProperty String name, @JsonProperty @NotNull 
String value, @JsonProperty boolean store)
+        implements Field {}
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/WelcomeResponse.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/WelcomeResponse.java
index 6043da4c7..ed8a8f75c 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/WelcomeResponse.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/WelcomeResponse.java
@@ -14,21 +14,8 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.databind.PropertyNamingStrategies;
-import com.fasterxml.jackson.databind.annotation.JsonNaming;
 
-@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public final class WelcomeResponse {
+public record WelcomeResponse(@JsonProperty("supported_lucene_versions") int[] 
supportedLuceneVersions) {
 
-    public static final WelcomeResponse INSTANCE = new WelcomeResponse();
-
-    private final int[] supportedLuceneVersions =
-            new int[] {IndexDefinition.LEGACY_LUCENE_VERSION, 
IndexDefinition.LATEST_LUCENE_VERSION};
-
-    private WelcomeResponse() {}
-
-    @JsonProperty
-    public int[] getSupportedLuceneVersions() {
-        return supportedLuceneVersions;
-    }
+    public static final WelcomeResponse INSTANCE = new WelcomeResponse(new 
int[] {9, 10});
 }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
index 35013bf8d..997e1685a 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/Index.java
@@ -55,29 +55,29 @@ public abstract class Index implements Closeable {
     protected abstract long doDiskSize() throws IOException;
 
     public final synchronized void update(final String docId, final 
DocumentUpdateRequest request) throws IOException {
-        assertUpdateSeqProgress(request.getMatchSeq(), request.getSeq());
+        assertUpdateSeqProgress(request.matchSeq(), request.seq());
         doUpdate(docId, request);
-        incrementUpdateSeq(request.getMatchSeq(), request.getSeq());
+        incrementUpdateSeq(request.matchSeq(), request.seq());
     }
 
     protected abstract void doUpdate(final String docId, final 
DocumentUpdateRequest request) throws IOException;
 
     public final synchronized void delete(final String docId, final 
DocumentDeleteRequest request) throws IOException {
         if (request.isPurge()) {
-            assertPurgeSeqProgress(request.getMatchSeq(), request.getSeq());
+            assertPurgeSeqProgress(request.matchSeq(), request.seq());
             doDelete(docId, request);
-            incrementPurgeSeq(request.getMatchSeq(), request.getSeq());
+            incrementPurgeSeq(request.matchSeq(), request.seq());
         } else {
-            assertUpdateSeqProgress(request.getMatchSeq(), request.getSeq());
+            assertUpdateSeqProgress(request.matchSeq(), request.seq());
             doDelete(docId, request);
-            incrementUpdateSeq(request.getMatchSeq(), request.getSeq());
+            incrementUpdateSeq(request.matchSeq(), request.seq());
         }
     }
 
     protected abstract void doDelete(final String docId, final 
DocumentDeleteRequest request) throws IOException;
 
     public final SearchResults search(final SearchRequest request) throws 
IOException {
-        assertMinSeqs(request.getMinUpdateSeq(), request.getMinPurgeSeq());
+        assertMinSeqs(request.minUpdateSeq(), request.minPurgeSeq());
         return doSearch(request);
     }
 
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
index 4067716ee..a6b38d994 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/core/IndexManager.java
@@ -393,7 +393,7 @@ public final class IndexManager implements Managed {
         final Path path = indexPath(name);
         final IndexDefinition indexDefinition = loadIndexDefinition(name);
         final Analyzer analyzer = 
LuceneAnalyzerFactory.fromDefinition(indexDefinition);
-        final int luceneVersion = indexDefinition.getLuceneVersion();
+        final int luceneVersion = indexDefinition.luceneVersion();
         final Directory dir = new 
DirectIODirectory(FSDirectory.open(path.resolve(Integer.toString(luceneVersion))));
         final IndexWriterConfig config = new IndexWriterConfig(analyzer);
         if (luceneVersion != IndexDefinition.LATEST_LUCENE_VERSION) {
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/AnalyzeHealthCheck.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/AnalyzeHealthCheck.java
index bd7edeaf3..10eaac43e 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/AnalyzeHealthCheck.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/AnalyzeHealthCheck.java
@@ -33,7 +33,7 @@ public final class AnalyzeHealthCheck extends HealthCheck {
         final AnalyzeRequest request = new AnalyzeRequest("standard", "hello 
goodbye");
         final AnalyzeResponse response = analyzeResource.analyzeText(request);
         final List<String> expected = Arrays.asList("hello", "goodbye");
-        final List<String> actual = response.getTokens();
+        final List<String> actual = response.tokens();
         if (expected.equals(actual)) {
             return Result.healthy();
         } else {
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
index 0a70b2b6c..ac8a24b4c 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
@@ -21,7 +21,7 @@ import org.apache.couchdb.nouveau.api.BulkUpdateRequest;
 import org.apache.couchdb.nouveau.api.DocumentUpdate;
 import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
 import org.apache.couchdb.nouveau.api.IndexDefinition;
-import org.apache.couchdb.nouveau.api.SearchRequest;
+import org.apache.couchdb.nouveau.api.SearchRequestBuilder;
 import org.apache.couchdb.nouveau.api.SearchResults;
 import org.apache.couchdb.nouveau.resources.IndexResource;
 
@@ -48,16 +48,16 @@ public final class IndexHealthCheck extends HealthCheck {
                     name,
                     new BulkUpdateRequest(List.of(new DocumentUpdate(
                             "foo", new DocumentUpdateRequest(0, 1, null, 
Collections.emptyList())))));
-            final SearchRequest searchRequest = new SearchRequest();
-            searchRequest.setQuery("_id:foo");
-            searchRequest.setMinUpdateSeq(1);
+            final SearchRequestBuilder searchRequestBuilder = new 
SearchRequestBuilder();
+            searchRequestBuilder.setQuery("_id:foo");
+            searchRequestBuilder.setMinUpdateSeq(1);
 
-            final SearchResults searchResults = 
indexResource.searchIndex(name, searchRequest);
-            if (searchResults.getTotalHits() == 1) {
+            final SearchResults searchResults = 
indexResource.searchIndex(name, searchRequestBuilder.build());
+            if (searchResults.totalHits() == 1) {
                 return Result.healthy();
             } else {
                 return Result.unhealthy(
-                        "Wrong number of search results, expected 1, got %d", 
searchResults.getTotalHits());
+                        "Wrong number of search results, expected 1, got %d", 
searchResults.totalHits());
             }
         } finally {
             indexResource.deletePath(name, null);
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactory.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactory.java
index b95230cec..6553cfd95 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactory.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactory.java
@@ -65,13 +65,12 @@ public final class LuceneAnalyzerFactory {
     private LuceneAnalyzerFactory() {}
 
     public static Analyzer fromDefinition(final IndexDefinition 
indexDefinition) {
-        final Analyzer defaultAnalyzer = 
newAnalyzer(indexDefinition.getDefaultAnalyzer());
+        final Analyzer defaultAnalyzer = 
newAnalyzer(indexDefinition.defaultAnalyzer());
         if (!indexDefinition.hasFieldAnalyzers()) {
             return defaultAnalyzer;
         }
         final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, 
Analyzer>();
-        for (Map.Entry<String, String> entry :
-                indexDefinition.getFieldAnalyzers().entrySet()) {
+        for (Map.Entry<String, String> entry : 
indexDefinition.fieldAnalyzers().entrySet()) {
             fieldAnalyzers.put(entry.getKey(), newAnalyzer(entry.getValue()));
         }
         return new PerFieldAnalyzerWrapper(defaultAnalyzer, fieldAnalyzers);
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
index e62ffdb5a..bb3521e23 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
@@ -27,7 +27,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
@@ -41,6 +40,7 @@ import org.apache.couchdb.nouveau.api.Field;
 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.api.SearchResultsBuilder;
 import org.apache.couchdb.nouveau.api.StoredField;
 import org.apache.couchdb.nouveau.api.StringField;
 import org.apache.couchdb.nouveau.api.TextField;
@@ -144,7 +144,7 @@ public class LuceneIndex extends Index {
     public void doUpdate(final String docId, final DocumentUpdateRequest 
request) throws IOException {
         final Term docIdTerm = docIdTerm(docId);
         final Document doc = toDocument(docId, request);
-        schema.update(request.getFields());
+        schema.update(request.fields());
         writer.updateDocument(docIdTerm, doc);
     }
 
@@ -224,7 +224,7 @@ public class LuceneIndex extends Index {
     private CollectorManager<?, ? extends TopDocs> hitCollector(final 
SearchRequest searchRequest) {
         final Sort sort = toSort(searchRequest);
 
-        final PrimitiveWrapper<?>[] after = searchRequest.getAfter();
+        final PrimitiveWrapper<?>[] after = searchRequest.after();
         final FieldDoc fieldDoc;
         if (after != null) {
             fieldDoc = toFieldDoc(after);
@@ -237,7 +237,7 @@ public class LuceneIndex extends Index {
             fieldDoc = null;
         }
 
-        return new TopFieldCollectorManager(sort, searchRequest.getLimit(), 
fieldDoc, 1000);
+        return new TopFieldCollectorManager(sort, searchRequest.limit(), 
fieldDoc, 1000);
     }
 
     private SortField getLastSortField(final Sort sort) {
@@ -248,15 +248,16 @@ public class LuceneIndex extends Index {
     private SearchResults toSearchResults(
             final SearchRequest searchRequest, final IndexSearcher searcher, 
final Object[] reduces)
             throws IOException {
-        final SearchResults result = new SearchResults();
-        collectHits(searcher, (TopDocs) reduces[0], result);
+        final SearchResultsBuilder builder = new SearchResultsBuilder();
+        collectHits(searcher, (TopDocs) reduces[0], builder);
         if (reduces.length == 2) {
-            collectFacets(searchRequest, searcher, (FacetsCollector) 
reduces[1], result);
+            collectFacets(searchRequest, searcher, (FacetsCollector) 
reduces[1], builder);
         }
-        return result;
+        return builder.build();
     }
 
-    private void collectHits(final IndexSearcher searcher, final TopDocs 
topDocs, final SearchResults searchResults)
+    private void collectHits(
+            final IndexSearcher searcher, final TopDocs topDocs, final 
SearchResultsBuilder searchResultsBuilder)
             throws IOException {
         final List<SearchHit> hits = new 
ArrayList<SearchHit>(topDocs.scoreDocs.length);
         final StoredFields storedFields = searcher.storedFields();
@@ -290,38 +291,37 @@ public class LuceneIndex extends Index {
             hits.add(new SearchHit(doc.get("_id"), after, fields));
         }
 
-        searchResults.setTotalHits(topDocs.totalHits.value());
-        searchResults.setTotalHitsRelation(topDocs.totalHits.relation());
-        searchResults.setHits(hits);
+        searchResultsBuilder.setTotalHits(topDocs.totalHits);
+        searchResultsBuilder.setHits(hits);
     }
 
     private void collectFacets(
             final SearchRequest searchRequest,
             final IndexSearcher searcher,
             final FacetsCollector fc,
-            final SearchResults searchResults)
+            final SearchResultsBuilder searchResultsBuilder)
             throws IOException {
         if (searchRequest.hasCounts()) {
             final Map<String, Map<String, Number>> countsMap = new 
HashMap<String, Map<String, Number>>(
-                    searchRequest.getCounts().size());
-            for (final String field : searchRequest.getCounts()) {
+                    searchRequest.counts().size());
+            for (final String field : searchRequest.counts()) {
                 final StringDocValuesReaderState state =
                         new 
StringDocValuesReaderState(searcher.getIndexReader(), field);
                 final StringValueFacetCounts counts = new 
StringValueFacetCounts(state, fc);
-                countsMap.put(field, collectFacets(counts, 
searchRequest.getTopN(), field));
+                countsMap.put(field, collectFacets(counts, 
searchRequest.topN(), field));
             }
-            searchResults.setCounts(countsMap);
+            searchResultsBuilder.setCounts(countsMap);
         }
 
         if (searchRequest.hasRanges()) {
             final Map<String, Map<String, Number>> rangesMap = new 
HashMap<String, Map<String, Number>>(
-                    searchRequest.getRanges().size());
+                    searchRequest.ranges().size());
             for (final Entry<String, List<DoubleRange>> entry :
-                    searchRequest.getRanges().entrySet()) {
+                    searchRequest.ranges().entrySet()) {
                 final DoubleRangeFacetCounts counts = 
toDoubleRangeFacetCounts(fc, entry.getKey(), entry.getValue());
-                rangesMap.put(entry.getKey(), collectFacets(counts, 
searchRequest.getTopN(), entry.getKey()));
+                rangesMap.put(entry.getKey(), collectFacets(counts, 
searchRequest.topN(), entry.getKey()));
             }
-            searchResults.setRanges(rangesMap);
+            searchResultsBuilder.setRanges(rangesMap);
         }
     }
 
@@ -332,10 +332,10 @@ public class LuceneIndex extends Index {
         for (int i = 0; i < luceneRanges.length; i++) {
             final DoubleRange range = ranges.get(i);
             luceneRanges[i] = new org.apache.lucene.facet.range.DoubleRange(
-                    range.getLabel(),
-                    range.getMin() != null ? range.getMin() : 
Double.NEGATIVE_INFINITY,
+                    range.label(),
+                    range.min() != null ? range.min() : 
Double.NEGATIVE_INFINITY,
                     range.isMinInclusive(),
-                    range.getMax() != null ? range.getMax() : 
Double.POSITIVE_INFINITY,
+                    range.max() != null ? range.max() : 
Double.POSITIVE_INFINITY,
                     range.isMaxInclusive());
         }
         return new DoubleRangeFacetCounts(field, fc, luceneRanges);
@@ -357,7 +357,7 @@ public class LuceneIndex extends Index {
             return DEFAULT_SORT;
         }
 
-        final List<String> sort = new 
ArrayList<String>(searchRequest.getSort());
+        final List<String> sort = new ArrayList<String>(searchRequest.sort());
         final String last = sort.get(sort.size() - 1);
         // Append _id field if not already present.
         switch (last) {
@@ -411,41 +411,41 @@ public class LuceneIndex extends Index {
 
         // partition (optional)
         if (request.hasPartition()) {
-            result.add(new 
org.apache.lucene.document.StringField("_partition", request.getPartition(), 
Store.NO));
+            result.add(new 
org.apache.lucene.document.StringField("_partition", request.partition(), 
Store.NO));
         }
 
         final CharsetDecoder utf8Decoder = 
Charset.forName("UTF-8").newDecoder();
 
-        for (Field field : request.getFields()) {
+        for (Field field : request.fields()) {
             // Underscore-prefix is reserved.
-            if (field.getName().startsWith("_")) {
+            if (field.name().startsWith("_")) {
                 continue;
             }
             if (field instanceof TextField) {
                 var f = (TextField) field;
                 result.add(new org.apache.lucene.document.TextField(
-                        f.getName(), f.getValue(), f.isStore() ? Store.YES : 
Store.NO));
+                        f.name(), f.value(), f.store() ? Store.YES : 
Store.NO));
             } else if (field instanceof StringField) {
                 var f = (StringField) field;
                 result.add(new org.apache.lucene.document.KeywordField(
-                        f.getName(), f.getValue(), f.isStore() ? Store.YES : 
Store.NO));
+                        f.name(), f.value(), f.store() ? Store.YES : 
Store.NO));
             } else if (field instanceof DoubleField) {
                 var f = (DoubleField) field;
                 result.add(new org.apache.lucene.document.DoubleField(
-                        f.getName(), f.getValue(), f.isStore() ? Store.YES : 
Store.NO));
+                        f.name(), f.value(), f.store() ? Store.YES : 
Store.NO));
             } else if (field instanceof StoredField) {
                 var f = (StoredField) field;
-                var val = f.getValue();
+                var val = f.value();
                 if (val instanceof String) {
-                    result.add(new 
org.apache.lucene.document.StoredField(f.getName(), (String) val));
+                    result.add(new 
org.apache.lucene.document.StoredField(f.name(), (String) val));
                 } else if (val instanceof Number) {
-                    result.add(new 
org.apache.lucene.document.StoredField(f.getName(), ((Number) 
val).doubleValue()));
+                    result.add(new 
org.apache.lucene.document.StoredField(f.name(), ((Number) val).doubleValue()));
                 } else if (val instanceof byte[]) {
                     try {
                         final CharBuffer buf = 
utf8Decoder.decode(ByteBuffer.wrap((byte[]) val));
-                        result.add(new 
org.apache.lucene.document.StoredField(f.getName(), buf.toString()));
+                        result.add(new 
org.apache.lucene.document.StoredField(f.name(), buf.toString()));
                     } catch (final CharacterCodingException e) {
-                        result.add(new 
org.apache.lucene.document.StoredField(f.getName(), (byte[]) val));
+                        result.add(new 
org.apache.lucene.document.StoredField(f.name(), (byte[]) val));
                     }
                 } else {
                     throw new WebApplicationException(field + " is not valid", 
Status.BAD_REQUEST);
@@ -518,16 +518,15 @@ public class LuceneIndex extends Index {
     }
 
     private Query parse(final SearchRequest request) {
-        var locale = request.getLocale() != null ? request.getLocale() : 
Locale.getDefault();
-        var pointsConfigMap = schema.toPointsConfigMap(locale);
+        var pointsConfigMap = schema.toPointsConfigMap(request.locale());
         var queryParser = new NouveauQueryParser(analyzer, pointsConfigMap);
 
         Query result;
         try {
-            result = queryParser.parse(request.getQuery(), "default");
+            result = queryParser.parse(request.query(), "default");
             if (request.hasPartition()) {
                 final BooleanQuery.Builder builder = new 
BooleanQuery.Builder();
-                builder.add(new TermQuery(new Term("_partition", 
request.getPartition())), Occur.MUST);
+                builder.add(new TermQuery(new Term("_partition", 
request.partition())), Occur.MUST);
                 builder.add(result, Occur.MUST);
                 result = builder.build();
             }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndexSchema.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndexSchema.java
index ef335bf5a..bf4351292 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndexSchema.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndexSchema.java
@@ -78,7 +78,7 @@ final class LuceneIndexSchema {
     public void update(final Collection<Field> fields) {
         Objects.requireNonNull(fields);
         for (var field : fields) {
-            map.putIfAbsent(field.getName(), Type.fromField(field));
+            map.putIfAbsent(field.name(), Type.fromField(field));
             assertType(field);
         }
     }
@@ -90,13 +90,13 @@ final class LuceneIndexSchema {
     public void assertType(final Field field) {
         Objects.requireNonNull(field);
         var expectedType = Type.fromField(field);
-        var actualType = map.get(field.getName());
+        var actualType = map.get(field.name());
         if (actualType == null) {
-            throw new WebApplicationException("Unknown field " + 
field.getName(), Status.BAD_REQUEST);
+            throw new WebApplicationException("Unknown field " + field.name(), 
Status.BAD_REQUEST);
         }
         if (expectedType != actualType) {
             throw new WebApplicationException(
-                    String.format("field %s is of type %s not %s", 
field.getName(), expectedType, actualType),
+                    String.format("field %s is of type %s not %s", 
field.name(), expectedType, actualType),
                     Status.BAD_REQUEST);
         }
     }
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/AnalyzeResource.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/AnalyzeResource.java
index d5249e205..8da17b1c5 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/AnalyzeResource.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/AnalyzeResource.java
@@ -46,11 +46,10 @@ public final class AnalyzeResource {
     @POST
     public AnalyzeResponse analyzeText(@NotNull @Valid AnalyzeRequest request) 
throws IOException {
         try {
-            final List<String> tokens =
-                    
tokenize(LuceneAnalyzerFactory.newAnalyzer(request.getAnalyzer()), 
request.getText());
+            final List<String> tokens = 
tokenize(LuceneAnalyzerFactory.newAnalyzer(request.analyzer()), request.text());
             return new AnalyzeResponse(tokens);
         } catch (IllegalArgumentException e) {
-            throw new WebApplicationException(request.getAnalyzer() + " not a 
valid analyzer", Status.BAD_REQUEST);
+            throw new WebApplicationException(request.analyzer() + " not a 
valid analyzer", Status.BAD_REQUEST);
         }
     }
 
diff --git 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
index ef78ab20e..612c4cb70 100644
--- 
a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
+++ 
b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
@@ -62,7 +62,7 @@ public final class IndexResource {
             throws IOException {
         if (!indexDefinition.isLatestVersion()) {
             throw new WebApplicationException(
-                    "Cannot create a new version " + 
indexDefinition.getLuceneVersion() + " index", Status.BAD_REQUEST);
+                    "Cannot create a new version " + 
indexDefinition.luceneVersion() + " index", Status.BAD_REQUEST);
         }
         indexManager.create(name, indexDefinition);
         return Ok.INSTANCE;
@@ -98,16 +98,14 @@ public final class IndexResource {
     @POST
     public Ok setIndexInfo(@PathParam("name") String name, @NotNull @Valid 
IndexInfoRequest request) throws Exception {
         return indexManager.with(name, (index) -> {
-            if (request.getMatchUpdateSeq().isPresent()
-                    && request.getUpdateSeq().isPresent()) {
+            if (request.matchUpdateSeq().isPresent() && 
request.updateSeq().isPresent()) {
                 index.setUpdateSeq(
-                        request.getMatchUpdateSeq().getAsLong(),
-                        request.getUpdateSeq().getAsLong());
+                        request.matchUpdateSeq().getAsLong(),
+                        request.updateSeq().getAsLong());
             }
-            if (request.getMatchPurgeSeq().isPresent() && 
request.getPurgeSeq().isPresent()) {
+            if (request.matchPurgeSeq().isPresent() && 
request.purgeSeq().isPresent()) {
                 index.setPurgeSeq(
-                        request.getMatchPurgeSeq().getAsLong(),
-                        request.getPurgeSeq().getAsLong());
+                        request.matchPurgeSeq().getAsLong(), 
request.purgeSeq().getAsLong());
             }
             return Ok.INSTANCE;
         });
@@ -141,11 +139,11 @@ public final class IndexResource {
     public Ok update(@PathParam("name") String name, @NotNull @Valid 
BulkUpdateRequest request) throws Exception {
         return indexManager.with(name, (index) -> {
             for (var update : request.updates()) {
-                if (update.request() instanceof DocumentUpdateRequest) {
-                    index.update(update.docId(), (DocumentUpdateRequest) 
update.request());
+                if (update.update() instanceof DocumentUpdateRequest) {
+                    index.update(update.docId(), (DocumentUpdateRequest) 
update.update());
                 }
-                if (update.request() instanceof DocumentDeleteRequest) {
-                    index.delete(update.docId(), (DocumentDeleteRequest) 
update.request());
+                if (update.update() instanceof DocumentDeleteRequest) {
+                    index.delete(update.docId(), (DocumentDeleteRequest) 
update.update());
                 }
             }
             return Ok.INSTANCE;
diff --git 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/IndexDefinitionTest.java
 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/IndexDefinitionTest.java
index 2e1e7ee03..ff4eb639a 100644
--- 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/IndexDefinitionTest.java
+++ 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/IndexDefinitionTest.java
@@ -31,12 +31,12 @@ public class IndexDefinitionTest {
     @Test
     public void legacyLuceneVersionIfMissing() throws Exception {
         var indexDefinition = mapper.readValue("{}", IndexDefinition.class);
-        
assertThat(indexDefinition.getLuceneVersion()).isEqualTo(IndexDefinition.LEGACY_LUCENE_VERSION);
+        
assertThat(indexDefinition.luceneVersion()).isEqualTo(IndexDefinition.LEGACY_LUCENE_VERSION);
     }
 
     @Test
     public void luceneVersionIsDeserializedIfPresent() throws Exception {
         var indexDefinition = mapper.readValue("{\"lucene_version\":10}", 
IndexDefinition.class);
-        assertThat(indexDefinition.getLuceneVersion()).isEqualTo(10);
+        assertThat(indexDefinition.luceneVersion()).isEqualTo(10);
     }
 }
diff --git 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
index 9269f11e8..5f09dcb2e 100644
--- 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
+++ 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/api/SearchRequestTest.java
@@ -47,12 +47,12 @@ public class SearchRequestTest {
     }
 
     private SearchRequest asObject() {
-        final SearchRequest result = new SearchRequest();
-        result.setQuery("*:*");
-        result.setLimit(10);
-        result.setCounts(List.of("bar"));
-        result.setTopN(5);
-        result.setRanges(Map.of("foo", List.of(new DoubleRange("0 to 100 inc", 
0.0, true, 100.0, true))));
-        return result;
+        final SearchRequestBuilder builder = new SearchRequestBuilder();
+        builder.setQuery("*:*");
+        builder.setLimit(10);
+        builder.setCounts(List.of("bar"));
+        builder.setTopN(5);
+        builder.setRanges(Map.of("foo", List.of(new DoubleRange("0 to 100 
inc", 0.0, true, 100.0, true))));
+        return builder.build();
     }
 }
diff --git 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/core/IndexManagerTest.java
 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/core/IndexManagerTest.java
index 7a122e67c..176cb7f44 100644
--- 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/core/IndexManagerTest.java
+++ 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/core/IndexManagerTest.java
@@ -25,7 +25,7 @@ import java.util.concurrent.ForkJoinPool;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.TimeUnit;
 import org.apache.couchdb.nouveau.api.IndexDefinition;
-import org.apache.couchdb.nouveau.api.SearchRequest;
+import org.apache.couchdb.nouveau.api.SearchRequestBuilder;
 import org.apache.couchdb.nouveau.lucene.ParallelSearcherFactory;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
@@ -64,10 +64,10 @@ public class IndexManagerTest {
         final IndexDefinition indexDefinition =
                 new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, 
"standard", null);
         manager.create("foo", indexDefinition);
-        var searchRequest = new SearchRequest();
-        searchRequest.setQuery("*:*");
-        var searchResults = manager.with("foo", (index) -> 
index.search(searchRequest));
-        assertThat(searchResults.getTotalHits()).isEqualTo(0);
+        var searchRequestBuilder = new SearchRequestBuilder();
+        searchRequestBuilder.setQuery("*:*");
+        var searchResults = manager.with("foo", (index) -> 
index.search(searchRequestBuilder.build()));
+        assertThat(searchResults.totalHits()).isEqualTo(0);
     }
 
     @Test
diff --git 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
index f87af2fe0..6cc542bdc 100644
--- 
a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
+++ 
b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneIndexTest.java
@@ -29,7 +29,7 @@ import org.apache.couchdb.nouveau.api.DoubleRange;
 import org.apache.couchdb.nouveau.api.Field;
 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.SearchRequestBuilder;
 import org.apache.couchdb.nouveau.api.SearchResults;
 import org.apache.couchdb.nouveau.api.StringField;
 import org.apache.couchdb.nouveau.core.Index;
@@ -80,11 +80,11 @@ public class LuceneIndexTest {
                 final DocumentUpdateRequest request = new 
DocumentUpdateRequest(i - 1, i, null, fields);
                 index.update("doc" + i, request);
             }
-            final SearchRequest request = new SearchRequest();
-            request.setMinUpdateSeq(count);
-            request.setQuery("*:*");
-            final SearchResults results = index.search(request);
-            assertThat(results.getTotalHits()).isEqualTo(count);
+            final SearchRequestBuilder builder = new SearchRequestBuilder();
+            builder.setMinUpdateSeq(count);
+            builder.setQuery("*:*");
+            final SearchResults results = index.search(builder.build());
+            assertThat(results.totalHits()).isEqualTo(count);
         } finally {
             cleanup(index);
         }
@@ -100,12 +100,12 @@ public class LuceneIndexTest {
                 final DocumentUpdateRequest request = new 
DocumentUpdateRequest(i - 1, i, null, fields);
                 index.update("doc" + i, request);
             }
-            final SearchRequest request = new SearchRequest();
-            request.setMinUpdateSeq(count);
-            request.setQuery("*:*");
-            request.setSort(List.of("foo"));
-            final SearchResults results = index.search(request);
-            assertThat(results.getTotalHits()).isEqualTo(count);
+            final SearchRequestBuilder builder = new SearchRequestBuilder();
+            builder.setMinUpdateSeq(count);
+            builder.setQuery("*:*");
+            builder.setSort(List.of("foo"));
+            final SearchResults results = index.search(builder.build());
+            assertThat(results.totalHits()).isEqualTo(count);
         } finally {
             cleanup(index);
         }
@@ -121,12 +121,12 @@ public class LuceneIndexTest {
                 final DocumentUpdateRequest request = new 
DocumentUpdateRequest(i - 1, i, null, fields);
                 index.update("doc" + i, request);
             }
-            final SearchRequest request = new SearchRequest();
-            request.setMinUpdateSeq(count);
-            request.setQuery("*:*");
-            request.setCounts(List.of("bar"));
-            final SearchResults results = index.search(request);
-            assertThat(results.getCounts()).isEqualTo(Map.of("bar", 
Map.of("baz", count)));
+            final SearchRequestBuilder builder = new SearchRequestBuilder();
+            builder.setMinUpdateSeq(count);
+            builder.setQuery("*:*");
+            builder.setCounts(List.of("bar"));
+            final SearchResults results = index.search(builder.build());
+            assertThat(results.counts()).isEqualTo(Map.of("bar", Map.of("baz", 
count)));
         } finally {
             cleanup(index);
         }
@@ -142,16 +142,16 @@ public class LuceneIndexTest {
                 final DocumentUpdateRequest request = new 
DocumentUpdateRequest(i - 1, i, null, fields);
                 index.update("doc" + i, request);
             }
-            final SearchRequest request = new SearchRequest();
-            request.setMinUpdateSeq(count);
-            request.setQuery("*:*");
-            request.setRanges(Map.of(
+            final SearchRequestBuilder builder = new SearchRequestBuilder();
+            builder.setMinUpdateSeq(count);
+            builder.setQuery("*:*");
+            builder.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 results = index.search(request);
-            assertThat(results.getRanges()).isEqualTo(Map.of("bar", 
Map.of("low", count / 2, "high", count / 2 + 1)));
+            final SearchResults results = index.search(builder.build());
+            assertThat(results.ranges()).isEqualTo(Map.of("bar", Map.of("low", 
count / 2, "high", count / 2 + 1)));
         } finally {
             cleanup(index);
         }
@@ -174,13 +174,13 @@ public class LuceneIndexTest {
                 index.update("doc" + i, request);
             }
 
-            final SearchRequest request = new SearchRequest();
-            request.setMinUpdateSeq(count);
-            request.setQuery("*:*");
-            request.setCounts(List.of("bar"));
-            request.setTopN(1);
-            final SearchResults results = index.search(request);
-            assertThat(results.getCounts()).isEqualTo(Map.of("bar", 
Map.of("baz", count + 5)));
+            final SearchRequestBuilder builder = new SearchRequestBuilder();
+            builder.setMinUpdateSeq(count);
+            builder.setQuery("*:*");
+            builder.setCounts(List.of("bar"));
+            builder.setTopN(1);
+            final SearchResults results = index.search(builder.build());
+            assertThat(results.counts()).isEqualTo(Map.of("bar", Map.of("baz", 
count + 5)));
         } finally {
             cleanup(index);
         }
@@ -215,10 +215,10 @@ public class LuceneIndexTest {
         try {
             // Require min seq 1 on new, empty index should fail.
             assertThrows(StaleIndexException.class, () -> {
-                final SearchRequest request = new SearchRequest();
-                request.setMinUpdateSeq(1);
-                request.setQuery("*:*");
-                index.search(request);
+                final SearchRequestBuilder builder = new 
SearchRequestBuilder();
+                builder.setMinUpdateSeq(1);
+                builder.setQuery("*:*");
+                index.search(builder.build());
             });
         } finally {
             cleanup(index);
@@ -230,18 +230,18 @@ public class LuceneIndexTest {
         Index index = setup(path);
         try {
             IndexInfo info = index.info();
-            assertThat(info.getDiskSize()).isEqualTo(0);
-            assertThat(info.getNumDocs()).isEqualTo(0);
-            assertThat(info.getUpdateSeq()).isEqualTo(0);
+            assertThat(info.diskSize()).isEqualTo(0);
+            assertThat(info.numDocs()).isEqualTo(0);
+            assertThat(info.updateSeq()).isEqualTo(0);
 
             final Collection<Field> fields = List.of(new DoubleField("bar", 
12.0, false));
             index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
             index.commit();
 
             info = index.info();
-            assertThat(info.getDiskSize()).isGreaterThan(0);
-            assertThat(info.getNumDocs()).isEqualTo(1);
-            assertThat(info.getUpdateSeq()).isEqualTo(2);
+            assertThat(info.diskSize()).isGreaterThan(0);
+            assertThat(info.numDocs()).isEqualTo(1);
+            assertThat(info.updateSeq()).isEqualTo(2);
         } finally {
             cleanup(index);
         }
@@ -256,14 +256,14 @@ public class LuceneIndexTest {
             index.commit();
 
             IndexInfo info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(1);
+            assertThat(info.numDocs()).isEqualTo(1);
 
             index.delete("foo", new DocumentDeleteRequest(2, 3, false));
             index.commit();
 
             info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(0);
-            assertThat(info.getUpdateSeq()).isEqualTo(3);
+            assertThat(info.numDocs()).isEqualTo(0);
+            assertThat(info.updateSeq()).isEqualTo(3);
         } finally {
             cleanup(index);
         }
@@ -278,14 +278,14 @@ public class LuceneIndexTest {
             index.commit();
 
             IndexInfo info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(1);
+            assertThat(info.numDocs()).isEqualTo(1);
 
             index.delete("foo", new DocumentDeleteRequest(0, 3, true));
             index.commit();
 
             info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(0);
-            assertThat(info.getPurgeSeq()).isEqualTo(3);
+            assertThat(info.numDocs()).isEqualTo(0);
+            assertThat(info.purgeSeq()).isEqualTo(3);
         } finally {
             cleanup(index);
         }


Reply via email to