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

org.neo4j.cypher.operations.PathValueBuilder 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.neo4j.values.storable.Values.NO_VALUE;
import static org.neo4j.values.virtual.VirtualValues.pathReference;

import java.util.ArrayList;
import java.util.function.Consumer;
import org.neo4j.cypher.internal.runtime.DbAccess;
import org.neo4j.exceptions.CypherTypeException;
import org.neo4j.internal.kernel.api.RelationshipScanCursor;
import org.neo4j.util.CalledFromGeneratedCode;
import org.neo4j.values.AnyValue;
import org.neo4j.values.storable.Value;
import org.neo4j.values.virtual.ListValue;
import org.neo4j.values.virtual.RelationshipVisitor;
import org.neo4j.values.virtual.VirtualNodeValue;
import org.neo4j.values.virtual.VirtualRelationshipValue;
import org.neo4j.values.virtual.VirtualValues;

/**
 * Builder for building paths from generated code, used when the length of the path is not known at compile time.
 * 

* NOTE: this class is designed to be easy-to-use from generated code rather than from code typed by more or less * anthropic beings, so refactor with some care. The PathValueBuilder is not allowed to be reused, once the path is * built the instance should be discarded with. */ @SuppressWarnings({"unused", "WeakerAccess"}) public class PathValueBuilder implements Consumer { private boolean hasBeenBuilt = false; private final ArrayList nodes = new ArrayList<>(); private final ArrayList rels = new ArrayList<>(); private final DbAccess dbAccess; private final RelationshipScanCursor cursor; private boolean seenNoValue; public PathValueBuilder(DbAccess dbAccess, RelationshipScanCursor cursor) { this.dbAccess = dbAccess; this.cursor = cursor; } /** * Creates a PathValue or NO_VALUE if any NO_VALUES has been encountered. * * @return a PathValue or NO_VALUE if any NO_VALUES has been encountered */ public AnyValue build() { if (hasBeenBuilt) { throw new IllegalStateException("This PathValueBuilder has already been used and reuse is not allowed."); } hasBeenBuilt = true; return seenNoValue ? NO_VALUE : pathReference(nodes, rels); } /** * Reads a node from the given offset of a (Trail group variable) List and adds it to the path * * @param value the list to get relationship from * @param offset in the list */ @CalledFromGeneratedCode public void addNodeFromList(AnyValue value, int offset) { if (notNoValue(value)) { if (value instanceof ListValue listValue) { addNode(listValue.value(offset)); } else { if (value instanceof Value v) throw CypherTypeException.expectedListFound( String.valueOf(v), v.prettyPrint(), CypherTypeValueMapper.valueType(value)); else throw CypherTypeException.expectedListFound( String.valueOf(value), String.valueOf(value), CypherTypeValueMapper.valueType(value)); } } } /** * Adds node to the path * * @param value the node to add */ @CalledFromGeneratedCode public void addNode(AnyValue value) { if (notNoValue(value)) { addNode((VirtualNodeValue) value); } } /** * Reads a relationship from the given offset of a (Trail group variable) List and adds it to the path * * @param value the list to get relationship from * @param offset in the list */ @CalledFromGeneratedCode public void addRelationshipFromList(AnyValue value, int offset) { if (notNoValue(value)) { if (value instanceof ListValue listValue) { addRelationship(listValue.value(offset)); } else { if (value instanceof Value v) throw CypherTypeException.expectedListFound( String.valueOf(v), v.prettyPrint(), CypherTypeValueMapper.valueType(value)); else throw CypherTypeException.expectedListFound( String.valueOf(value), String.valueOf(value), CypherTypeValueMapper.valueType(value)); } } } @CalledFromGeneratedCode public void addRelationship(AnyValue value) { if (notNoValue(value)) { addRelationship((VirtualRelationshipValue) value); } } @CalledFromGeneratedCode public void addRelationship(VirtualRelationshipValue value) { rels.add(value); } /** * Adds node to the path * * @param nodeValue the node to add */ @CalledFromGeneratedCode public void addNode(VirtualNodeValue nodeValue) { nodes.add(nodeValue); } /** * Adds incoming relationship to the path * * @param value the incoming relationship to add */ @CalledFromGeneratedCode public void addIncoming(AnyValue value) { if (notNoValue(value)) { addIncoming((VirtualRelationshipValue) value); } } /** * Adds incoming relationship to the path * * @param relationship the incoming relationship to add */ @CalledFromGeneratedCode public void addIncoming(VirtualRelationshipValue relationship) { nodes.add(VirtualValues.node(relationship.startNodeId(this))); rels.add(relationship); } /** * Adds outgoing relationship to the path * * @param value the outgoing relationship to add */ @CalledFromGeneratedCode public void addOutgoing(AnyValue value) { if (notNoValue(value)) { addOutgoing((VirtualRelationshipValue) value); } } /** * Adds outgoing relationship to the path * * @param relationship the outgoing relationship to add */ @CalledFromGeneratedCode public void addOutgoing(VirtualRelationshipValue relationship) { nodes.add(VirtualValues.node(relationship.endNodeId(this))); rels.add(relationship); } private void add(VirtualRelationshipValue relationship, VirtualNodeValue nextNode) { rels.add(relationship); nodes.add(nextNode); } /** * Adds undirected relationship to the path * * @param value the undirected relationship to add */ @CalledFromGeneratedCode public void addUndirected(AnyValue value) { if (notNoValue(value)) { addUndirected((VirtualRelationshipValue) value); } } /** * Adds undirected relationship to the path * * @param relationship the undirected relationship to add */ @CalledFromGeneratedCode public void addUndirected(VirtualRelationshipValue relationship) { var previous = nodes.get(nodes.size() - 1); long start = relationship.startNodeId(this); long end = relationship.endNodeId(this); if (previous.id() == start) { add(relationship, VirtualValues.node(end)); } else { add(relationship, VirtualValues.node(start)); } } /** * Adds multiple incoming relationships to the path * * @param value the incoming relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleIncoming(AnyValue value, AnyValue target) { if (notNoValue(value) && notNoValue(target)) { addMultipleIncoming((ListValue) value, (VirtualNodeValue) target); } } /** * Adds multiple incoming relationships to the path * * @param relationships the incoming relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleIncoming(ListValue relationships, VirtualNodeValue target) { if (relationships.isEmpty()) { // nothing to do here return; } AnyValue last = relationships.last(); relationships.forEach(r -> { if (r == last) { if (notNoValue(last)) { nodes.add(target); rels.add(((VirtualRelationshipValue) last)); } } else { addIncoming(r); } }); } /** * Adds multiple incoming relationships to the path * * @param value the incoming relationships to add */ @CalledFromGeneratedCode public void addMultipleIncoming(AnyValue value) { if (notNoValue(value)) { addMultipleIncoming((ListValue) value); } } /** * Adds multiple incoming relationships to the path * * @param relationships the incoming relationships to add */ @CalledFromGeneratedCode public void addMultipleIncoming(ListValue relationships) { if (relationships.isEmpty()) { // nothing to do here return; } relationships.forEach(this::addIncoming); } /** * Adds multiple outgoing relationships to the path * * @param value the outgoing relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleOutgoing(AnyValue value, AnyValue target) { if (notNoValue(value) && notNoValue(target)) { addMultipleOutgoing((ListValue) value, (VirtualNodeValue) target); } } /** * Adds multiple outgoing relationships to the path * * @param relationships the outgoing relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleOutgoing(ListValue relationships, VirtualNodeValue target) { if (relationships.isEmpty()) { // nothing to do here return; } AnyValue last = relationships.last(); relationships.forEach(r -> { if (r == last) { if (notNoValue(last)) { rels.add(((VirtualRelationshipValue) last)); nodes.add(target); } } else { addOutgoing(r); } }); } /** * Adds multiple outgoing relationships to the path * * @param value the outgoing relationships to add */ @CalledFromGeneratedCode public void addMultipleOutgoing(AnyValue value) { if (notNoValue(value)) { addMultipleOutgoing((ListValue) value); } } /** * Adds multiple outgoing relationships to the path * * @param relationships the outgoing relationships to add */ @CalledFromGeneratedCode public void addMultipleOutgoing(ListValue relationships) { if (relationships.isEmpty()) { // nothing to do here return; } relationships.forEach(this::addOutgoing); } /** * Adds multiple undirected relationships to the path * * @param value the undirected relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleUndirected(AnyValue value, AnyValue target) { if (notNoValue(value) && notNoValue(target)) { addMultipleUndirected((ListValue) value, (VirtualNodeValue) target); } } /** * Adds multiple undirected relationships to the path * * @param relationships the undirected relationships to add * @param target the final target node of the path */ @CalledFromGeneratedCode public void addMultipleUndirected(ListValue relationships, VirtualNodeValue target) { if (relationships.isEmpty()) { // nothing to add return; } AnyValue last = relationships.value(relationships.actualSize() - 1); relationships.forEach(r -> { if (r == last) { if (notNoValue(last)) { rels.add(((VirtualRelationshipValue) last)); nodes.add(target); } } else { addUndirected(r); } }); } /** * Adds multiple undirected relationships to the path * * @param value the undirected relationships to add */ @CalledFromGeneratedCode public void addMultipleUndirected(AnyValue value) { if (notNoValue(value)) { addMultipleUndirected((ListValue) value); } } /** * Adds multiple undirected relationships to the path * * @param relationships the undirected relationships to add */ @CalledFromGeneratedCode public void addMultipleUndirected(ListValue relationships) { if (relationships.isEmpty()) { // nothing to add return; } relationships.forEach(this::addUndirected); } public void addNoValue() { this.seenNoValue = true; } @Override public void accept(RelationshipVisitor relationshipVisitor) { dbAccess.singleRelationship(relationshipVisitor.id(), cursor); // This ignores that a relationship might have been deleted here, this is weird but it is backwards compatible cursor.next(); relationshipVisitor.visit(cursor.sourceNodeReference(), cursor.targetNodeReference(), cursor.type()); } private boolean notNoValue(AnyValue value) { if (!seenNoValue && value == NO_VALUE) { seenNoValue = true; } return !seenNoValue; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy