blob: c43976110400d986cef7b4f7d50c632443393f48 [file] [log] [blame]
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package org.apache.couchdb.nouveau.lucene9;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.apache.couchdb.nouveau.api.DocumentDeleteRequest;
import org.apache.couchdb.nouveau.api.DocumentUpdateRequest;
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.SearchRequest;
import org.apache.couchdb.nouveau.api.SearchResults;
import org.apache.couchdb.nouveau.api.StringField;
import org.apache.couchdb.nouveau.core.Index;
import org.apache.couchdb.nouveau.core.UpdatesOutOfOrderException;
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.SearcherManager;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
public class Lucene9IndexTest {
protected final Index setup(final Path path) throws IOException {
final IndexDefinition indexDefinition = new IndexDefinition();
indexDefinition.setDefaultAnalyzer("standard");
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 SearcherManager searcherManager = new SearcherManager(writer, null);
return new Lucene9Index(analyzer, writer, 0L, 0L, searcherManager);
}
protected final void cleanup(final Index index) throws IOException {
index.setDeleteOnClose(true);
index.close();
}
@Test
public void testOpenClose(@TempDir Path path) throws IOException {
final Index index = setup(path);
cleanup(index);
}
@Test
public void testSearching(@TempDir Path path) throws IOException {
final Index index = setup(path);
try {
final int count = 100;
List<String> ids = new ArrayList<String>(count);
for (int i = 1; i <= count; i++) {
final Collection<Field> fields = List.of(new StringField("foo", "bar", false));
final DocumentUpdateRequest request = new DocumentUpdateRequest(i - 1, i, null, fields);
final String id = "doc" + i;
ids.add(id);
index.update(id, request);
}
ids.sort(String::compareTo);
final SearchRequest request = new SearchRequest();
request.setQuery("*:*");
request.setLimit(count);
final SearchResults results = index.search(request);
assertThat(results.getTotalHits()).isEqualTo(count);
for (int i = 0; i < count; i++) {
var hit = results.getHits().get(i);
assertThat(hit.getId()).isEqualTo(ids.get(i));
assertThat(hit.getSeq()).isEqualTo(Long.parseLong(ids.get(i).substring(3)));
}
} finally {
cleanup(index);
}
}
@Test
public void testSort(@TempDir Path path) throws IOException {
final Index index = setup(path);
try {
final int count = 100;
for (int i = 1; i <= count; i++) {
final Collection<Field> fields = List.of(new StringField("foo", "bar", false));
final DocumentUpdateRequest request = new DocumentUpdateRequest(i - 1, i, null, fields);
index.update("doc" + i, request);
}
final SearchRequest request = new SearchRequest();
request.setQuery("*:*");
request.setSort(List.of("foo<string>"));
final SearchResults results = index.search(request);
assertThat(results.getTotalHits()).isEqualTo(count);
} finally {
cleanup(index);
}
}
@Test
public void testCounts(@TempDir Path path) throws IOException {
Index index = setup(path);
try {
final int count = 100;
for (int i = 1; i <= count; i++) {
final Collection<Field> fields = List.of(new StringField("bar", "baz", false));
final DocumentUpdateRequest request = new DocumentUpdateRequest(i - 1, i, null, fields);
index.update("doc" + i, request);
}
final SearchRequest request = new SearchRequest();
request.setQuery("*:*");
request.setCounts(List.of("bar"));
final SearchResults results = index.search(request);
assertThat(results.getCounts()).isEqualTo(Map.of("bar", Map.of("baz", count)));
} finally {
cleanup(index);
}
}
@Test
public void testRanges(@TempDir Path path) throws IOException {
Index index = setup(path);
try {
final int count = 100;
for (int i = 1; i <= count; i++) {
final Collection<Field> fields = List.of(new DoubleField("bar", (double) i, false));
final DocumentUpdateRequest request = new DocumentUpdateRequest(i - 1, i, null, fields);
index.update("doc" + i, request);
}
final SearchRequest request = new SearchRequest();
request.setQuery("*:*");
request.setRanges(Map.of(
"bar",
List.of(
new DoubleRange("low", 0.0, true, (double) count / 2, true),
new DoubleRange("high", (double) count / 2, true, (double) count, true))));
final SearchResults results = index.search(request);
assertThat(results.getRanges()).isEqualTo(Map.of("bar", Map.of("low", count / 2, "high", count / 2 + 1)));
} finally {
cleanup(index);
}
}
@Test
public void testOutOfOrder(@TempDir Path path) throws IOException {
Index index = setup(path);
try {
final Collection<Field> fields = Collections.emptyList();
// get match seq wrong
assertThrows(
UpdatesOutOfOrderException.class,
() -> index.update("foo", new DocumentUpdateRequest(1, 2, null, fields)));
// Go to 2.
index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
// Should be prevented from going down to 1.
assertThrows(
UpdatesOutOfOrderException.class,
() -> index.update("foo", new DocumentUpdateRequest(2, 1, null, fields)));
} finally {
cleanup(index);
}
}
@Test
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);
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);
} finally {
cleanup(index);
}
}
@Test
public void testDelete(@TempDir Path path) throws IOException {
Index index = setup(path);
try {
final Collection<Field> fields = List.of(new DoubleField("bar", 12.0, false));
index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
index.commit();
IndexInfo info = index.info();
assertThat(info.getNumDocs()).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);
} finally {
cleanup(index);
}
}
@Test
public void testPurge(@TempDir Path path) throws IOException {
Index index = setup(path);
try {
final Collection<Field> fields = List.of(new DoubleField("bar", 12.0, false));
index.update("foo", new DocumentUpdateRequest(0, 2, null, fields));
index.commit();
IndexInfo info = index.info();
assertThat(info.getNumDocs()).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);
} finally {
cleanup(index);
}
}
}