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 2026/03/22 18:01:22 UTC

(couchdb) branch nouveau-update-bundle created (now b781b2738)

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

rnewson pushed a change to branch nouveau-update-bundle
in repository https://gitbox.apache.org/repos/asf/couchdb.git


      at b781b2738 remove open_or_create_index/2

This branch includes the following new commits:

     new b0855a384 upgrade to Lucene 10.4.0
     new c13bb23bf use StandardCharsets.UTF_8
     new c15701386 convert Analyze{Request,Response} to record
     new a9065b370 convert IndexInfo{Request,Response} to record
     new daac383ea improve annotations in BulkUpdateRequest
     new f9fb70d62 convert Document api classes to records
     new 84bdcfb52 convert IndexDefinition to record
     new 3b6169ada convert api field classes to record
     new c4c16bd8f convert Search api classes to records
     new 06d8f4e55 remove unused function
     new b781b2738 remove open_or_create_index/2

The 11 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(couchdb) 02/11: use StandardCharsets.UTF_8

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c13bb23bfb572f7987f1842ca74cf33c934d9559
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 09:46:33 2026 +0000

    use StandardCharsets.UTF_8
---
 .../main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java    | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

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..9d28072df 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
@@ -20,8 +20,8 @@ import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.CharBuffer;
 import java.nio.charset.CharacterCodingException;
-import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
+import java.nio.charset.StandardCharsets;
 import java.nio.file.NoSuchFileException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -414,7 +414,7 @@ public class LuceneIndex extends Index {
             result.add(new org.apache.lucene.document.StringField("_partition", request.getPartition(), Store.NO));
         }
 
-        final CharsetDecoder utf8Decoder = Charset.forName("UTF-8").newDecoder();
+        final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
 
         for (Field field : request.getFields()) {
             // Underscore-prefix is reserved.
@@ -475,7 +475,7 @@ public class LuceneIndex extends Index {
     }
 
     private PrimitiveWrapper<?>[] toAfter(final FieldDoc fieldDoc) {
-        final CharsetDecoder utf8Decoder = Charset.forName("UTF-8").newDecoder();
+        final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
         final PrimitiveWrapper<?>[] fields = new PrimitiveWrapper<?>[fieldDoc.fields.length];
         for (int i = 0; i < fields.length; i++) {
             if (fieldDoc.fields[i] instanceof String) {


(couchdb) 04/11: convert IndexInfo{Request,Response} to record

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a9065b370e109ed87d50521e0752dbc735285867
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 10:04:13 2026 +0000

    convert IndexInfo{Request,Response} to record
---
 .../org/apache/couchdb/nouveau/api/IndexInfo.java  | 68 ----------------------
 .../couchdb/nouveau/api/IndexInfoRequest.java      | 54 +++--------------
 .../couchdb/nouveau/api/IndexInfoResponse.java     | 25 ++++++++
 .../org/apache/couchdb/nouveau/core/Index.java     |  6 +-
 .../couchdb/nouveau/resources/IndexResource.java   | 16 +++--
 .../couchdb/nouveau/lucene/LuceneIndexTest.java    | 32 +++++-----
 6 files changed, 60 insertions(+), 141 deletions(-)

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
deleted file mode 100644
index 9958c7780..000000000
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfo.java
+++ /dev/null
@@ -1,68 +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.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 + "]";
-    }
-}
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..bc2f016f8 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
@@ -13,50 +13,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(
+        @PositiveOrZero OptionalLong matchUpdateSeq,
+        @PositiveOrZero OptionalLong updateSeq,
+        @PositiveOrZero OptionalLong matchPurgeSeq,
+        @PositiveOrZero OptionalLong purgeSeq) {}
diff --git a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoResponse.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoResponse.java
new file mode 100644
index 000000000..26543c677
--- /dev/null
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/IndexInfoResponse.java
@@ -0,0 +1,25 @@
+//
+// 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.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
+import jakarta.validation.constraints.PositiveOrZero;
+
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record IndexInfoResponse(
+        @PositiveOrZero long updateSeq,
+        @PositiveOrZero long purgeSeq,
+        @PositiveOrZero long numDocs,
+        @PositiveOrZero long diskSize) {}
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..528a885b6 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
@@ -19,7 +19,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import org.apache.couchdb.nouveau.api.DocumentDeleteRequest;
 import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
-import org.apache.couchdb.nouveau.api.IndexInfo;
+import org.apache.couchdb.nouveau.api.IndexInfoResponse;
 import org.apache.couchdb.nouveau.api.SearchRequest;
 import org.apache.couchdb.nouveau.api.SearchResults;
 
@@ -44,10 +44,10 @@ public abstract class Index implements Closeable {
         this.purgeSeq = purgeSeq;
     }
 
-    public final IndexInfo info() throws IOException {
+    public final IndexInfoResponse info() throws IOException {
         final int numDocs = doNumDocs();
         final long diskSize = doDiskSize();
-        return new IndexInfo(updateSeq, purgeSeq, numDocs, diskSize);
+        return new IndexInfoResponse(updateSeq, purgeSeq, numDocs, diskSize);
     }
 
     protected abstract int doNumDocs() throws IOException;
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..ecddb6a34 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
@@ -36,8 +36,8 @@ import org.apache.couchdb.nouveau.api.BulkUpdateRequest;
 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.IndexInfoRequest;
+import org.apache.couchdb.nouveau.api.IndexInfoResponse;
 import org.apache.couchdb.nouveau.api.Ok;
 import org.apache.couchdb.nouveau.api.SearchRequest;
 import org.apache.couchdb.nouveau.api.SearchResults;
@@ -89,7 +89,7 @@ public final class IndexResource {
     }
 
     @GET
-    public IndexInfo getIndexInfo(@PathParam("name") String name) throws Exception {
+    public IndexInfoResponse getIndexInfo(@PathParam("name") String name) throws Exception {
         return indexManager.with(name, (index) -> {
             return index.info();
         });
@@ -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;
         });
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..abc0af1cf 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
@@ -28,7 +28,7 @@ import org.apache.couchdb.nouveau.api.DoubleField;
 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.IndexInfoResponse;
 import org.apache.couchdb.nouveau.api.SearchRequest;
 import org.apache.couchdb.nouveau.api.SearchResults;
 import org.apache.couchdb.nouveau.api.StringField;
@@ -229,19 +229,19 @@ public class LuceneIndexTest {
     public void testInfo(@TempDir Path path) throws IOException {
         Index index = setup(path);
         try {
-            IndexInfo info = index.info();
-            assertThat(info.getDiskSize()).isEqualTo(0);
-            assertThat(info.getNumDocs()).isEqualTo(0);
-            assertThat(info.getUpdateSeq()).isEqualTo(0);
+            IndexInfoResponse info = index.info();
+            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);
         }
@@ -255,15 +255,15 @@ public class LuceneIndexTest {
             index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
             index.commit();
 
-            IndexInfo info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(1);
+            IndexInfoResponse info = index.info();
+            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);
         }
@@ -277,15 +277,15 @@ public class LuceneIndexTest {
             index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
             index.commit();
 
-            IndexInfo info = index.info();
-            assertThat(info.getNumDocs()).isEqualTo(1);
+            IndexInfoResponse info = index.info();
+            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);
         }


(couchdb) 01/11: upgrade to Lucene 10.4.0

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b0855a384e8f85dbe294bc83f7f5511c16c3bdd4
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 09:42:34 2026 +0000

    upgrade to Lucene 10.4.0
---
 extra/nouveau/build.gradle                                              | 2 +-
 .../main/java/org/apache/couchdb/nouveau/lucene/QueryDeserializer.java  | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/extra/nouveau/build.gradle b/extra/nouveau/build.gradle
index 495b04893..a9fb5454d 100644
--- a/extra/nouveau/build.gradle
+++ b/extra/nouveau/build.gradle
@@ -23,7 +23,7 @@ dependencies {
     implementation 'io.dropwizard.metrics:metrics-jersey2'
     testImplementation 'io.dropwizard:dropwizard-testing'
 
-    def luceneVersion = '10.3.2'
+    def luceneVersion = '10.4.0'
     implementation 'org.apache.lucene:lucene-core:' + luceneVersion
     implementation 'org.apache.lucene:lucene-queryparser:' + luceneVersion
     implementation 'org.apache.lucene:lucene-analysis-common:' + luceneVersion
diff --git a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/QueryDeserializer.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/QueryDeserializer.java
index 04a771cf5..aa5460b9a 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/QueryDeserializer.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/QueryDeserializer.java
@@ -109,7 +109,7 @@ public class QueryDeserializer extends StdDeserializer<Query> {
             case "point_range": {
             }
             case "match_all":
-                return new MatchAllDocsQuery();
+                return MatchAllDocsQuery.INSTANCE;
         }
         throw new JsonParseException(parser, type + " not a supported query type");
     }


(couchdb) 10/11: remove unused function

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 06d8f4e5513d69ab347e3daac03e7383b2106d72
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 23:31:42 2026 +0000

    remove unused function
---
 src/nouveau/src/nouveau_index_updater.erl | 12 +-----------
 1 file changed, 1 insertion(+), 11 deletions(-)

diff --git a/src/nouveau/src/nouveau_index_updater.erl b/src/nouveau/src/nouveau_index_updater.erl
index 10d4144f0..763e12d03 100644
--- a/src/nouveau/src/nouveau_index_updater.erl
+++ b/src/nouveau/src/nouveau_index_updater.erl
@@ -18,7 +18,7 @@
 -include("nouveau.hrl").
 
 %% public api
--export([outdated/1, get_db_info/1]).
+-export([get_db_info/1]).
 
 %% callbacks
 -export([update/1]).
@@ -47,16 +47,6 @@
     batch
 }).
 
