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

org.neo4j.kernel.impl.newapi.DefaultEntityTokenIndexCursor Maven / Gradle / Ivy

Go to download

Neo4j kernel is a lightweight, embedded Java database designed to store data structured as graphs rather than tables. For more information, see http://neo4j.org.

There is a newer version: 5.25.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [https://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Neo4j is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */
package org.neo4j.kernel.impl.newapi;

import static org.neo4j.collection.PrimitiveLongCollections.iterator;
import static org.neo4j.collection.PrimitiveLongCollections.reverseIterator;
import static org.neo4j.internal.schema.IndexOrder.DESCENDING;
import static org.neo4j.kernel.impl.newapi.Read.NO_ID;

import java.util.NoSuchElementException;
import org.eclipse.collections.api.iterator.LongIterator;
import org.eclipse.collections.api.set.primitive.LongSet;
import org.neo4j.collection.PrimitiveLongCollections;
import org.neo4j.internal.kernel.api.KernelReadTracer;
import org.neo4j.internal.schema.IndexOrder;
import org.neo4j.kernel.api.index.IndexProgressor;
import org.neo4j.kernel.api.txstate.TransactionState;
import org.neo4j.kernel.impl.index.schema.TokenScanValueIndexProgressor;

/**
 * Base for index cursors that can handle scans with IndexOrder.
 */
abstract class DefaultEntityTokenIndexCursor>
        extends IndexCursor implements InternalTokenIndexCursor {
    protected Read read;

    protected long entity;
    protected long entityFromIndex;

    protected int tokenId;
    // Defaulting order to ASCENDING, argument being that if it is important that you do not assume a order you can
    // specify none
    // And if you are indifferent to index order BUT you can treat an index with order then it might be beneficial
    // to assume ascending for functionality like skipUntil
    protected IndexOrder order = IndexOrder.ASCENDING;
    private PeekableLongIterator added;
    private LongSet removed;
    private boolean useMergeSort;
    private final PrimitiveSortedMergeJoin sortedMergeJoin = new PrimitiveSortedMergeJoin();
    private boolean shortcutSecurity;

    DefaultEntityTokenIndexCursor(CursorPool pool) {
        super(pool);
        this.entity = NO_ID;
    }

    @Override
    public abstract void release();

    protected abstract boolean innerNext();

    protected abstract LongIterator createAddedInTxState(TransactionState txState, int token, IndexOrder order);

    /**
     * The returned LongSet must be immutable or a private copy.
     */
    protected abstract LongSet createDeletedInTxState(TransactionState txState, int token);

    protected abstract void traceScan(KernelReadTracer tracer, int token);

    protected abstract void traceNext(KernelReadTracer tracer, long entity);

    protected abstract boolean allowedToSeeAllEntitiesWithToken(int token);

    protected abstract boolean allowedToSeeEntity(long entityReference);

    private PeekableLongIterator peekable(LongIterator actual) {
        return actual != null ? new PeekableLongIterator(actual) : null;
    }

    @Override
    public void initialize(IndexProgressor progressor, int token, IndexOrder order) {
        initialize(progressor);
        if (read.hasTxStateWithChanges()) {
            added = peekable(createAddedInTxState(read.txState(), token, order));
            removed = createDeletedInTxState(read.txState(), token);
            useMergeSort = order != IndexOrder.NONE;
            if (useMergeSort) {
                sortedMergeJoin.initialize(order);
            }
        } else {
            useMergeSort = false;
        }
        tokenId = token;
        initSecurity(token);

        if (tracer != null) {
            traceScan(tracer, token);
        }
        this.order = order;
    }

    @Override
    public void initialize(IndexProgressor progressor, int token, LongIterator added, LongSet removed) {
        initialize(progressor);
        useMergeSort = false;
        this.added = peekable(added);
        this.removed = removed;
        this.tokenId = token;
        initSecurity(token);

        if (tracer != null) {
            traceScan(tracer, token);
        }
    }

    @Override
    public boolean acceptEntity(long reference, int tokenId) {
        if (isRemoved(reference) || !allowed(reference)) {
            return false;
        }
        this.entityFromIndex = reference;
        this.tokenId = tokenId;

        return true;
    }

    @Override
    public boolean next() {
        entity = NO_ID;
        entityFromIndex = NO_ID;
        final var hasNext = useMergeSort ? nextWithOrdering() : nextWithoutOrder();
        if (hasNext && tracer != null) {
            traceNext(tracer, entity);
        }
        return hasNext;
    }

    @Override
    public void closeInternal() {
        if (!isClosed()) {
            closeProgressor();
            entity = NO_ID;
            entityFromIndex = NO_ID;
            tokenId = (int) NO_ID;
            read = null;
            added = null;
            removed = null;
        }
        super.closeInternal();
    }

    @Override
    public boolean isClosed() {
        return isProgressorClosed();
    }

    @Override
    public void setRead(Read read) {
        this.read = read;
    }

    public long entityReference() {
        return entity;
    }

    protected boolean allowed(long reference) {
        return shortcutSecurity || allowedToSeeEntity(reference);
    }

    protected long nextEntity() {
        return entityFromIndex;
    }

    private void initSecurity(int token) {
        shortcutSecurity = allowedToSeeAllEntitiesWithToken(token);
    }

    private boolean nextWithoutOrder() {
        if (added != null && added.hasNext()) {
            entity = added.next();
        } else if (innerNext()) {
            entity = nextEntity();
        }

        return entity != NO_ID;
    }

    private boolean nextWithOrdering() {
        // items from Tx state
        if (sortedMergeJoin.needsA() && added.hasNext()) {
            sortedMergeJoin.setA(added.next());
        }

        // items from index/store
        if (sortedMergeJoin.needsB() && innerNext()) {
            sortedMergeJoin.setB(entityFromIndex);
        }

        final var nextId = sortedMergeJoin.next();
        if (nextId == NO_ID) {
            return false;
        } else {
            entity = nextId;
            return true;
        }
    }

    private boolean isRemoved(long reference) {
        return removed != null && removed.contains(reference);
    }

    protected static LongIterator sortTxState(LongSet frozenAdded, IndexOrder order) {
        return switch (order) {
            case NONE -> frozenAdded.longIterator();
            case ASCENDING, DESCENDING -> sorted(frozenAdded.toSortedArray(), order);
        };
    }

    private static LongIterator sorted(long[] items, IndexOrder order) {
        return DESCENDING == order ? reverseIterator(items) : iterator(items);
    }

    public void skipUntil(long id) {
        TokenScanValueIndexProgressor indexProgressor = (TokenScanValueIndexProgressor) progressor;

        if (order == IndexOrder.NONE) {
            throw new IllegalStateException("IndexOrder " + order + " not supported for skipUntil");
        }

        if (added != null) {
            if (order != DESCENDING) {
                while (added.hasNext() && added.peek() < id) {
                    added.next();
                }
            } else {
                while (added.hasNext() && added.peek() > id) {
                    added.next();
                }
            }
        }

        // Move progressor to correct spot
        indexProgressor.skipUntil(id);
    }

    private static class PeekableLongIterator extends PrimitiveLongCollections.AbstractPrimitiveLongBaseIterator {
        private final LongIterator iterator;

        PeekableLongIterator(LongIterator iterator) {
            this.iterator = iterator;
        }

        @Override
        protected boolean fetchNext() {
            return iterator.hasNext() && next(iterator.next());
        }

        public long peek() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return next;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy