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

org.neo4j.cypher.operations.CursorUtils Maven / Gradle / Ivy

/*
 * 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.cypher.operations;

import static org.apache.commons.lang3.ArrayUtils.EMPTY_INT_ARRAY;
import static org.apache.commons.lang3.ArrayUtils.indexOf;
import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_LABEL;
import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_NODE;
import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_PROPERTY_KEY;
import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_RELATIONSHIP;
import static org.neo4j.kernel.api.StatementConstants.NO_SUCH_RELATIONSHIP_TYPE;
import static org.neo4j.values.storable.Values.NO_VALUE;

import java.util.Arrays;
import java.util.function.Consumer;
import org.eclipse.collections.api.set.primitive.IntSet;
import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList;
import org.neo4j.cypher.internal.runtime.DbAccess;
import org.neo4j.exceptions.CypherTypeException;
import org.neo4j.exceptions.EntityNotFoundException;
import org.neo4j.graphdb.Direction;
import org.neo4j.internal.kernel.api.CursorFactory;
import org.neo4j.internal.kernel.api.EntityCursor;
import org.neo4j.internal.kernel.api.NodeCursor;
import org.neo4j.internal.kernel.api.PropertyCursor;
import org.neo4j.internal.kernel.api.Read;
import org.neo4j.internal.kernel.api.RelationshipDataAccessor;
import org.neo4j.internal.kernel.api.RelationshipScanCursor;
import org.neo4j.internal.kernel.api.RelationshipTraversalCursor;
import org.neo4j.internal.kernel.api.TokenRead;
import org.neo4j.internal.kernel.api.exceptions.PropertyKeyIdNotFoundKernelException;
import org.neo4j.internal.kernel.api.helpers.RelationshipSelections;
import org.neo4j.io.pagecache.context.CursorContext;
import org.neo4j.kernel.api.StatementConstants;
import org.neo4j.kernel.impl.newapi.Cursors;
import org.neo4j.storageengine.api.PropertySelection;
import org.neo4j.util.CalledFromGeneratedCode;
import org.neo4j.values.AnyValue;
import org.neo4j.values.storable.DurationValue;
import org.neo4j.values.storable.PointValue;
import org.neo4j.values.storable.TemporalValue;
import org.neo4j.values.storable.Value;
import org.neo4j.values.storable.Values;
import org.neo4j.values.virtual.MapValue;
import org.neo4j.values.virtual.MapValueBuilder;
import org.neo4j.values.virtual.RelationshipVisitor;
import org.neo4j.values.virtual.VirtualNodeValue;
import org.neo4j.values.virtual.VirtualRelationshipValue;
import org.neo4j.values.virtual.VirtualValues;

/**
 * Utilities for working with cursors from within generated code
 */
@SuppressWarnings({"Duplicates"})
public final class CursorUtils {
    /**
     * Do not instantiate this class
     */
    private CursorUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * Fetches a given property from a node
     *
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return The value of the given property
     * @throws EntityNotFoundException If the node was deleted in transaction.
     */
    public static Value nodeGetProperty(
            Read read, NodeCursor nodeCursor, long node, PropertyCursor propertyCursor, int prop)
            throws EntityNotFoundException {
        assert node >= NO_SUCH_NODE;
        return nodeGetProperty(read, nodeCursor, node, propertyCursor, prop, true);
    }