-outdated(#index{} = Index) ->
-    case open_or_create_index(Index) of
-        {ok, #{} = Info} ->
-            #{<<"update_seq">> := IndexUpdateSeq, <<"purge_seq">> := IndexPurgeSeq} = Info,
-            {DbUpdateSeq, DbPurgeSeq} = get_db_info(Index),
-            DbUpdateSeq > IndexUpdateSeq orelse DbPurgeSeq > IndexPurgeSeq;
-        {error, Reason} ->
-            {error, Reason}
-    end.
-
 update(#index{} = Index) ->
     {ok, Db} = couch_db:open_int(Index#index.dbname, []),
     try


(couchdb) 07/11: convert IndexDefinition to record

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 84bdcfb5211f02c7fc03e1cdb7b0c9b3a8db5e49
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 15:05:51 2026 +0000

    convert IndexDefinition to record
---
 .../couchdb/nouveau/api/IndexDefinition.java       | 92 +++-------------------
 .../apache/couchdb/nouveau/core/IndexManager.java  |  2 +-
 .../couchdb/nouveau/health/IndexHealthCheck.java   |  7 +-
 .../nouveau/lucene/LuceneAnalyzerFactory.java      |  6 +-
 .../couchdb/nouveau/resources/IndexResource.java   |  3 +-
 .../couchdb/nouveau/api/IndexDefinitionTest.java   |  5 +-
 .../couchdb/nouveau/core/IndexManagerTest.java     | 23 +++---
 .../nouveau/health/IndexHealthCheckTest.java       |  5 +-
 .../nouveau/lucene/LuceneAnalyzerFactoryTest.java  | 10 ++-
 .../couchdb/nouveau/lucene/LuceneIndexTest.java    |  3 +-
 10 files changed, 47 insertions(+), 109 deletions(-)

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..5b800a3ea 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
@@ -14,105 +14,35 @@
 package org.apache.couchdb.nouveau.api;
 
 import com.fasterxml.jackson.annotation.JsonIgnore;
-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 java.util.Map;
+import java.util.Optional;
+import java.util.OptionalInt;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class IndexDefinition {
+public record IndexDefinition(
+        @Min(LEGACY_LUCENE_VERSION) @Max(LATEST_LUCENE_VERSION) OptionalInt luceneVersion,
+        @NotEmpty String defaultAnalyzer,
+        Optional<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
+        this(OptionalInt.of(LATEST_LUCENE_VERSION), "standard", Optional.empty());
     }
 
-    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;
+    @JsonIgnore
+    public int luceneVersionAsInt() {
+        return luceneVersion.orElse(LEGACY_LUCENE_VERSION);
     }
 
     @JsonIgnore
     public boolean isLatestVersion() {
-        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 + "]";
+        return luceneVersion.getAsInt() == LATEST_LUCENE_VERSION;
     }
 }
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..6120f8703 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.luceneVersionAsInt();
         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/IndexHealthCheck.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
index 0a70b2b6c..81bca3094 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
@@ -17,6 +17,8 @@ import com.codahale.metrics.health.HealthCheck;
 import java.io.IOException;
 import java.util.Collections;
 import java.util.List;
+import java.util.Optional;
+import java.util.OptionalInt;
 import org.apache.couchdb.nouveau.api.BulkUpdateRequest;
 import org.apache.couchdb.nouveau.api.DocumentUpdate;
 import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
@@ -42,7 +44,10 @@ public final class IndexHealthCheck extends HealthCheck {
             // Ignored, index might not exist yet.
         }
 
-        indexResource.createIndex(name, new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null));
+        indexResource.createIndex(
+                name,
+                new IndexDefinition(
+                        OptionalInt.of(IndexDefinition.LATEST_LUCENE_VERSION), "standard", Optional.empty()));
         try {
             indexResource.update(
                     name,
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..89c268f40 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,13 @@ public final class LuceneAnalyzerFactory {
     private LuceneAnalyzerFactory() {}
 
     public static Analyzer fromDefinition(final IndexDefinition indexDefinition) {
-        final Analyzer defaultAnalyzer = newAnalyzer(indexDefinition.getDefaultAnalyzer());
-        if (!indexDefinition.hasFieldAnalyzers()) {
+        final Analyzer defaultAnalyzer = newAnalyzer(indexDefinition.defaultAnalyzer());
+        if (indexDefinition.fieldAnalyzers().isEmpty()) {
             return defaultAnalyzer;
         }
         final Map<String, Analyzer> fieldAnalyzers = new HashMap<String, Analyzer>();
         for (Map.Entry<String, String> entry :
-                indexDefinition.getFieldAnalyzers().entrySet()) {
+                indexDefinition.fieldAnalyzers().get().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/resources/IndexResource.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
index 756230590..18539fbed 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,8 @@ 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.luceneVersionAsInt() + " index",
+                    Status.BAD_REQUEST);
         }
         indexManager.create(name, indexDefinition);
         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..d0b73da41 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
@@ -26,17 +26,18 @@ public class IndexDefinitionTest {
     @BeforeAll
     public static void setup() {
         mapper = new ObjectMapper();
+        mapper.findAndRegisterModules();
     }
 
     @Test
     public void legacyLuceneVersionIfMissing() throws Exception {
         var indexDefinition = mapper.readValue("{}", IndexDefinition.class);
-        assertThat(indexDefinition.getLuceneVersion()).isEqualTo(IndexDefinition.LEGACY_LUCENE_VERSION);
+        assertThat(indexDefinition.luceneVersionAsInt()).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.luceneVersionAsInt()).isEqualTo(10);
     }
 }
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..e1d867bd2 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
@@ -43,9 +43,12 @@ public class IndexManagerTest {
         executorService = Executors.newScheduledThreadPool(2);
         rootDir = path;
 
+        var mapper = new ObjectMapper();
+        mapper.findAndRegisterModules();
+
         manager = new IndexManager();
         manager.setRootDir(path);
-        manager.setObjectMapper(new ObjectMapper());
+        manager.setObjectMapper(mapper);
         manager.setCommitIntervalSeconds(10);
         manager.setScheduledExecutorService(executorService);
         manager.setSearcherFactory(new ParallelSearcherFactory(ForkJoinPool.commonPool()));
@@ -61,8 +64,7 @@ public class IndexManagerTest {
 
     @Test
     public void managerReturnsUsableIndex() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
         manager.create("foo", indexDefinition);
         var searchRequest = new SearchRequest();
         searchRequest.setQuery("*:*");
@@ -72,8 +74,7 @@ public class IndexManagerTest {
 
     @Test
     public void managerReopensAClosedIndex() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
 
         manager.create("bar", indexDefinition);
 
@@ -90,8 +91,7 @@ public class IndexManagerTest {
 
     @Test
     public void deleteAllRemovesIndexByName() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
 
         assertThat(countIndexes()).isEqualTo(0);
         manager.create("bar", indexDefinition);
@@ -102,8 +102,7 @@ public class IndexManagerTest {
 
     @Test
     public void deleteAllRemovesIndexByPath() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
 
         assertThat(countIndexes()).isEqualTo(0);
         manager.create("foo/bar", indexDefinition);
@@ -114,8 +113,7 @@ public class IndexManagerTest {
 
     @Test
     public void deleteAllRemovesIndexByGlob() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
 
         assertThat(countIndexes()).isEqualTo(0);
         manager.create("foo/bar", indexDefinition);
@@ -126,8 +124,7 @@ public class IndexManagerTest {
 
     @Test
     public void deleteAllRemovesIndexByGlobExceptExclusions() throws Exception {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
 
         assertThat(countIndexes()).isEqualTo(0);
         manager.create("foo/bar", indexDefinition);
diff --git a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/health/IndexHealthCheckTest.java b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/health/IndexHealthCheckTest.java
index 0c777fbab..8e1e02b05 100644
--- a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/health/IndexHealthCheckTest.java
+++ b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/health/IndexHealthCheckTest.java
@@ -28,11 +28,14 @@ public class IndexHealthCheckTest {
 
     @Test
     public void testIndexHealthCheck(@TempDir final Path tempDir) throws Exception {
+        var mapper = new ObjectMapper();
+        mapper.findAndRegisterModules();
+
         var manager = new IndexManager();
         manager.setCommitIntervalSeconds(30);
         manager.setIdleSeconds(60);
         manager.setMaxIndexesOpen(1);
-        manager.setObjectMapper(new ObjectMapper());
+        manager.setObjectMapper(mapper);
         manager.setRootDir(tempDir);
         manager.setScheduledExecutorService(Executors.newScheduledThreadPool(2));
         manager.setSearcherFactory(new SearcherFactory());
diff --git a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactoryTest.java b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactoryTest.java
index 383cf34dc..ec10f156f 100644
--- a/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactoryTest.java
+++ b/extra/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene/LuceneAnalyzerFactoryTest.java
@@ -19,6 +19,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import jakarta.ws.rs.WebApplicationException;
 import java.lang.reflect.Method;
 import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalInt;
 import org.apache.couchdb.nouveau.api.IndexDefinition;
 import org.apache.lucene.analysis.Analyzer;
 import org.apache.lucene.analysis.ar.ArabicAnalyzer;
@@ -257,9 +259,9 @@ public class LuceneAnalyzerFactoryTest {
     @Test
     public void testFieldAnalyzers() throws Exception {
         final IndexDefinition indexDefinition = new IndexDefinition(
-                IndexDefinition.LATEST_LUCENE_VERSION,
+                OptionalInt.of(IndexDefinition.LATEST_LUCENE_VERSION),
                 "standard",
-                Map.of("english", "english", "thai", "thai", "email", "email"));
+                Optional.of(Map.of("english", "english", "thai", "thai", "email", "email")));
         final Analyzer analyzer = LuceneAnalyzerFactory.fromDefinition(indexDefinition);
         assertThat(analyzer).isInstanceOf(PerFieldAnalyzerWrapper.class);
         final Method m = PerFieldAnalyzerWrapper.class.getDeclaredMethod("getWrappedAnalyzer", String.class);
@@ -277,8 +279,8 @@ public class LuceneAnalyzerFactoryTest {
 
     private void assertAnalyzer(final String name, final Class<? extends Analyzer> clazz) throws Exception {
         assertThat(LuceneAnalyzerFactory.newAnalyzer(name)).isInstanceOf(clazz);
-        assertThat(LuceneAnalyzerFactory.fromDefinition(
-                        new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, name, null)))
+        assertThat(LuceneAnalyzerFactory.fromDefinition(new IndexDefinition(
+                        OptionalInt.of(IndexDefinition.LATEST_LUCENE_VERSION), name, Optional.empty())))
                 .isInstanceOf(clazz);
     }
 }
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 abc0af1cf..83ee15268 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
@@ -48,8 +48,7 @@ import org.junit.jupiter.api.io.TempDir;
 public class LuceneIndexTest {
 
     protected final Index setup(final Path path) throws IOException {
-        final IndexDefinition indexDefinition =
-                new IndexDefinition(IndexDefinition.LATEST_LUCENE_VERSION, "standard", null);
+        final IndexDefinition indexDefinition = new IndexDefinition();
         final Analyzer analyzer = LuceneAnalyzerFactory.fromDefinition(indexDefinition);
         final Directory dir = new DirectIODirectory(FSDirectory.open(path));
         final IndexWriterConfig config = new IndexWriterConfig(analyzer);


(couchdb) 06/11: convert Document api classes to records

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit f9fb70d62a4c5a81c8d21dfc0c9f2c5d0af665d6
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 10:19:55 2026 +0000

    convert Document api classes to records
---
 .../couchdb/nouveau/api/DocumentDeleteRequest.java | 49 ++-------------
 .../couchdb/nouveau/api/DocumentRequest.java       |  2 +-
 .../apache/couchdb/nouveau/api/DocumentUpdate.java |  6 +-
 .../couchdb/nouveau/api/DocumentUpdateRequest.java | 69 ++++++----------------
 .../org/apache/couchdb/nouveau/core/Index.java     | 14 ++---
 .../apache/couchdb/nouveau/lucene/LuceneIndex.java | 10 ++--
 .../couchdb/nouveau/resources/IndexResource.java   |  8 +--
 7 files changed, 43 insertions(+), 115 deletions(-)

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..f306b376b 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
@@ -13,50 +13,11 @@
 
 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;
-    }
-
-    public boolean isPurge() {
-        return purge;
-    }
-
-    @Override
-    public String toString() {
-        return "DocumentDeleteRequest [matchSeq=" + matchSeq + ", seq=" + seq + ", purge=" + purge + "]";
-    }
-}
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record DocumentDeleteRequest(@PositiveOrZero long matchSeq, @Positive long seq, boolean purge)
+        implements DocumentRequest {}
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..b078e00b1 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
@@ -13,6 +13,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(String docId, 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..205968d3a 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
@@ -13,61 +13,26 @@
 
 package org.apache.couchdb.nouveau.api;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
-import jakarta.validation.Valid;
+import com.fasterxml.jackson.databind.PropertyNamingStrategies;
+import com.fasterxml.jackson.databind.annotation.JsonNaming;
 import jakarta.validation.constraints.NotEmpty;
+import jakarta.validation.constraints.NotNull;
 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;
-    }
-
-    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 + "]";
+import java.util.Optional;
+
+@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
+public record DocumentUpdateRequest(
+        @PositiveOrZero long matchSeq,
+        @Positive long seq,
+        @NotNull Optional<String> partition,
+        @NotEmpty Collection<Field> fields)
+        implements DocumentRequest {
+
+    public DocumentUpdateRequest {
+        if (partition == null) {
+            partition = Optional.empty();
+        }
     }
 }
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 528a885b6..e7693702c 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,22 +55,22 @@ 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());
+        if (request.purge()) {
+            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());
         }
     }
 
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 9d28072df..e8eb46ccc 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
@@ -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);
     }
 
@@ -410,13 +410,13 @@ public class LuceneIndex extends Index {
         result.add(new SortedDocValuesField("_id", new BytesRef(docId)));
 
         // partition (optional)
-        if (request.hasPartition()) {
-            result.add(new org.apache.lucene.document.StringField("_partition", request.getPartition(), Store.NO));
-        }
+        request.partition()
+                .ifPresent(partition ->
+                        result.add(new org.apache.lucene.document.StringField("_partition", partition, Store.NO)));
 
         final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
 
-        for (Field field : request.getFields()) {
+        for (Field field : request.fields()) {
             // Underscore-prefix is reserved.
             if (field.getName().startsWith("_")) {
                 continue;
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 ecddb6a34..756230590 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
@@ -139,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;


(couchdb) 08/11: convert api field classes to record

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3b6169ada8975639730ba09bd934a705d2d054cb
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 15:22:24 2026 +0000

    convert api field classes to record
---
 .../apache/couchdb/nouveau/api/DoubleField.java    | 38 +--------------------
 .../java/org/apache/couchdb/nouveau/api/Field.java | 17 ++--------
 .../apache/couchdb/nouveau/api/StoredField.java    | 30 ++---------------
 .../apache/couchdb/nouveau/api/StringField.java    | 39 ++--------------------
 .../org/apache/couchdb/nouveau/api/TextField.java  | 39 ++--------------------
 .../apache/couchdb/nouveau/lucene/LuceneIndex.java | 18 +++++-----
 .../couchdb/nouveau/lucene/LuceneIndexSchema.java  |  8 ++---
 7 files changed, 23 insertions(+), 166 deletions(-)

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..c86b67ffc 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
@@ -13,40 +13,4 @@
 
 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(String name, double value, boolean store) implements Field {}
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/StoredField.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/StoredField.java
index 697fc4f93..318c0f138 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
@@ -13,33 +13,9 @@
 
 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(
+        String name, @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..d116efa7b 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
@@ -13,41 +13,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;
-import java.util.Objects;
+import jakarta.validation.constraints.NotEmpty;
 
-@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(String name, @NotEmpty String value, 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..ed1ab844e 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
@@ -13,41 +13,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;
-import java.util.Objects;
+import jakarta.validation.constraints.NotEmpty;
 
-@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(String name, @NotEmpty String value, boolean store) implements Field {}
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 e8eb46ccc..1f6a735ff 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
@@ -418,34 +418,34 @@ public class LuceneIndex extends Index {
 
         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);
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);
         }
     }


(couchdb) 03/11: convert Analyze{Request,Response} to record

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c1570138683c26d0d77c04e316f5d66e0688809c
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 09:53:10 2026 +0000

    convert Analyze{Request,Response} to record
---
 .../apache/couchdb/nouveau/api/AnalyzeRequest.java | 32 +---------------------
 .../couchdb/nouveau/api/AnalyzeResponse.java       | 23 +---------------
 .../couchdb/nouveau/health/AnalyzeHealthCheck.java |  2 +-
 .../couchdb/nouveau/resources/AnalyzeResource.java |  5 ++--
 4 files changed, 5 insertions(+), 57 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..d02f09a1f 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
@@ -13,36 +13,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(@NotEmpty String analyzer, @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..09945b9f0 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
@@ -13,29 +13,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(@NotNull List<@NotEmpty String> tokens) {}
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/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);
         }
     }
 


(couchdb) 09/11: convert Search api classes to records

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit c4c16bd8fee2d9cfc9ac453926f5d80bad6ecb2f
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 20:27:47 2026 +0000

    convert Search api classes to records
---
 .../org/apache/couchdb/nouveau/api/SearchHit.java  |  39 +---
 .../apache/couchdb/nouveau/api/SearchRequest.java  | 207 +++++++++------------
 .../apache/couchdb/nouveau/api/SearchResults.java  |  74 +-------
 .../org/apache/couchdb/nouveau/core/Index.java     |   2 +-
 .../couchdb/nouveau/health/IndexHealthCheck.java   |  12 +-
 .../apache/couchdb/nouveau/lucene/LuceneIndex.java | 154 +++++++--------
 .../couchdb/nouveau/api/SearchRequestTest.java     |  15 +-
 .../couchdb/nouveau/core/IndexManagerTest.java     |   5 +-
 .../couchdb/nouveau/lucene/LuceneIndexTest.java    |  70 +++----
 9 files changed, 225 insertions(+), 353 deletions(-)

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..9a0d8229b 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
@@ -18,43 +18,8 @@ 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<@NotNull 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..948b080a7 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
@@ -13,7 +13,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;
@@ -25,162 +24,126 @@ import jakarta.validation.constraints.PositiveOrZero;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
+import java.util.OptionalInt;
 import org.apache.couchdb.nouveau.core.ser.PrimitiveWrapper;
 
 @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
-public class SearchRequest {
+public record SearchRequest(
+        @NotNull String query,
+        @PositiveOrZero long minUpdateSeq,
+        @PositiveOrZero long minPurgeSeq,
+        Optional<Locale> locale,
+        Optional<String> partition,
+        @Positive OptionalInt limit,
+        @NotNull Optional<List<@NotEmpty String>> sort,
+        @NotNull Optional<List<@NotEmpty String>> counts,
+        @NotNull Optional<Map<@NotEmpty String, List<@NotNull DoubleRange>>> ranges,
+        @NotNull Optional<PrimitiveWrapper<?>[]> after,
+        @Min(1) @Max(1000) OptionalInt topN) {
 
-    @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 int limitAsInt() {
+        return limit.orElse(25);
     }
 
-    public void setQuery(final String query) {
-        this.query = query;
+    public int topNAsInt() {
+        return topN.orElse(10);
     }
 
-    @JsonProperty
-    public String getQuery() {
-        return query;
+    public boolean hasCounts() {
+        return !counts.isEmpty();
     }
 
-    public void setMinUpdateSeq(final long minUpdateSeq) {
-        this.minUpdateSeq = minUpdateSeq;
+    public boolean hasRanges() {
+        return !ranges.isEmpty();
     }
 
-    @JsonProperty
-    public long getMinUpdateSeq() {
-        return minUpdateSeq;
+    public Locale getLocale() {
+        return locale.orElse(Locale.getDefault());
     }
 
-    public void setMinPurgeSeq(final long minPurgeSeq) {
-        this.minPurgeSeq = minPurgeSeq;
-    }
+    public static class Builder {
 
-    @JsonProperty
-    public long getMinPurgeSeq() {
-        return minPurgeSeq;
-    }
+        private String query;
 
-    public void setLocale(final Locale locale) {
-        this.locale = locale;
-    }
+        private long minUpdateSeq;
 
-    @JsonProperty
-    public Locale getLocale() {
-        return locale;
-    }
+        private long minPurgeSeq;
 
-    public void setPartition(final String partition) {
-        this.partition = partition;
-    }
+        private Optional<Locale> locale = Optional.empty();
 
-    @JsonProperty
-    public String getPartition() {
-        return partition;
-    }
+        private Optional<String> partition = Optional.empty();
 
-    public boolean hasPartition() {
-        return partition != null;
-    }
+        private OptionalInt limit = OptionalInt.empty();
 
-    public void setLimit(final int limit) {
-        this.limit = limit;
-    }
+        private Optional<List<String>> sort = Optional.empty();
 
-    @JsonProperty
-    public int getLimit() {
-        return limit;
-    }
+        private Optional<List<String>> counts = Optional.empty();
 
-    public boolean hasSort() {
-        return sort != null;
-    }
+        private Optional<Map<String, List<DoubleRange>>> ranges = Optional.empty();
 
-    @JsonProperty
-    public List<String> getSort() {
-        return sort;
-    }
+        private Optional<PrimitiveWrapper<?>[]> after = Optional.empty();
 
-    public void setSort(List<String> sort) {
-        this.sort = sort;
-    }
+        private OptionalInt topN = OptionalInt.empty();
 
-    public boolean hasCounts() {
-        return counts != null;
-    }
+        public Builder setQuery(final String query) {
+            this.query = query;
+            return this;
+        }
 
-    public void setCounts(final List<String> counts) {
-        this.counts = counts;
-    }
+        public Builder setMinUpdateSeq(final long minUpdateSeq) {
+            this.minUpdateSeq = minUpdateSeq;
+            return this;
+        }
 
-    @JsonProperty
-    public List<String> getCounts() {
-        return counts;
-    }
+        public Builder setMinPurgeSeq(final long minPurgeSeq) {
+            this.minPurgeSeq = minPurgeSeq;
+            return this;
+        }
 
-    public boolean hasRanges() {
-        return ranges != null;
-    }
+        public Builder setLocale(final Locale locale) {
+            this.locale = Optional.of(locale);
+            return this;
+        }
 
-    public void setRanges(final Map<String, List<DoubleRange>> ranges) {
-        this.ranges = ranges;
-    }
+        public Builder setPartition(final String partition) {
+            this.partition = Optional.of(partition);
+            return this;
+        }
 
-    @JsonProperty
-    public Map<String, List<DoubleRange>> getRanges() {
-        return ranges;
-    }
+        public Builder setLimit(final int limit) {
+            this.limit = OptionalInt.of(limit);
+            return this;
+        }
 
-    public void setTopN(final int topN) {
-        this.topN = topN;
-    }
+        public Builder setSort(List<String> sort) {
+            this.sort = Optional.of(sort);
+            return this;
+        }
 
-    @JsonProperty
-    public int getTopN() {
-        return topN;
-    }
+        public Builder setCounts(final List<String> counts) {
+            this.counts = Optional.of(counts);
+            return this;
+        }
 
-    public void setAfter(final PrimitiveWrapper<?>[] after) {
-        this.after = after;
-    }
+        public Builder setRanges(final Map<String, List<DoubleRange>> ranges) {
+            this.ranges = Optional.of(ranges);
+            return this;
+        }
 
-    @JsonProperty
-    public PrimitiveWrapper<?>[] getAfter() {
-        return after;
-    }
+        public Builder setTopN(final int topN) {
+            this.topN = OptionalInt.of(topN);
+            return this;
+        }
+
+        public Builder setAfter(final PrimitiveWrapper<?>[] after) {
+            this.after = Optional.of(after);
+            return this;
+        }
 
-    @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 + "]";
+        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..dec725e8a 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
@@ -13,7 +13,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;
@@ -23,70 +22,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(
+        @PositiveOrZero long totalHits,
+        @NotNull Relation totalHitsRelation,
+        @NotNull List<@NotNull SearchHit> hits,
+        Map<@NotNull String, Map<@NotNull String, Number>> counts,
+        Map<@NotNull String, Map<@NotNull String, Number>> ranges) {}
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 e7693702c..c90cc6aaa 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
@@ -77,7 +77,7 @@ public abstract class Index implements Closeable {
     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/health/IndexHealthCheck.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/health/IndexHealthCheck.java
index 81bca3094..5b4e7beb8 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
@@ -53,16 +53,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 SearchRequest searchRequest = new SearchRequest.Builder()
+                    .setQuery("_id:foo")
+                    .setMinUpdateSeq(1)
+                    .build();
             final SearchResults searchResults = indexResource.searchIndex(name, searchRequest);
-            if (searchResults.getTotalHits() == 1) {
+            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/LuceneIndex.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene/LuceneIndex.java
index 1f6a735ff..14cc672e9 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
@@ -25,9 +25,9 @@ import java.nio.charset.StandardCharsets;
 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.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Objects;
@@ -223,21 +223,20 @@ public class LuceneIndex extends Index {
 
     private CollectorManager<?, ? extends TopDocs> hitCollector(final SearchRequest searchRequest) {
         final Sort sort = toSort(searchRequest);
+        final FieldDoc fieldDoc = searchRequest
+                .after()
+                .map(after -> {
+                    var result = toFieldDoc(after);
+                    if (getLastSortField(sort).getReverse()) {
+                        result.doc = 0;
+                    } else {
+                        result.doc = Integer.MAX_VALUE;
+                    }
+                    return result;
+                })
+                .orElse(null);
 
-        final PrimitiveWrapper<?>[] 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 new TopFieldCollectorManager(sort, searchRequest.getLimit(), fieldDoc, 1000);
+        return new TopFieldCollectorManager(sort, searchRequest.limitAsInt(), fieldDoc, 1000);
     }
 
     private SortField getLastSortField(final Sort sort) {
@@ -248,17 +247,20 @@ 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 TopDocs topDocs = (TopDocs) reduces[0];
+        final var hits = collectHits(searcher, topDocs);
+        Map<String, Map<String, Number>> counts = Collections.emptyMap();
+        Map<String, Map<String, Number>> ranges = Collections.emptyMap();
+
         if (reduces.length == 2) {
-            collectFacets(searchRequest, searcher, (FacetsCollector) reduces[1], result);
+            counts = collectCounts(searchRequest, searcher, (FacetsCollector) reduces[1]);
+            ranges = collectRanges(searchRequest, searcher, (FacetsCollector) reduces[1]);
         }
-        return result;
+        return new SearchResults(topDocs.totalHits.value(), topDocs.totalHits.relation(), hits, counts, ranges);
     }
 
-    private void collectHits(final IndexSearcher searcher, final TopDocs topDocs, final SearchResults searchResults)
-            throws IOException {
-        final List<SearchHit> hits = new ArrayList<SearchHit>(topDocs.scoreDocs.length);
+    private List<SearchHit> collectHits(final IndexSearcher searcher, final TopDocs topDocs) throws IOException {
+        final List<SearchHit> result = new ArrayList<SearchHit>(topDocs.scoreDocs.length);
         final StoredFields storedFields = searcher.storedFields();
 
         for (final ScoreDoc scoreDoc : topDocs.scoreDocs) {
@@ -287,42 +289,40 @@ public class LuceneIndex extends Index {
             }
 
             final PrimitiveWrapper<?>[] after = toAfter(((FieldDoc) scoreDoc));
-            hits.add(new SearchHit(doc.get("_id"), after, fields));
+            result.add(new SearchHit(doc.get("_id"), after, fields));
         }
-
-        searchResults.setTotalHits(topDocs.totalHits.value());
-        searchResults.setTotalHitsRelation(topDocs.totalHits.relation());
-        searchResults.setHits(hits);
+        return result;
     }
 
-    private void collectFacets(
-            final SearchRequest searchRequest,
-            final IndexSearcher searcher,
-            final FacetsCollector fc,
-            final SearchResults searchResults)
+    private Map<String, Map<String, Number>> collectCounts(
+            final SearchRequest searchRequest, final IndexSearcher searcher, final FacetsCollector fc)
             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()) {
-                final StringDocValuesReaderState state =
-                        new StringDocValuesReaderState(searcher.getIndexReader(), field);
-                final StringValueFacetCounts counts = new StringValueFacetCounts(state, fc);
-                countsMap.put(field, collectFacets(counts, searchRequest.getTopN(), field));
-            }
-            searchResults.setCounts(countsMap);
+        if (!searchRequest.hasCounts()) {
+            return Collections.emptyMap();
+        }
+        var c = searchRequest.counts().get();
+        final Map<String, Map<String, Number>> result = new HashMap<String, Map<String, Number>>(c.size());
+        for (final String field : c) {
+            final StringDocValuesReaderState state = new StringDocValuesReaderState(searcher.getIndexReader(), field);
+            final StringValueFacetCounts counts = new StringValueFacetCounts(state, fc);
+            result.put(field, collectFacets(counts, searchRequest.topNAsInt(), field));
         }
+        return result;
+    }
 
-        if (searchRequest.hasRanges()) {
-            final Map<String, Map<String, Number>> rangesMap = new HashMap<String, Map<String, Number>>(
-                    searchRequest.getRanges().size());
-            for (final Entry<String, List<DoubleRange>> entry :
-                    searchRequest.getRanges().entrySet()) {
-                final DoubleRangeFacetCounts counts = toDoubleRangeFacetCounts(fc, entry.getKey(), entry.getValue());
-                rangesMap.put(entry.getKey(), collectFacets(counts, searchRequest.getTopN(), entry.getKey()));
-            }
-            searchResults.setRanges(rangesMap);
+    private Map<String, Map<String, Number>> collectRanges(
+            final SearchRequest searchRequest, final IndexSearcher searcher, final FacetsCollector fc)
+            throws IOException {
+        if (!searchRequest.hasRanges()) {
+            return Collections.emptyMap();
         }
+        var r = searchRequest.ranges().get();
+        final Map<String, Map<String, Number>> result = new HashMap<String, Map<String, Number>>(r.size());
+        for (final Entry<String, List<DoubleRange>> entry : r.entrySet()) {
+            final DoubleRangeFacetCounts counts = toDoubleRangeFacetCounts(fc, entry.getKey(), entry.getValue());
+            result.put(entry.getKey(), collectFacets(counts, searchRequest.topNAsInt(), entry.getKey()));
+        }
+        return result;
     }
 
     private DoubleRangeFacetCounts toDoubleRangeFacetCounts(
@@ -353,21 +353,22 @@ public class LuceneIndex extends Index {
 
     // 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":
-            case "_id":
-                break;
-            default:
-                sort.add("_id");
-        }
-        return convertSort(sort);
+        return searchRequest
+                .sort()
+                .map(s -> {
+                    final List<String> sort = new ArrayList<String>(s);
+                    final String last = sort.get(sort.size() - 1);
+                    // Append _id field if not already present.
+                    switch (last) {
+                        case "-_id":
+                        case "_id":
+                            break;
+                        default:
+                            sort.add("_id");
+                    }
+                    return convertSort(sort);
+                })
+                .orElse(DEFAULT_SORT);
     }
 
     private Sort convertSort(final List<String> sort) {
@@ -518,23 +519,22 @@ 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.getLocale());
         var queryParser = new NouveauQueryParser(analyzer, pointsConfigMap);
 
-        Query result;
         try {
-            result = queryParser.parse(request.getQuery(), "default");
-            if (request.hasPartition()) {
-                final BooleanQuery.Builder builder = new BooleanQuery.Builder();
-                builder.add(new TermQuery(new Term("_partition", request.getPartition())), Occur.MUST);
-                builder.add(result, Occur.MUST);
-                result = builder.build();
-            }
+            var result = queryParser.parse(request.query(), "default");
+            return request.partition()
+                    .map(partition -> {
+                        final BooleanQuery.Builder builder = new BooleanQuery.Builder();
+                        builder.add(new TermQuery(new Term("_partition", partition)), Occur.MUST);
+                        builder.add(result, Occur.MUST);
+                        return (Query) builder.build();
+                    })
+                    .orElse(result);
         } catch (final QueryNodeException e) {
             throw new WebApplicationException(e.getMessage(), e, Status.BAD_REQUEST);
         }
-        return result;
     }
 
     private LuceneIndexSchema initSchema(IndexWriter writer) {
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..9c02589d8 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
@@ -28,6 +28,7 @@ public class SearchRequestTest {
     @BeforeAll
     public static void setupMapper() {
         mapper = new ObjectMapper();
+        mapper.findAndRegisterModules();
     }
 
     @Test
@@ -47,12 +48,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;
+        return new SearchRequest.Builder()
+                .setQuery("*:*")
+                .setLimit(10)
+                .setCounts(List.of("bar"))
+                .setTopN(5)
+                .setRanges(Map.of("foo", List.of(new DoubleRange("0 to 100 inc", 0.0, true, 100.0, true))))
+                .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 e1d867bd2..b76ff1268 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
@@ -66,10 +66,9 @@ public class IndexManagerTest {
     public void managerReturnsUsableIndex() throws Exception {
         final IndexDefinition indexDefinition = new IndexDefinition();
         manager.create("foo", indexDefinition);
-        var searchRequest = new SearchRequest();
-        searchRequest.setQuery("*:*");
+        var searchRequest = new SearchRequest.Builder().setQuery("*:*").build();
         var searchResults = manager.with("foo", (index) -> index.search(searchRequest));
-        assertThat(searchResults.getTotalHits()).isEqualTo(0);
+        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 83ee15268..e8375184f 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
@@ -79,11 +79,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("*:*");
+            final SearchRequest request = new SearchRequest.Builder()
+                    .setMinUpdateSeq(count)
+                    .setQuery("*:*")
+                    .build();
             final SearchResults results = index.search(request);
-            assertThat(results.getTotalHits()).isEqualTo(count);
+            assertThat(results.totalHits()).isEqualTo(count);
         } finally {
             cleanup(index);
         }
@@ -99,12 +100,13 @@ 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 SearchRequest request = new SearchRequest.Builder()
+                    .setMinUpdateSeq(count)
+                    .setQuery("*:*")
+                    .setSort(List.of("foo"))
+                    .build();
             final SearchResults results = index.search(request);
-            assertThat(results.getTotalHits()).isEqualTo(count);
+            assertThat(results.totalHits()).isEqualTo(count);
         } finally {
             cleanup(index);
         }
@@ -120,12 +122,13 @@ 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 SearchRequest request = new SearchRequest.Builder()
+                    .setMinUpdateSeq(count)
+                    .setQuery("*:*")
+                    .setCounts(List.of("bar"))
+                    .build();
             final SearchResults results = index.search(request);
-            assertThat(results.getCounts()).isEqualTo(Map.of("bar", Map.of("baz", count)));
+            assertThat(results.counts()).isEqualTo(Map.of("bar", Map.of("baz", count)));
         } finally {
             cleanup(index);
         }
@@ -141,16 +144,17 @@ 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(
-                    "bar",
-                    List.of(
-                            new DoubleRange("low", 0.0, true, (double) count / 2, true),
-                            new DoubleRange("high", (double) count / 2, true, (double) count, true))));
+            final SearchRequest request = new SearchRequest.Builder()
+                    .setMinUpdateSeq(count)
+                    .setQuery("*:*")
+                    .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))))
+                    .build();
             final SearchResults results = index.search(request);
-            assertThat(results.getRanges()).isEqualTo(Map.of("bar", Map.of("low", count / 2, "high", count / 2 + 1)));
+            assertThat(results.ranges()).isEqualTo(Map.of("bar", Map.of("low", count / 2, "high", count / 2 + 1)));
         } finally {
             cleanup(index);
         }
@@ -173,13 +177,14 @@ 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 SearchRequest request = new SearchRequest.Builder()
+                    .setMinUpdateSeq(count)
+                    .setQuery("*:*")
+                    .setCounts(List.of("bar"))
+                    .setTopN(1)
+                    .build();
             final SearchResults results = index.search(request);
-            assertThat(results.getCounts()).isEqualTo(Map.of("bar", Map.of("baz", count + 5)));
+            assertThat(results.counts()).isEqualTo(Map.of("bar", Map.of("baz", count + 5)));
         } finally {
             cleanup(index);
         }
@@ -214,9 +219,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("*:*");
+                final SearchRequest request = new SearchRequest.Builder()
+                        .setMinUpdateSeq(1)
+                        .setQuery("*:*")
+                        .build();
                 index.search(request);
             });
         } finally {


(couchdb) 11/11: remove open_or_create_index/2

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b781b273848a184b868467d5722de7e2f957c0f8
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 23:32:59 2026 +0000

    remove open_or_create_index/2
---
 src/nouveau/src/nouveau_index_updater.erl | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/src/nouveau/src/nouveau_index_updater.erl b/src/nouveau/src/nouveau_index_updater.erl
index 763e12d03..62fcb55d3 100644
--- a/src/nouveau/src/nouveau_index_updater.erl
+++ b/src/nouveau/src/nouveau_index_updater.erl
@@ -50,10 +50,11 @@
 update(#index{} = Index) ->
     {ok, Db} = couch_db:open_int(Index#index.dbname, []),
     try
-        case open_or_create_index(Db, Index) of
+        case open_or_create_index(Index) of
             {error, Reason} ->
                 exit({error, Reason});
             {ok, #{} = Info} ->
+                nouveau_util:maybe_create_local_purge_doc(Db, Index),
                 #{<<"update_seq">> := IndexUpdateSeq, <<"purge_seq">> := IndexPurgeSeq} = Info,
                 ChangesSince = couch_db:count_changes_since(Db, IndexUpdateSeq),
                 PurgesSince = couch_db:get_purge_seq(Db) - IndexPurgeSeq,
@@ -214,15 +215,6 @@ open_or_create_index(#index{} = Index) ->
             {error, Reason}
     end.
 
-open_or_create_index(Db, #index{} = Index) ->
-    case open_or_create_index(Index) of
-        {ok, #{} = Info} ->
-            nouveau_util:maybe_create_local_purge_doc(Db, Index),
-            {ok, Info};
-        Else ->
-            Else
-    end.
-
 get_db_info(#index{} = Index) ->
     {ok, Db} = couch_db:open_int(Index#index.dbname, []),
     try


(couchdb) 05/11: improve annotations in BulkUpdateRequest

Posted by rn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit daac383ea42e7bab2bcff8f90023f93252ca67ad
Author: Robert Newson <rn...@apache.org>
AuthorDate: Sat Mar 21 10:06:37 2026 +0000

    improve annotations in BulkUpdateRequest
---
 .../main/java/org/apache/couchdb/nouveau/api/BulkUpdateRequest.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/BulkUpdateRequest.java b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/BulkUpdateRequest.java
index 3077ea8b3..048ba53e2 100644
--- a/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/BulkUpdateRequest.java
+++ b/extra/nouveau/src/main/java/org/apache/couchdb/nouveau/api/BulkUpdateRequest.java
@@ -13,7 +13,7 @@
 
 package org.apache.couchdb.nouveau.api;
 
-import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotEmpty;
 import java.util.List;
 
-public record BulkUpdateRequest(@JsonProperty List<DocumentUpdate> updates) {}
+public record BulkUpdateRequest(@NotEmpty List<DocumentUpdate> updates) {}