package it.cavallium.dbengine.database.disk; import io.net5.buffer.api.Drop; import io.net5.buffer.api.Owned; import io.net5.buffer.api.Resource; import io.net5.buffer.api.Send; import io.net5.buffer.api.internal.ResourceSupport; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; import java.io.IOException; import java.io.UncheckedIOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import org.apache.lucene.index.Fields; import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReaderContext; import org.apache.lucene.index.MultiReader; import org.apache.lucene.index.StoredFieldVisitor; import org.apache.lucene.index.Term; import org.apache.lucene.search.IndexSearcher; public interface LLIndexSearchers extends Resource { static LLIndexSearchers of(List> indexSearchers) { return new ShardedIndexSearchers(indexSearchers, d -> {}); } static UnshardedIndexSearchers unsharded(Send indexSearcher) { return new UnshardedIndexSearchers(indexSearcher, d -> {}); } List shards(); IndexSearcher shard(int shardIndex); IndexReader allShards(); class UnshardedIndexSearchers extends ResourceSupport implements LLIndexSearchers { private LLIndexSearcher indexSearcher; public UnshardedIndexSearchers(Send indexSearcher, Drop drop) { super(new CloseOnDrop(drop)); this.indexSearcher = indexSearcher.receive(); } @Override public List shards() { return List.of(indexSearcher.getIndexSearcher()); } @Override public IndexSearcher shard(int shardIndex) { if (!isOwned()) { throw attachTrace(new IllegalStateException("UnshardedIndexSearchers must be owned to be used")); } if (shardIndex != -1) { throw new IndexOutOfBoundsException("Shard index " + shardIndex + " is invalid, this is a unsharded index"); } return indexSearcher.getIndexSearcher(); } @Override public IndexReader allShards() { return indexSearcher.getIndexReader(); } public IndexSearcher shard() { return this.shard(-1); } @Override protected RuntimeException createResourceClosedException() { return new IllegalStateException("Closed"); } @Override protected Owned prepareSend() { Send indexSearcher = this.indexSearcher.send(); this.makeInaccessible(); return drop -> new UnshardedIndexSearchers(indexSearcher, drop); } private void makeInaccessible() { this.indexSearcher = null; } private static class CloseOnDrop implements Drop { private final Drop delegate; public CloseOnDrop(Drop drop) { this.delegate = drop; } @Override public void drop(UnshardedIndexSearchers obj) { try { if (obj.indexSearcher != null) obj.indexSearcher.close(); delegate.drop(obj); } finally { obj.makeInaccessible(); } } } } class ShardedIndexSearchers extends ResourceSupport implements LLIndexSearchers { private List indexSearchers; private List indexSearchersVals; public ShardedIndexSearchers(List> indexSearchers, Drop drop) { super(new CloseOnDrop(drop)); this.indexSearchers = new ArrayList<>(indexSearchers.size()); this.indexSearchersVals = new ArrayList<>(indexSearchers.size()); for (Send llIndexSearcher : indexSearchers) { var indexSearcher = llIndexSearcher.receive(); this.indexSearchers.add(indexSearcher); this.indexSearchersVals.add(indexSearcher.getIndexSearcher()); } } @Override public List shards() { if (!isOwned()) { throw attachTrace(new IllegalStateException("ShardedIndexSearchers must be owned to be used")); } return Collections.unmodifiableList(indexSearchersVals); } @Override public IndexSearcher shard(int shardIndex) { if (!isOwned()) { throw attachTrace(new IllegalStateException("ShardedIndexSearchers must be owned to be used")); } if (shardIndex < 0) { throw new IndexOutOfBoundsException("Shard index " + shardIndex + " is invalid"); } return indexSearchersVals.get(shardIndex); } @Override public IndexReader allShards() { if (!isOwned()) { throw attachTrace(new IllegalStateException("ShardedIndexSearchers must be owned to be used")); } var irs = new IndexReader[indexSearchersVals.size()]; for (int i = 0, s = indexSearchersVals.size(); i < s; i++) { irs[i] = indexSearchersVals.get(i).getIndexReader(); } Object2IntOpenHashMap indexes = new Object2IntOpenHashMap<>(); for (int i = 0; i < irs.length; i++) { indexes.put(irs[i], i); } try { return new MultiReader(irs, Comparator.comparingInt(indexes::getInt), false); } catch (IOException ex) { // This shouldn't happen throw new UncheckedIOException(ex); } } @Override protected RuntimeException createResourceClosedException() { return new IllegalStateException("Closed"); } @Override protected Owned prepareSend() { List> indexSearchers = new ArrayList<>(this.indexSearchers.size()); for (LLIndexSearcher indexSearcher : this.indexSearchers) { indexSearchers.add(indexSearcher.send()); } this.makeInaccessible(); return drop -> new ShardedIndexSearchers(indexSearchers, drop); } private void makeInaccessible() { this.indexSearchers = null; this.indexSearchersVals = null; } private static class CloseOnDrop implements Drop { private volatile boolean dropped = false; private final Drop delegate; public CloseOnDrop(Drop drop) { this.delegate = drop; } @Override public void drop(ShardedIndexSearchers obj) { try { assert !dropped; if (obj.indexSearchers != null) { for (LLIndexSearcher indexSearcher : obj.indexSearchers) { if (indexSearcher.isAccessible()) { indexSearcher.close(); } } } dropped = true; delegate.drop(obj); } finally { obj.makeInaccessible(); } } } } }