    /**
     * Fetches a given property from a node
     *
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @param throwOnDeleted if true and exception will be thrown if node has been deleted
     * @return The value of the given property
     * @throws EntityNotFoundException If the node was deleted in transaction.
     */
    public static Value nodeGetProperty(
            Read read,
            NodeCursor nodeCursor,
            long node,
            PropertyCursor propertyCursor,
            int prop,
            boolean throwOnDeleted)
            throws EntityNotFoundException {
        assert node >= NO_SUCH_NODE;

        if (node == NO_SUCH_NODE) {
            return NO_VALUE;
        }
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return NO_VALUE;
        }
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            if (throwOnDeleted && read.nodeDeletedInTransaction(node)) {
                throw new EntityNotFoundException(
                        String.format("Node with id %d has been deleted in this transaction", node));
            } else {
                return NO_VALUE;
            }
        }
        return nodeGetProperty(nodeCursor, propertyCursor, prop);
    }

    /**
     * Fetches a given property from a node, where the node has already been loaded.
     *
     * @param nodeCursor The node cursor which currently points to the node to get the property from.
     * @param propertyCursor The property cursor to use to read the property.
     * @param prop The property key id
     * @return The value of the property, otherwise {@link Values#NO_VALUE} if not found.
     */
    public static Value nodeGetProperty(NodeCursor nodeCursor, PropertyCursor propertyCursor, int prop) {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return NO_VALUE;
        }
        nodeCursor.properties(propertyCursor, PropertySelection.selection(prop));
        return propertyCursor.next() ? propertyCursor.propertyValue() : NO_VALUE;
    }

    /**
     * Returns a set of property values (or NoValue) from the specified entity cursor.
     *
     * Note, NoValue will be used for tokens that does not exist, including TokenConstants.NO_TOKEN.
     */
    public static Value[] entityGetProperties(EntityCursor entityCursor, PropertyCursor propertyCursor, int[] tokens) {
        assert entityCursor.reference() != StatementConstants.NO_SUCH_ENTITY;

        final Value[] values = emptyPropertyArray(tokens.length);
        entityCursor.properties(propertyCursor, PropertySelection.selection(tokens));
        while (propertyCursor.next()) {
            final int index = indexOf(tokens, propertyCursor.propertyKey());
            values[index] = propertyCursor.propertyValue();
        }
        return values;
    }

    public static Value[] emptyPropertyArray(int len) {
        Value[] values = new Value[len];
        Arrays.fill(values, NO_VALUE);
        return values;
    }

    /**
     * Checks if a given node has the given property
     *
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return true if node has property otherwise false
     */
    public static boolean nodeHasProperty(
            Read read, NodeCursor nodeCursor, long node, PropertyCursor propertyCursor, int prop)
            throws EntityNotFoundException {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return false;
        }
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            return false;
        }
        return nodeHasProperty(nodeCursor, propertyCursor, prop);
    }

    /**
     * Checks if a given node has the given property, where the node has already been loaded.
     *
     * @param nodeCursor The node cursor which currently points to the node to check property existence for.
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return {@code true} if node has property otherwise {@code false}.
     */
    public static boolean nodeHasProperty(NodeCursor nodeCursor, PropertyCursor propertyCursor, int prop) {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return false;
        }
        nodeCursor.properties(propertyCursor, PropertySelection.onlyKeysSelection(prop));
        return propertyCursor.next();
    }

    /**
     * Checks if given node has a given label.
     *
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @param label The id of the label
     * @return {@code true} if the node has the label, otherwise {@code false}
     */
    public static boolean nodeHasLabel(Read read, NodeCursor nodeCursor, long node, int label) {
        if (label == NO_SUCH_LABEL) {
            return false;
        }
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            return false;
        }

        return nodeCursor.hasLabel(label);
    }

    /**
     * Checks if a given node has all the given labels
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @param labels The labels to check for
     * @return {@code true} if the node has all the labels, otherwise {@code false}
     */
    public static boolean nodeHasLabels(Read read, NodeCursor nodeCursor, long node, int[] labels) {
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            return false;
        }

        return nodeHasLabels(nodeCursor, labels);
    }

    /**
     * Checks if a given node has all the given labels
     * @param nodeCursor A node cursor positioned on a particular node
     * @param labels The labels to check for
     * @return {@code true} if the node has all the labels, otherwise {@code false}
     */
    public static boolean nodeHasLabels(NodeCursor nodeCursor, int[] labels) {
        for (int label : labels) {
            if (label == NO_SUCH_LABEL) {
                return false;
            }
            if (!nodeCursor.hasLabel(label)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Checks if given node has any label at all.
     *
     * @param read The current Read instance
     * @param nodeCursor The node cursor to use
     * @param node The id of the node
     * @return {@code true} if the node has the label, otherwise {@code false}
     */
    public static boolean nodeHasALabel(Read read, NodeCursor nodeCursor, long node) {
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            return false;
        }

        return nodeCursor.hasLabel();
    }

    /**
     * Checks if given node has any label at all.
     *
     * @param nodeCursor The node cursor to use
     * @return {@code true} if the node has the label, otherwise {@code false}
     */
    public static boolean nodeHasALabel(NodeCursor nodeCursor) {
        return nodeCursor.hasLabel();
    }

    /**
     * Returns true if any of the specified labels are set on the node with id `node`.
     */
    public static boolean nodeHasAnyLabel(Read read, NodeCursor nodeCursor, long node, int[] labels) {
        read.singleNode(node, nodeCursor);
        if (!nodeCursor.next()) {
            return false;
        }

        return nodeHasAnyLabel(nodeCursor, labels);
    }

    /**
     * Returns true if any of the specified labels are set on the node that `cursor` is pointing at.
     */
    public static boolean nodeHasAnyLabel(NodeCursor cursor, int[] labels) {
        var nodeLabels = cursor.labels();

        for (int label : labels) {
            if (nodeLabels.contains(label)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if given relationship has a given type.
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param type The id of the type
     * @return {@code true} if the relationship has the type, otherwise {@code false}
     */
    @CalledFromGeneratedCode
    public static boolean relationshipHasType(
            Read read, RelationshipScanCursor relationshipCursor, long relationship, int type) {
        if (type == NO_SUCH_RELATIONSHIP_TYPE) {
            return false;
        }
        read.singleRelationship(relationship, relationshipCursor);
        if (!relationshipCursor.next()) {
            return false;
        }

        return relationshipCursor.type() == type;
    }

    /**
     * Checks if given relationship has a given type.
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param types The types to check for
     * @return {@code true} if the relationship has the type, otherwise {@code false}
     */
    public static boolean relationshipHasTypes(
            Read read, RelationshipScanCursor relationshipCursor, long relationship, int[] types) {
        assert types.length > 0;
        int typeToLookFor = types[0];
        for (int i = 1; i < types.length; i++) {
            if (types[i] != typeToLookFor) {
                return false;
            }
        }
        if (typeToLookFor == NO_SUCH_RELATIONSHIP_TYPE) {
            return false;
        }

        read.singleRelationship(relationship, relationshipCursor);
        if (!relationshipCursor.next()) {
            return false;
        }

        return relationshipCursor.type() == typeToLookFor;
    }

    public static boolean relationshipHasTypes(
            Read read, RelationshipScanCursor relationshipCursor, VirtualRelationshipValue relationship, int[] types) {
        assert types.length > 0;
        int typeToLookFor = types[0];
        for (int i = 1; i < types.length; i++) {
            if (types[i] != typeToLookFor) {
                return false;
            }
        }
        if (typeToLookFor == NO_SUCH_RELATIONSHIP_TYPE) {
            return false;
        }

        return new VirtualRelationshipReader(read, relationshipCursor, relationship, true).hasType(typeToLookFor);
    }

    @CalledFromGeneratedCode
    public static boolean relationshipHasTypes(RelationshipScanCursor relationshipCursor, int[] types) {
        assert types.length > 0;
        int typeToLookFor = types[0];
        for (int i = 1; i < types.length; i++) {
            if (types[i] != typeToLookFor) {
                return false;
            }
        }
        if (typeToLookFor == NO_SUCH_RELATIONSHIP_TYPE) {
            return false;
        }

        return relationshipCursor.type() == typeToLookFor;
    }

    public static RelationshipTraversalCursor nodeGetRelationships(
            Read read,
            CursorFactory cursors,
            NodeCursor node,
            long nodeId,
            Direction direction,
            int[] types,
            CursorContext cursorContext) {
        read.singleNode(nodeId, node);
        if (!node.next()) {
            return Cursors.emptyTraversalCursor(read);
        }
        return switch (direction) {
            case OUTGOING -> RelationshipSelections.outgoingCursor(cursors, node, types, cursorContext);
            case INCOMING -> RelationshipSelections.incomingCursor(cursors, node, types, cursorContext);
            case BOTH -> RelationshipSelections.allCursor(cursors, node, types, cursorContext);
        };
    }

    /**
     * Fetches a given property from a relationship
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return The value of the given property
     * @throws EntityNotFoundException If the node cannot be find.
     */
    public static Value relationshipGetProperty(
            Read read,
            RelationshipScanCursor relationshipCursor,
            long relationship,
            PropertyCursor propertyCursor,
            int prop)
            throws EntityNotFoundException {
        return relationshipGetProperty(read, relationshipCursor, relationship, propertyCursor, prop, true);
    }

    /**
     * Fetches a given property from a relationship
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @param throwOnDeleted if true and exception will be thrown if node has been deleted
     * @return The value of the given property
     * @throws EntityNotFoundException If the node cannot be find.
     */
    public static Value relationshipGetProperty(
            Read read,
            RelationshipScanCursor relationshipCursor,
            long relationship,
            PropertyCursor propertyCursor,
            int prop,
            boolean throwOnDeleted)
            throws EntityNotFoundException {
        assert relationship >= NO_SUCH_RELATIONSHIP;

        if (relationship == NO_SUCH_RELATIONSHIP) {
            return NO_VALUE;
        }
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return NO_VALUE;
        }
        read.singleRelationship(relationship, relationshipCursor);
        if (!relationshipCursor.next()) {
            if (throwOnDeleted && read.relationshipDeletedInTransaction(relationship)) {
                throw new EntityNotFoundException(
                        String.format("Relationship with id %d has been deleted in this transaction", relationship));
            } else {
                return NO_VALUE;
            }
        }
        relationshipCursor.properties(propertyCursor, PropertySelection.selection(prop));
        return propertyCursor.next() ? propertyCursor.propertyValue() : NO_VALUE;
    }

    /**
     * Fetches a given property from a relationship
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @param throwOnDeleted if true and exception will be thrown if node has been deleted
     * @return The value of the given property
     * @throws EntityNotFoundException If the node cannot be find.
     */
    public static Value relationshipGetProperty(
            Read read,
            RelationshipScanCursor relationshipCursor,
            VirtualRelationshipValue relationship,
            PropertyCursor propertyCursor,
            int prop,
            boolean throwOnDeleted)
            throws EntityNotFoundException {
        assert relationship.id() >= NO_SUCH_RELATIONSHIP;

        if (relationship.id() == NO_SUCH_RELATIONSHIP) {
            return NO_VALUE;
        }
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return NO_VALUE;
        }
        return new VirtualRelationshipReader(read, relationshipCursor, relationship, throwOnDeleted)
                .property(propertyCursor, prop);
    }

    /**
     * Fetches a given property from a relationship, where the relationship has already been loaded.
     *
     * @param relationshipCursor relationship cursor which currently points to the relationship to get the property from.
     * @param propertyCursor the property cursor to use to read the property.
     * @param prop property key id
     * @return the value of the property, otherwise {@link Values#NO_VALUE} if not found.
     */
    public static Value relationshipGetProperty(
            RelationshipDataAccessor relationshipCursor, PropertyCursor propertyCursor, int prop) {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return NO_VALUE;
        }
        relationshipCursor.properties(propertyCursor, PropertySelection.selection(prop));
        return propertyCursor.next() ? propertyCursor.propertyValue() : NO_VALUE;
    }

    /**
     * Checks if a given relationship has the given property
     *
     * @param read The current Read instance
     * @param relationshipCursor The relationship cursor to use
     * @param relationship The id of the relationship
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return true if relationship has property otherwise false
     */
    public static boolean relationshipHasProperty(
            Read read,
            RelationshipScanCursor relationshipCursor,
            long relationship,
            PropertyCursor propertyCursor,
            int prop)
            throws EntityNotFoundException {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return false;
        }
        read.singleRelationship(relationship, relationshipCursor);
        if (!relationshipCursor.next()) {
            return false;
        }
        relationshipCursor.properties(propertyCursor, PropertySelection.onlyKeysSelection(prop));
        return propertyCursor.next();
    }

    public static boolean relationshipHasProperty(
            Read read,
            RelationshipScanCursor relationshipCursor,
            VirtualRelationshipValue relationship,
            PropertyCursor propertyCursor,
            int prop)
            throws EntityNotFoundException {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return false;
        }
        return new VirtualRelationshipReader(read, relationshipCursor, relationship, true)
                .hasProperty(propertyCursor, prop);
    }

    /**
     * Checks if a given relationship has the given property, where the relationship has already been loaded.
     *
     * @param relationshipCursor The relationship cursor which currently points to the relationship to check property existence for.
     * @param propertyCursor The property cursor to use
     * @param prop The id of the property to find
     * @return {@code true} if relationship has property otherwise {@code false}.
     */
    @CalledFromGeneratedCode
    public static boolean relationshipHasProperty(
            RelationshipDataAccessor relationshipCursor, PropertyCursor propertyCursor, int prop) {
        if (prop == NO_SUCH_PROPERTY_KEY) {
            return false;
        }
        relationshipCursor.properties(propertyCursor, PropertySelection.onlyKeysSelection(prop));
        return propertyCursor.next();
    }

    @CalledFromGeneratedCode
    public static int[] relationshipPropertyIds(
            Read read,
            VirtualRelationshipValue relationship,
            RelationshipScanCursor cursor,
            PropertyCursor propertyCursor) {
        return new VirtualRelationshipReader(read, cursor, relationship).propertyIds(propertyCursor);
    }

    @CalledFromGeneratedCode
    public static MapValue relationshipAsMap(
            Read read,
            TokenRead tokenRead,
            VirtualRelationshipValue relationship,
            RelationshipScanCursor cursor,
            PropertyCursor propertyCursor,
            MapValueBuilder seenProperties,
            IntSet seenPropertyTokens)
            throws PropertyKeyIdNotFoundKernelException {
        return new VirtualRelationshipReader(read, cursor, relationship)
                .asMap(tokenRead, propertyCursor, seenProperties, seenPropertyTokens);
    }

    @CalledFromGeneratedCode
    public static AnyValue propertyGet(
            String key,
            AnyValue container,
            Read read,
            DbAccess dbAccess,
            NodeCursor nodeCursor,
            RelationshipScanCursor relationshipScanCursor,
            PropertyCursor propertyCursor) {
        if (container == NO_VALUE) {
            return NO_VALUE;
        } else if (container instanceof VirtualNodeValue node) {
            return nodeGetProperty(read, nodeCursor, node.id(), propertyCursor, dbAccess.propertyKey(key));
        } else if (container instanceof VirtualRelationshipValue rel) {
            return relationshipGetProperty(
                    read, relationshipScanCursor, rel, propertyCursor, dbAccess.propertyKey(key), true);
        } else if (container instanceof MapValue map) {
            return map.get(key);
        } else if (container instanceof TemporalValue temporal) {
            return temporal.get(key);
        } else if (container instanceof DurationValue duration) {
            return duration.get(key);
        } else if (container instanceof PointValue point) {
            return point.get(key);
        } else {
            if (container instanceof Value value)
                throw CypherTypeException.expectedMap(
                        String.valueOf(value), value.prettyPrint(), CypherTypeValueMapper.valueType(container));
            else
                throw CypherTypeException.expectedMap(
                        String.valueOf(container),
                        String.valueOf(container),
                        CypherTypeValueMapper.valueType(container));
        }
    }

    @CalledFromGeneratedCode
    public static AnyValue[] propertiesGet(
            String[] keys,
            AnyValue container,
            Read read,
            DbAccess dbAccess,
            NodeCursor nodeCursor,
            RelationshipScanCursor relationshipScanCursor,
            PropertyCursor propertyCursor) {
        if (container == NO_VALUE) {
            return emptyPropertyArray(keys.length);
        } else if (container instanceof VirtualNodeValue node) {
            return propertiesGet(propertyKeys(keys, dbAccess), node.id(), read, nodeCursor, propertyCursor);
        } else if (container instanceof VirtualRelationshipValue rel) {
            return propertiesGet(propertyKeys(keys, dbAccess), rel, read, relationshipScanCursor, propertyCursor);
        } else {
            return propertiesGet(keys, container);
        }
    }

    public static AnyValue[] propertiesGet(String[] keys, AnyValue container) {
        if (container instanceof MapValue map) {
            return propertiesGet(keys, map);
        } else if (container instanceof TemporalValue temporal) {
            return propertiesGet(keys, temporal);
        } else if (container instanceof DurationValue duration) {
            return propertiesGet(keys, duration);
        } else if (container instanceof PointValue point) {
            return propertiesGet(keys, point);
        } else {
            if (container instanceof Value value)
                throw CypherTypeException.expectedMap(
                        String.valueOf(value), value.prettyPrint(), CypherTypeValueMapper.valueType(container));
            else
                throw CypherTypeException.expectedMap(
                        String.valueOf(container),
                        String.valueOf(container),
                        CypherTypeValueMapper.valueType(container));
        }
    }

    public static Value[] propertiesGet(
            int[] keys, long node, Read read, NodeCursor nodeCursor, PropertyCursor propertyCursor) {
        read.singleNode(node, nodeCursor);
        if (nodeCursor.next()) {
            return entityGetProperties(nodeCursor, propertyCursor, keys);
        } else if (read.nodeDeletedInTransaction(node)) {
            throw new EntityNotFoundException(
                    String.format("Node with id %d has been deleted in this transaction", node));
        } else {
            return emptyPropertyArray(keys.length);
        }
    }

    public static Value[] propertiesGet(
            int[] keys, long rel, Read read, RelationshipScanCursor relCursor, PropertyCursor propertyCursor) {
        read.singleRelationship(rel, relCursor);
        if (relCursor.next()) {
            return entityGetProperties(relCursor, propertyCursor, keys);
        } else if (read.relationshipDeletedInTransaction(rel)) {
            throw new EntityNotFoundException(
                    String.format("Relationship with id %d has been deleted in this transaction", rel));
        } else {
            return emptyPropertyArray(keys.length);
        }
    }

    public static Value[] propertiesGet(
            int[] keys,
            VirtualRelationshipValue rel,
            Read read,
            RelationshipScanCursor relCursor,
            PropertyCursor propertyCursor) {
        return new VirtualRelationshipReader(read, relCursor, rel, true).properties(keys, propertyCursor);
    }

    public static int[] propertyKeys(String[] keys, DbAccess dbAccess) {
        int[] tokens = new int[keys.length];
        for (int i = 0; i < keys.length; i++) {
            tokens[i] = dbAccess.propertyKey(keys[i]);
        }
        return tokens;
    }

    public static AnyValue[] propertiesGet(String[] keys, MapValue map) {
        var result = new AnyValue[keys.length];
        for (int i = 0; i < keys.length; i++) {
            result[i] = map.get(keys[i]);
        }
        return result;
    }

    public static AnyValue[] propertiesGet(String[] keys, TemporalValue map) {
        var result = new AnyValue[keys.length];
        for (int i = 0; i < keys.length; i++) {
            result[i] = map.get(keys[i]);
        }
        return result;
    }

    public static AnyValue[] propertiesGet(String[] keys, DurationValue map) {
        var result = new AnyValue[keys.length];
        for (int i = 0; i < keys.length; i++) {
            result[i] = map.get(keys[i]);
        }
        return result;
    }

    public static AnyValue[] propertiesGet(String[] keys, PointValue map) {
        var result = new AnyValue[keys.length];
        for (int i = 0; i < keys.length; i++) {
            result[i] = map.get(keys[i]);
        }
        return result;
    }

    public static VirtualRelationshipValue relationshipById(RelationshipDataAccessor cursor) {
        return VirtualValues.relationship(
                cursor.relationshipReference(),
                cursor.sourceNodeReference(),
                cursor.targetNodeReference(),
                cursor.type());
    }

    /**
     * Helper class for efficient reading of data from a VirtualRelationshipValue.
     * 

* This class should only be short-lived and should not be part of any long-lived state and * should only be used to invoke a single method, e.g., * *

{@code
     *  new VirtualRelationshipReader(read, cursor, rel).property(propCursor, prop);
     * }
     * 
*/ static final class VirtualRelationshipReader implements Consumer { private final Read read; // NOTE: storing RelationshipScanCursor as state can be dangerous and this is one reason why we shouldn't keep // instances of this class around. The cursor needs to be kept as a field in order to implement `accept`. private final RelationshipScanCursor cursor; private final VirtualRelationshipValue relationship; private final boolean throwOnDeleted; private boolean isSet; VirtualRelationshipReader(Read read, RelationshipScanCursor cursor, VirtualRelationshipValue relationship) { this(read, cursor, relationship, true); } VirtualRelationshipReader( Read read, RelationshipScanCursor cursor, VirtualRelationshipValue relationship, boolean throwOnDeleted) { this.read = read; this.cursor = cursor; this.relationship = relationship; this.throwOnDeleted = throwOnDeleted; this.isSet = false; } @Override public void accept(RelationshipVisitor relationshipVisitor) { read.singleRelationship(relationship.id(), cursor); if (cursor.next()) { relationshipVisitor.visit(cursor.sourceNodeReference(), cursor.targetNodeReference(), cursor.type()); this.isSet = true; } } private boolean next() { long start = relationship.startNodeId(this); long end = relationship.endNodeId(this); int type = relationship.relationshipTypeId(this); if (!isSet) { read.singleRelationship(relationship.id(), start, type, end, cursor); if (!cursor.next()) { if (throwOnDeleted && read.relationshipDeletedInTransaction(relationship.id())) { throw new EntityNotFoundException(String.format( "Relationship with id %d has been deleted in this transaction", relationship.id())); } else { return false; } } } return true; } public Value property(PropertyCursor propertyCursor, int prop) { if (next()) { cursor.properties(propertyCursor, PropertySelection.selection(prop)); return propertyCursor.next() ? propertyCursor.propertyValue() : NO_VALUE; } else { return NO_VALUE; } } public Value[] properties(int[] keys, PropertyCursor propertyCursor) { if (next()) { return entityGetProperties(cursor, propertyCursor, keys); } else { return emptyPropertyArray(keys.length); } } public boolean hasProperty(PropertyCursor propertyCursor, int prop) { if (next()) { cursor.properties(propertyCursor, PropertySelection.onlyKeysSelection(prop)); return propertyCursor.next(); } else { return false; } } public boolean hasType(int typeToLookFor) { if (next()) { return cursor.type() == typeToLookFor; } else { return false; } } public int[] propertyIds(PropertyCursor propertyCursor) { if (next()) { var res = new IntArrayList(); cursor.properties(propertyCursor); while (propertyCursor.next()) { res.add(propertyCursor.propertyKey()); } return res.toArray(); } else { return EMPTY_INT_ARRAY; } } public MapValue asMap( TokenRead tokenRead, PropertyCursor propertyCursor, MapValueBuilder builder, IntSet seenTokens) throws PropertyKeyIdNotFoundKernelException { if (next()) { cursor.properties(propertyCursor, PropertySelection.ALL_PROPERTIES.excluding(seenTokens::contains)); while (propertyCursor.next()) { builder.add( tokenRead.propertyKeyName(propertyCursor.propertyKey()), propertyCursor.propertyValue()); } return builder.build(); } else { return VirtualValues.EMPTY_MAP; } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy