Repository navigation
[Enhancement] Optimize Merge On KNN Field when soft_delete mode #18050
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,15 +32,24 @@ | |
|
|
||
| package org.opensearch.index.engine; | ||
|
|
||
| import org.apache.lucene.codecs.DocValuesProducer; | ||
| import org.apache.lucene.codecs.FieldsProducer; | ||
| import org.apache.lucene.codecs.KnnVectorsReader; | ||
| import org.apache.lucene.index.BinaryDocValues; | ||
| import org.apache.lucene.index.CodecReader; | ||
| import org.apache.lucene.index.DocValuesSkipper; | ||
| import org.apache.lucene.index.FieldInfo; | ||
| import org.apache.lucene.index.FilterCodecReader; | ||
| import org.apache.lucene.index.FilterLeafReader; | ||
| import org.apache.lucene.index.FilteredTermsEnum; | ||
| import org.apache.lucene.index.ImpactsEnum; | ||
| import org.apache.lucene.index.MergePolicy; | ||
| import org.apache.lucene.index.NumericDocValues; | ||
| import org.apache.lucene.index.OneMergeWrappingMergePolicy; | ||
| import org.apache.lucene.index.PostingsEnum; | ||
| import org.apache.lucene.index.SortedDocValues; | ||
| import org.apache.lucene.index.SortedNumericDocValues; | ||
| import org.apache.lucene.index.SortedSetDocValues; | ||
| import org.apache.lucene.index.Terms; | ||
| import org.apache.lucene.index.TermsEnum; | ||
| import org.apache.lucene.search.DocIdSetIterator; | ||
|
|
@@ -49,6 +58,7 @@ | |
|
|
||
| import java.io.IOException; | ||
| import java.util.Iterator; | ||
| import java.util.Objects; | ||
|
|
||
| /** | ||
| * This merge policy drops id field postings for all delete documents this can be | ||
|
|
@@ -62,9 +72,11 @@ | |
| * | ||
| * @opensearch.internal | ||
| */ | ||
| final class PrunePostingsMergePolicy extends OneMergeWrappingMergePolicy { | ||
| final class PrunePostingsAndKnnMergePolicy extends OneMergeWrappingMergePolicy { | ||
|
|
||
| PrunePostingsMergePolicy(MergePolicy in, String idField) { | ||
| public static final String KNN_FIELD = "knn_field"; | ||
|
|
||
| PrunePostingsAndKnnMergePolicy(MergePolicy in, String idField) { | ||
| super(in, toWrap -> new OneMerge(toWrap.segments) { | ||
| @Override | ||
| public CodecReader wrapForMerge(CodecReader reader) throws IOException { | ||
|
|
@@ -156,6 +168,65 @@ public int size() { | |
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public DocValuesProducer getDocValuesReader() { | ||
| DocValuesProducer docValuesProducer = super.getDocValuesReader(); | ||
| if (docValuesProducer == null) { | ||
| return null; | ||
| } | ||
|
|
||
| return new DocValuesProducer() { | ||
| @Override | ||
| public NumericDocValues getNumeric(FieldInfo fieldInfo) throws IOException { | ||
| return docValuesProducer.getNumeric(fieldInfo); | ||
| } | ||
|
|
||
| @Override | ||
| public BinaryDocValues getBinary(FieldInfo fieldInfo) throws IOException { | ||
| BinaryDocValues binaryDocValues = docValuesProducer.getBinary(fieldInfo); | ||
| if (binaryDocValues != null && fieldInfo.attributes().containsKey(KNN_FIELD)) { | ||
| return new MergeFilterBinaryDocValues(binaryDocValues, liveDocs); | ||
| } | ||
| return binaryDocValues; | ||
| } | ||
|
|
||
| @Override | ||
| public SortedDocValues getSorted(FieldInfo fieldInfo) throws IOException { | ||
| return docValuesProducer.getSorted(fieldInfo); | ||
| } | ||
|
|
||
| @Override | ||
| public SortedNumericDocValues getSortedNumeric(FieldInfo fieldInfo) throws IOException { | ||
| return docValuesProducer.getSortedNumeric(fieldInfo); | ||
| } | ||
|
|
||
| @Override | ||
| public SortedSetDocValues getSortedSet(FieldInfo fieldInfo) throws IOException { | ||
| return docValuesProducer.getSortedSet(fieldInfo); | ||
| } | ||
|
|
||
| @Override | ||
| public DocValuesSkipper getSkipper(FieldInfo fieldInfo) throws IOException { | ||
| return docValuesProducer.getSkipper(fieldInfo); | ||
| } | ||
|
|
||
| @Override | ||
| public void checkIntegrity() throws IOException { | ||
| docValuesProducer.checkIntegrity(); | ||
| } | ||
|
|
||
| @Override | ||
| public void close() throws IOException { | ||
| docValuesProducer.close(); | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| @Override | ||
| public KnnVectorsReader getVectorReader() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why don't we pruning the vector field?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. +1 on this.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i need to override the KnnVectorsReader with different type like 'FloatVectorValues' and 'ByteVectorValues' and handle the iterator with different type and merge logic. so i think it would be better to create new PR and handle |
||
| return super.getVectorReader(); | ||
| } | ||
|
|
||
| @Override | ||
| public CacheHelper getCoreCacheHelper() { | ||
| return null; | ||
|
|
@@ -236,4 +307,44 @@ public BytesRef getPayload() throws IOException { | |
| return in.getPayload(); | ||
| } | ||
| } | ||
|
|
||
| private static final class MergeFilterBinaryDocValues extends BinaryDocValues { | ||
|
|
||
| protected final BinaryDocValues in; | ||
| protected final Bits liveDocs; | ||
|
|
||
| protected MergeFilterBinaryDocValues(BinaryDocValues in, Bits liveDocs) { | ||
| Objects.requireNonNull(in); | ||
| this.in = in; | ||
| this.liveDocs = liveDocs; | ||
| } | ||
|
|
||
| public int docID() { | ||
| return this.in.docID(); | ||
| } | ||
|
|
||
| public int nextDoc() throws IOException { | ||
| int docId; | ||
| do { | ||
| docId = in.nextDoc(); | ||
| } while (docId != DocIdSetIterator.NO_MORE_DOCS && liveDocs.get(docId) == false); | ||
| return docId; | ||
| } | ||
|
|
||
| public int advance(int target) throws IOException { | ||
| return this.in.advance(target); | ||
| } | ||
|
|
||
| public boolean advanceExact(int target) throws IOException { | ||
| return this.in.advanceExact(target); | ||
| } | ||
|
|
||
| public long cost() { | ||
| return this.in.cost(); | ||
| } | ||
|
|
||
| public BytesRef binaryValue() throws IOException { | ||
| return this.in.binaryValue(); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if we should expose something of the plugin within the core. Maybe we can provide an extension point in the engine plugin.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@luyuncheng I think we should just make this generic. Why we want to make this specific to k-NN. Docs containing BDV should just be removed if they are deleted. What is the point of having the deleted docs?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good Idea. i think all BinaryDocValues should skipped on merge with soft_deletes.