More flexible delete and query api (#1790)

Replaces the filter and delete by repository api's with a more flexible api, which allows to filter and delete by any id part.
This commit is contained in:
Sebastian Sdorra
2021-09-01 16:19:19 +02:00
committed by GitHub
parent ea7964d224
commit 70fba6c990
14 changed files with 346 additions and 102 deletions

View File

@@ -94,7 +94,7 @@ public class RepositoryIndexer implements Indexer<Repository> {
if (Repository.class.equals(index.getDetails().getType())) {
index.delete().byId(Id.of(Repository.class, repository.getId()));
} else {
index.delete().byRepository(repository);
index.delete().by(Repository.class, repository).execute();
}
};
}

View File

@@ -24,10 +24,12 @@
package sonia.scm.search;
import sonia.scm.repository.Repository;
final class FieldNames {
private FieldNames(){}
static final String ID = "_id";
static final String REPOSITORY = "_repository";
static final String REPOSITORY = Names.field(Repository.class);
static final String PERMISSION = "_permission";
}

View File

@@ -31,17 +31,18 @@ import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
import static sonia.scm.search.FieldNames.ID;
import static sonia.scm.search.FieldNames.PERMISSION;
import static sonia.scm.search.FieldNames.REPOSITORY;
class LuceneIndex<T> implements Index<T>, AutoCloseable {
@@ -127,8 +128,8 @@ class LuceneIndex<T> implements Index<T>, AutoCloseable {
@Override
public void byId(Id<T> id) {
try {
long count = writer.deleteDocuments(idTerm(id));
LOG.debug("delete {} document(s) by id {} from index {}", count, id, details);
LOG.debug("delete document(s) by id {} from index {}", id, details);
writer.deleteDocuments(idTerm(id));
} catch (IOException e) {
throw new SearchEngineException("failed to delete document from index", e);
}
@@ -137,26 +138,43 @@ class LuceneIndex<T> implements Index<T>, AutoCloseable {
@Override
public void all() {
try {
long count = writer.deleteAll();
LOG.debug("deleted all {} documents from index {}", count, details);
LOG.debug("deleted all documents from index {}", details);
writer.deleteAll();
} catch (IOException ex) {
throw new SearchEngineException("failed to delete documents by type " + searchableType.getName() + " from index", ex);
}
}
@Override
public void byRepository(String repositoryId) {
public DeleteBy by(Class<?> type, String id) {
return new LuceneDeleteBy(type, id);
}
}
private class LuceneDeleteBy implements DeleteBy {
private final Map<Class<?>, String> map = new HashMap<>();
private LuceneDeleteBy(Class<?> type, String id) {
map.put(type, id);
}
@Override
public DeleteBy and(Class<?> type, String id) {
map.put(type, id);
return this;
}
@Override
public void execute() {
Query query = Queries.filterQuery(map);
try {
long count = writer.deleteDocuments(repositoryTerm(repositoryId));
LOG.debug("deleted {} documents by repository {} from index {}", count, repositoryId, details);
} catch (IOException ex) {
throw new SearchEngineException("failed to delete documents by repository " + repositoryId + " from index", ex);
LOG.debug("delete document(s) by query {} from index {}", query, details);
writer.deleteDocuments(query);
} catch (IOException e) {
throw new SearchEngineException("failed to delete document from index", e);
}
}
@Nonnull
private Term repositoryTerm(String repositoryId) {
return new Term(REPOSITORY, repositoryId);
}
}
}

View File

@@ -48,4 +48,8 @@ final class Names {
}
return nameFromAnnotation;
}
public static String field(Class<?> type) {
return "_" + create(type);
}
}

View File

@@ -29,7 +29,7 @@ import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import java.util.Optional;
import java.util.Map;
import static org.apache.lucene.search.BooleanClause.Occur.MUST;
@@ -38,18 +38,30 @@ final class Queries {
private Queries() {
}
private static Query repositoryQuery(String repositoryId) {
return new TermQuery(new Term(FieldNames.REPOSITORY, repositoryId));
}
static Query filter(Query query, QueryBuilder.QueryParams params) {
Optional<String> repositoryId = params.getRepositoryId();
if (repositoryId.isPresent()) {
return new BooleanQuery.Builder()
.add(query, MUST)
.add(repositoryQuery(repositoryId.get()), MUST)
.build();
Map<Class<?>, String> filters = params.getFilters();
if (!filters.isEmpty()) {
BooleanQuery.Builder builder = builder(filters);
builder.add(query, MUST);
return builder.build();
}
return query;
}
static Query filterQuery(Map<Class<?>, String> filters) {
return builder(filters).build();
}
private static BooleanQuery.Builder builder(Map<Class<?>, String> filters) {
BooleanQuery.Builder builder = new BooleanQuery.Builder();
for (Map.Entry<Class<?>, String> e : filters.entrySet()) {
Term term = createTerm(e.getKey(), e.getValue());
builder.add(new TermQuery(term), MUST);
}
return builder;
}
private static Term createTerm(Class<?> type, String id) {
return new Term(Names.field(type), id);
}
}

View File

@@ -28,6 +28,7 @@ import com.google.common.annotations.VisibleForTesting;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -70,6 +71,10 @@ class SharableIndexWriter {
return writer.deleteDocuments(term);
}
long deleteDocuments(Query query) throws IOException {
return writer.deleteDocuments(query);
}
long deleteAll() throws IOException {
return writer.deleteAll();
}