summaryrefslogtreecommitdiff
path: root/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
diff options
context:
space:
mode:
Diffstat (limited to 'nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java')
-rw-r--r--nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java147
1 files changed, 147 insertions, 0 deletions
diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
new file mode 100644
index 000000000..4273582b6
--- /dev/null
+++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/resources/IndexResource.java
@@ -0,0 +1,147 @@
+//
+// 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.resources;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.couchdb.nouveau.api.DocumentDeleteRequest;
+import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
+import org.apache.couchdb.nouveau.api.IndexDefinition;
+import org.apache.couchdb.nouveau.api.IndexInfo;
+import org.apache.couchdb.nouveau.api.SearchRequest;
+import org.apache.couchdb.nouveau.api.SearchResults;
+import org.apache.couchdb.nouveau.core.IndexLoader;
+import org.apache.couchdb.nouveau.core.IndexManager;
+import org.apache.couchdb.nouveau.lucene9.Lucene9AnalyzerFactory;
+import org.apache.couchdb.nouveau.lucene9.Lucene9Index;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.index.IndexWriterConfig;
+import org.apache.lucene.misc.store.DirectIODirectory;
+import org.apache.lucene.search.SearcherFactory;
+import org.apache.lucene.search.SearcherManager;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.FSDirectory;
+
+import com.codahale.metrics.annotation.ExceptionMetered;
+import com.codahale.metrics.annotation.Metered;
+import com.codahale.metrics.annotation.ResponseMetered;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import jakarta.ws.rs.Consumes;
+import jakarta.ws.rs.DELETE;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.POST;
+import jakarta.ws.rs.PUT;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.PathParam;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;;
+
+@Path("/index/{name}")
+@Metered
+@ResponseMetered
+@ExceptionMetered(cause = IOException.class)
+@Consumes(MediaType.APPLICATION_JSON)
+@Produces(MediaType.APPLICATION_JSON)
+public final class IndexResource {
+
+ private final IndexManager indexManager;
+ private final SearcherFactory searcherFactory;
+
+ public IndexResource(final IndexManager indexManager, final SearcherFactory searcherFactory) {
+ this.indexManager = Objects.requireNonNull(indexManager);
+ this.searcherFactory = Objects.requireNonNull(searcherFactory);
+ }
+
+ @PUT
+ public void createIndex(@PathParam("name") String name, @NotNull @Valid IndexDefinition indexDefinition)
+ throws IOException {
+ indexManager.create(name, indexDefinition);
+ }
+
+ @DELETE
+ @Path("/doc/{docId}")
+ public void deleteDoc(@PathParam("name") String name, @PathParam("docId") String docId,
+ @NotNull @Valid DocumentDeleteRequest request) throws Exception {
+ indexManager.with(name, indexLoader(), (index) -> {
+ index.delete(docId, request);
+ return null;
+ });
+ }
+
+ @DELETE
+ public void deletePath(@PathParam("name") String path, @Valid final List<String> exclusions) throws IOException {
+ indexManager.deleteAll(path, exclusions);
+ }
+
+ @GET
+ public IndexInfo indexInfo(@PathParam("name") String name) throws Exception {
+ return indexManager.with(name, indexLoader(), (index) -> {
+ return index.info();
+ });
+ }
+
+ @POST
+ @Path("/search")
+ public SearchResults searchIndex(@PathParam("name") String name,
+ @NotNull @Valid SearchRequest request)
+ throws Exception {
+ return indexManager.with(name, indexLoader(), (index) -> {
+ return index.search(request);
+ });
+ }
+
+ @PUT
+ @Path("/doc/{docId}")
+ public void updateDoc(@PathParam("name") String name, @PathParam("docId") String docId,
+ @NotNull @Valid DocumentUpdateRequest request)
+ throws Exception {
+ indexManager.with(name, indexLoader(), (index) -> {
+ index.update(docId, request);
+ return null;
+ });
+ }
+
+ private IndexLoader indexLoader() {
+ return (path, indexDefinition) -> {
+ final Analyzer analyzer = Lucene9AnalyzerFactory.fromDefinition(indexDefinition);
+ final Directory dir = new DirectIODirectory(FSDirectory.open(path));
+ final IndexWriterConfig config = new IndexWriterConfig(analyzer);
+ config.setUseCompoundFile(false);
+ final IndexWriter writer = new IndexWriter(dir, config);
+ final long updateSeq = getUpdateSeq(writer);
+ final SearcherManager searcherManager = new SearcherManager(writer, searcherFactory);
+ return new Lucene9Index(analyzer, writer, updateSeq, searcherManager);
+ };
+ }
+
+ private static long getUpdateSeq(final IndexWriter writer) throws IOException {
+ final Iterable<Map.Entry<String, String>> commitData = writer.getLiveCommitData();
+ if (commitData == null) {
+ return 0L;
+ }
+ for (Map.Entry<String, String> entry : commitData) {
+ if (entry.getKey().equals("update_seq")) {
+ return Long.parseLong(entry.getValue());
+ }
+ }
+ return 0L;
+ }
+
+}