All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.elasticsearch.common.lucene.uid.UidField Maven / Gradle / Ivy

There is a newer version: 8.14.1
Show newest version
/*
 * Licensed to Elastic Search and Shay Banon under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. Elastic Search licenses this
 * file to you 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.elasticsearch.common.lucene.uid;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PayloadAttribute;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.*;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.Numbers;
import org.elasticsearch.common.lucene.Lucene;
import org.elasticsearch.index.mapper.internal.UidFieldMapper;

import java.io.IOException;
import java.io.Reader;

/**
 *
 */
// TODO: LUCENE 4 UPGRADE: Store version as doc values instead of as a payload.
public class UidField extends Field {

    public static class DocIdAndVersion {
        public final int docId;
        public final long version;
        public final AtomicReaderContext reader;

        public DocIdAndVersion(int docId, long version, AtomicReaderContext reader) {
            this.docId = docId;
            this.version = version;
            this.reader = reader;
        }
    }

    // this works fine for nested docs since they don't have the payload which has the version
    // so we iterate till we find the one with the payload
    public static DocIdAndVersion loadDocIdAndVersion(AtomicReaderContext context, Term term) {
        int docId = Lucene.NO_DOC;
        try {
            Terms terms = context.reader().terms(term.field());
            if (terms == null) {
                return null;
            }
            final TermsEnum termsEnum = terms.iterator(null);
            if (termsEnum == null) {
                return null;
            }
            if (!termsEnum.seekExact(term.bytes(), true)) {
                return null;
            }
            DocsAndPositionsEnum uid = termsEnum.docsAndPositions(context.reader().getLiveDocs(), null, DocsAndPositionsEnum.FLAG_PAYLOADS);
            if (uid == null || uid.nextDoc() == DocIdSetIterator.NO_MORE_DOCS) {
                return null; // no doc
            }
            // Note, only master docs uid have version payload, so we can use that info to not
            // take them into account
            do {
                docId = uid.docID();
                uid.nextPosition();
                if (uid.getPayload() == null) {
                    continue;
                }
                if (uid.getPayload().length < 8) {
                    continue;
                }
                byte[] payload = new byte[uid.getPayload().length];
                System.arraycopy(uid.getPayload().bytes, uid.getPayload().offset, payload, 0, uid.getPayload().length);
                return new DocIdAndVersion(docId, Numbers.bytesToLong(payload), context);
            } while (uid.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
            return new DocIdAndVersion(docId, -2, context);
        } catch (Exception e) {
            return new DocIdAndVersion(docId, -2, context);
        }
    }

    /**
     * Load the version for the uid from the reader, returning -1 if no doc exists, or -2 if
     * no version is available (for backward comp.)
     */
    public static long loadVersion(AtomicReaderContext context, Term term) {
        try {
            Terms terms = context.reader().terms(term.field());
            if (terms == null) {
                return -1;
            }
            final TermsEnum termsEnum = terms.iterator(null);
            if (termsEnum == null) {
                return -1;
            }
            if (!termsEnum.seekExact(term.bytes(), true)) {
                return -1;
            }
            DocsAndPositionsEnum uid = termsEnum.docsAndPositions(context.reader().getLiveDocs(), null, DocsAndPositionsEnum.FLAG_PAYLOADS);
            if (uid == null || uid.nextDoc() == DocIdSetIterator.NO_MORE_DOCS) {
                return -1;
            }
            // Note, only master docs uid have version payload, so we can use that info to not
            // take them into account
            do {
                uid.nextPosition();
                if (uid.getPayload() == null) {
                    continue;
                }
                if (uid.getPayload().length < 8) {
                    continue;
                }
                byte[] payload = new byte[uid.getPayload().length];
                System.arraycopy(uid.getPayload().bytes, uid.getPayload().offset, payload, 0, uid.getPayload().length);
                return Numbers.bytesToLong(payload);
            } while (uid.nextDoc() != DocIdSetIterator.NO_MORE_DOCS);
            return -2;
        } catch (Exception e) {
            return -2;
        }
    }

    private String uid;

    private long version;

    public UidField(String uid) {
        this(UidFieldMapper.NAME, uid, 0);
    }

    public UidField(String name, String uid, long version) {
        super(name, UidFieldMapper.Defaults.FIELD_TYPE);
        this.uid = uid;
        this.version = version;
        this.tokenStream = new UidPayloadTokenStream(this);
    }

    public String uid() {
        return this.uid;
    }

    public void setUid(String uid) {
        this.uid = uid;
    }

    @Override
    public String stringValue() {
        return uid;
    }

    @Override
    public Reader readerValue() {
        return null;
    }

    public long version() {
        return this.version;
    }

    public void version(long version) {
        this.version = version;
    }

    @Override
    public TokenStream tokenStream(Analyzer analyzer) throws IOException {
        return tokenStream;
    }

    public static final class UidPayloadTokenStream extends TokenStream {

        private final PayloadAttribute payloadAttribute = addAttribute(PayloadAttribute.class);
        private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);

        private final UidField field;

        private boolean added = false;

        public UidPayloadTokenStream(UidField field) {
            this.field = field;
        }

        @Override
        public void reset() throws IOException {
            added = false;
        }

        @Override
        public final boolean incrementToken() throws IOException {
            if (added) {
                return false;
            }
            termAtt.setLength(0);
            termAtt.append(field.uid);
            payloadAttribute.setPayload(new BytesRef(Numbers.longToBytes(field.version())));
            added = true;
            return true;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy