/** * Creates a relationship with the given id, from the nodes identified by id and of type typeId * * @param id The id of the relationship to create. * @param type The id of the relationship type this relationship will have. * @param firstNodeId The id of the start node. * @param secondNodeId The id of the end node. */ public void relationshipCreate( long id, int type, long firstNodeId, long secondNodeId, RecordAccessSet recordChangeSet) { // TODO could be unnecessary to mark as changed here already, dense nodes may not need to change NodeRecord firstNode = recordChangeSet.getNodeRecords().getOrLoad(firstNodeId, null).forChangingLinkage(); if (!firstNode.inUse()) { throw new IllegalStateException( "First node[" + firstNodeId + "] is deleted and cannot be used to create a relationship"); } NodeRecord secondNode = recordChangeSet.getNodeRecords().getOrLoad(secondNodeId, null).forChangingLinkage(); if (!secondNode.inUse()) { throw new IllegalStateException( "Second node[" + secondNodeId + "] is deleted and cannot be used to create a relationship"); } convertNodeToDenseIfNecessary( firstNode, recordChangeSet.getRelRecords(), recordChangeSet.getRelGroupRecords()); convertNodeToDenseIfNecessary( secondNode, recordChangeSet.getRelRecords(), recordChangeSet.getRelGroupRecords()); RelationshipRecord record = recordChangeSet.getRelRecords().create(id, null).forChangingLinkage(); record.setLinks(firstNodeId, secondNodeId, type); record.setInUse(true); record.setCreated(); connectRelationship( firstNode, secondNode, record, recordChangeSet.getRelRecords(), recordChangeSet.getRelGroupRecords()); }
@Override public Collection<DynamicRecord> put(long[] labelIds, NodeStore nodeStore) { long existingLabelsField = node.getLabelField(); long existingLabelsBits = parseLabelsBody(existingLabelsField); Collection<DynamicRecord> changedDynamicRecords = node.getDynamicLabelRecords(); if (labelField != 0) { // There are existing dynamic label records, get them nodeStore.ensureHeavy(node, existingLabelsBits); changedDynamicRecords = node.getDynamicLabelRecords(); setNotInUse(changedDynamicRecords); } if (!new InlineNodeLabels(labelField, node) .tryInlineInNodeRecord(labelIds, changedDynamicRecords)) { Set<DynamicRecord> allRecords = new HashSet<>(changedDynamicRecords); Collection<DynamicRecord> allocatedRecords = nodeStore.allocateRecordsForDynamicLabels( node.getId(), labelIds, changedDynamicRecords.iterator()); allRecords.addAll(allocatedRecords); node.setLabelField(dynamicPointer(allocatedRecords), allocatedRecords); changedDynamicRecords = allRecords; } return changedDynamicRecords; }
@Override public Iterable<Long> getRelationshipIds(long nodeId) { NodeRecord nodeRecord = getNodeRecord(nodeId); long nextRel = nodeRecord.getNextRel(); List<Long> ids = new ArrayList<Long>(); while (nextRel != Record.NO_NEXT_RELATIONSHIP.intValue()) { RelationshipRecord relRecord = getRelationshipRecord(nextRel); ids.add(relRecord.getId()); long firstNode = relRecord.getFirstNode(); long secondNode = relRecord.getSecondNode(); if (firstNode == nodeId) { nextRel = relRecord.getFirstNextRel(); } else if (secondNode == nodeId) { nextRel = relRecord.getSecondNextRel(); } else { throw new InvalidRecordException( "Node[" + nodeId + "] not part of firstNode[" + firstNode + "] or secondNode[" + secondNode + "]"); } } return ids; }
@Override public Collection<DynamicRecord> remove(long labelId, NodeStore nodeStore) { nodeStore.ensureHeavy(node, parseLabelsBody(labelField)); Collection<DynamicRecord> existingRecords = node.getDynamicLabelRecords(); long[] existingLabelIds = nodeStore.getDynamicLabelsArray(existingRecords); long[] newLabelIds = filter(existingLabelIds, labelId); if (new InlineNodeLabels(labelField, node) .tryInlineInNodeRecord(newLabelIds, existingRecords)) { setNotInUse(existingRecords); } else { Collection<DynamicRecord> newRecords = nodeStore.allocateRecordsForDynamicLabels( node.getId(), newLabelIds, existingRecords.iterator()); node.setLabelField(dynamicPointer(newRecords), existingRecords); if (!newRecords.equals( existingRecords)) { // One less dynamic record, mark that one as not in use for (DynamicRecord record : existingRecords) { if (!newRecords.contains(record)) { record.setInUse(false); record.setLength(0); // so that it will not be made heavy again... } } } } return existingRecords; }
public Iterable<SimpleRelationship> getSimpleRelationships(long nodeId) { NodeRecord nodeRecord = getNodeRecord(nodeId); long nextRel = nodeRecord.getNextRel(); List<SimpleRelationship> rels = new ArrayList<SimpleRelationship>(); while (nextRel != Record.NO_NEXT_RELATIONSHIP.intValue()) { RelationshipRecord relRecord = getRelationshipRecord(nextRel); RelationshipType type = new RelationshipTypeImpl(typeHolder.getName(relRecord.getType())); rels.add( new SimpleRelationship( relRecord.getId(), relRecord.getFirstNode(), relRecord.getSecondNode(), type)); long firstNode = relRecord.getFirstNode(); long secondNode = relRecord.getSecondNode(); if (firstNode == nodeId) { nextRel = relRecord.getFirstNextRel(); } else if (secondNode == nodeId) { nextRel = relRecord.getSecondNextRel(); } else { throw new InvalidRecordException( "Node[" + nodeId + "] not part of firstNode[" + firstNode + "] or secondNode[" + secondNode + "]"); } } return rels; }
@Override public Map<String, Object> getNodeProperties(long nodeId) { NodeRecord record = getNodeRecord(nodeId); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { return getPropertyChain(record.getNextProp()); } return Collections.emptyMap(); }
private void setCorrectNextRel(NodeRecord node, RelationshipRecord rel, long nextRel) { if (node.getId() == rel.getFirstNode()) { rel.setFirstNextRel(nextRel); } if (node.getId() == rel.getSecondNode()) { rel.setSecondNextRel(nextRel); } }
private long internalCreateNode(long nodeId, Map<String, Object> properties) { NodeRecord nodeRecord = new NodeRecord( nodeId, Record.NO_NEXT_RELATIONSHIP.intValue(), Record.NO_NEXT_PROPERTY.intValue()); nodeRecord.setInUse(true); nodeRecord.setCreated(); nodeRecord.setNextProp(createPropertyChain(properties)); getNodeStore().updateRecord(nodeRecord); return nodeId; }
@Override public void setNodeProperties(long node, Map<String, Object> properties) { NodeRecord record = getNodeRecord(node).forChangingData(); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { propertyDeletor.getAndDeletePropertyChain(record, recordAccess.getPropertyRecords()); } record.setNextProp( propertyCreator.createPropertyChain( record, propertiesIterator(properties), recordAccess.getPropertyRecords())); recordAccess.commit(); }
@Override public Collection<DynamicRecord> add(long labelId, NodeStore nodeStore) { nodeStore.ensureHeavy(node, parseLabelsBody(labelField)); Collection<DynamicRecord> existingRecords = node.getDynamicLabelRecords(); long[] existingLabelIds = nodeStore.getDynamicLabelsArray(existingRecords); long[] newLabelIds = LabelIdArray.concatAndSort(existingLabelIds, labelId); Collection<DynamicRecord> changedDynamicRecords = nodeStore.allocateRecordsForDynamicLabels( node.getId(), newLabelIds, existingRecords.iterator()); node.setLabelField(dynamicPointer(changedDynamicRecords), changedDynamicRecords); return changedDynamicRecords; }
@Test public void shouldNotReportMissingPropertyForDeletedRelationshipWithProperty() { // given NodeRecord oldNode1 = add(inUse(new NodeRecord(1, false, NONE, NONE))); NodeRecord oldNode2 = add(inUse(new NodeRecord(2, false, NONE, NONE))); RelationshipRecord oldRel = add(inUse(new RelationshipRecord(42, 1, 2, 7))); oldNode1.setNextRel(oldRel.getId()); oldNode2.setNextRel(oldRel.getId()); PropertyRecord oldProperty = add(inUse(new PropertyRecord(101))); oldProperty.setRelId(oldRel.getId()); oldRel.setNextProp(oldProperty.getId()); NodeRecord newNode1 = add(notInUse(new NodeRecord(1, false, NONE, NONE))); NodeRecord newNode2 = add(notInUse(new NodeRecord(2, false, NONE, NONE))); RelationshipRecord newRel = add(notInUse(new RelationshipRecord(42, 1, 2, 7))); newNode1.setNextRel(newRel.getId()); newNode2.setNextRel(newRel.getId()); PropertyRecord newProperty = add(notInUse(new PropertyRecord(101))); newProperty.setRelId(newRel.getId()); newRel.setNextProp(newProperty.getId()); // when ConsistencyReport.PropertyConsistencyReport report = checkChange(oldProperty, newProperty); // then verifyNoMoreInteractions(report); }
@Override public long[] getIfLoaded() { if (node.isLight()) { return null; } for (DynamicRecord dynamic : node.getDynamicLabelRecords()) { if (dynamic.isLight()) { return null; } } return stripNodeId( (long[]) getRightArray(readFullByteArrayFromHeavyRecords(node.getDynamicLabelRecords(), ARRAY))); }
private long internalCreateNode(long nodeId, Map<String, Object> properties, Label... labels) { NodeRecord nodeRecord = recordAccess.getNodeRecords().create(nodeId, null).forChangingData(); nodeRecord.setInUse(true); nodeRecord.setCreated(); nodeRecord.setNextProp( propertyCreator.createPropertyChain( nodeRecord, propertiesIterator(properties), recordAccess.getPropertyRecords())); if (labels.length > 0) { setNodeLabels(nodeRecord, labels); } recordAccess.commit(); return nodeId; }
@Test public void shouldReadNodeRecords() throws IOException { URL nodeStoreFile = getClass().getResource("exampledb/neostore.nodestore.db"); LegacyNodeStoreReader nodeStoreReader = new LegacyNodeStoreReader(new File(nodeStoreFile.getFile())); assertEquals(1001, nodeStoreReader.getMaxId()); Iterable<NodeRecord> records = nodeStoreReader.readNodeStore(); int nodeCount = 0; for (NodeRecord record : records) { if (record.inUse()) nodeCount++; } assertEquals(501, nodeCount); nodeStoreReader.close(); }
private void convertNodeToDenseIfNecessary( NodeRecord node, RecordAccess<Long, RelationshipRecord, Void> relRecords, RecordAccess<Long, RelationshipGroupRecord, Integer> relGroupRecords) { if (node.isDense()) { return; } long relId = node.getNextRel(); if (relId != Record.NO_NEXT_RELATIONSHIP.intValue()) { RecordProxy<Long, RelationshipRecord, Void> relChange = relRecords.getOrLoad(relId, null); RelationshipRecord rel = relChange.forReadingLinkage(); if (RelationshipCounter.relCount(node.getId(), rel) >= neoStore.getDenseNodeThreshold()) { convertNodeToDenseNode(node, relChange.forChangingLinkage(), relRecords, relGroupRecords); } } }
private void connect(NodeRecord node, RelationshipRecord rel) { if (node.getNextRel() != Record.NO_NEXT_RELATIONSHIP.intValue()) { RelationshipRecord nextRel = getRelationshipStore().getRecord(node.getNextRel()); boolean changed = false; if (nextRel.getFirstNode() == node.getId()) { nextRel.setFirstPrevRel(rel.getId()); changed = true; } if (nextRel.getSecondNode() == node.getId()) { nextRel.setSecondPrevRel(rel.getId()); changed = true; } if (!changed) { throw new InvalidRecordException(node + " dont match " + nextRel); } getRelationshipStore().updateRecord(nextRel); } }
@Override public void setNodeProperties(long node, Map<String, Object> properties) { NodeRecord record = getNodeRecord(node); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { deletePropertyChain(record.getNextProp()); /* * Batch inserter does not make any attempt to maintain the store's * integrity. It makes sense however to keep some things intact where * the cost is relatively low. So here, when we delete the property * chain we first make sure that the node record (or the relationship * record below) does not point anymore to the deleted properties. This * way, if during creation, something goes wrong, it will not have the properties * expected instead of throwing invalid record exceptions. */ record.setNextProp(Record.NO_NEXT_PROPERTY.intValue()); getNodeStore().updateRecord(record); } record.setNextProp(createPropertyChain(properties)); getNodeStore().updateRecord(record); }
private void convertNodeToDenseNode( NodeRecord node, RelationshipRecord firstRel, RecordAccess<Long, RelationshipRecord, Void> relRecords, RecordAccess<Long, RelationshipGroupRecord, Integer> relGroupRecords) { firstRel = relRecords.getOrLoad(firstRel.getId(), null).forChangingLinkage(); node.setDense(true); node.setNextRel(Record.NO_NEXT_RELATIONSHIP.intValue()); long relId = firstRel.getId(); RelationshipRecord relRecord = firstRel; while (relId != Record.NO_NEXT_RELATIONSHIP.intValue()) { locker.getWriteLock(relId); relId = relChain(relRecord, node.getId()).get(relRecord); connectRelationshipToDenseNode(node, relRecord, relRecords, relGroupRecords); if (relId == Record.NO_NEXT_RELATIONSHIP.intValue()) { break; } relRecord = relRecords.getOrLoad(relId, null).forChangingLinkage(); } if (upgradedDenseNodes == null) { upgradedDenseNodes = new ArrayList<>(); } upgradedDenseNodes.add(node); }
private void connectRelationshipToDenseNode( NodeRecord node, RelationshipRecord rel, RecordAccess<Long, RelationshipRecord, Void> relRecords, RecordAccess<Long, RelationshipGroupRecord, Integer> relGroupRecords) { RelationshipGroupRecord group = relGroupGetter .getOrCreateRelationshipGroup(node, rel.getType(), relGroupRecords) .forChangingData(); RelIdArray.DirectionWrapper dir = DirectionIdentifier.wrapDirection(rel, node); long nextRel = dir.getNextRel(group); setCorrectNextRel(node, rel, nextRel); connect(node.getId(), nextRel, rel, relRecords); dir.setNextRel(group, rel.getId()); }
private void connectRelationship( NodeRecord firstNode, NodeRecord secondNode, RelationshipRecord rel) { assert firstNode.getNextRel() != rel.getId(); assert secondNode.getNextRel() != rel.getId(); rel.setFirstNextRel(firstNode.getNextRel()); rel.setSecondNextRel(secondNode.getNextRel()); connect(firstNode, rel); connect(secondNode, rel); firstNode.setNextRel(rel.getId()); secondNode.setNextRel(rel.getId()); }
public RecordProxy<Long, RelationshipGroupRecord, Integer> getRelationshipGroup( NodeRecord node, int type) { long groupId = node.getNextRel(); long previousGroupId = Record.NO_NEXT_RELATIONSHIP.intValue(); Set<Integer> allTypes = new HashSet<>(); while (groupId != Record.NO_NEXT_RELATIONSHIP.intValue()) { RecordProxy<Long, RelationshipGroupRecord, Integer> change = recordChangeSet.getRelGroupRecords().getOrLoad(groupId, type); RelationshipGroupRecord record = change.forReadingData(); record.setPrev(previousGroupId); // not persistent so not a "change" allTypes.add(record.getType()); if (record.getType() == type) { return change; } previousGroupId = groupId; groupId = record.getNext(); } return null; }
private void migrateNodes(NodeStore nodeStore, PropertyWriter propertyWriter) throws IOException { Iterable<NodeRecord> records = legacyStore.getNodeStoreReader().readNodeStore(); // estimate total number of nodes using file size then calc number of dots or percentage // complete for (NodeRecord nodeRecord : records) { reportProgress(nodeRecord.getId()); nodeStore.setHighId(nodeRecord.getId() + 1); if (nodeRecord.inUse()) { long startOfPropertyChain = nodeRecord.getNextProp(); if (startOfPropertyChain != Record.NO_NEXT_RELATIONSHIP.intValue()) { long propertyRecordId = migrateProperties(startOfPropertyChain, propertyWriter); nodeRecord.setNextProp(propertyRecordId); } nodeStore.updateRecord(nodeRecord); } else { nodeStore.freeId(nodeRecord.getId()); } } legacyStore.getNodeStoreReader().close(); }
@Test public void shouldNotReportMissingPropertyForDeletedNodeWithProperty() { // given PropertyRecord oldProperty = add(inUse(new PropertyRecord(10))); NodeRecord oldNode = add(inUse(new NodeRecord(20, false, 0, 0))); oldProperty.setNodeId(oldNode.getId()); oldNode.setNextProp(oldProperty.getId()); PropertyRecord newProperty = add(notInUse(new PropertyRecord(10))); NodeRecord newNode = add(notInUse(new NodeRecord(20, false, 0, 0))); newProperty.setNodeId(newNode.getId()); newNode.setNextProp(newProperty.getId()); // when ConsistencyReport.PropertyConsistencyReport report = checkChange(oldProperty, newProperty); // then verifyNoMoreInteractions(report); }
@Override public String toString() { return after.toString(); }
@Override public long[] get(NodeStore nodeStore) { nodeStore.ensureHeavy(node, getFirstDynamicRecordId()); return nodeStore.getDynamicLabelsArray(node.getDynamicLabelRecords()); }
public void updateFirstRelationships() { for (RecordProxy<Long, NodeRecord, Void> change : recordChangeSet.getNodeRecords().changes()) { NodeRecord record = change.forReadingLinkage(); txState.setFirstIds(record.getId(), record.getNextRel(), record.getNextProp()); } }
private void connectRelationship( NodeRecord firstNode, NodeRecord secondNode, RelationshipRecord rel, RecordAccess<Long, RelationshipRecord, Void> relRecords, RecordAccess<Long, RelationshipGroupRecord, Integer> relGroupRecords) { // Assertion interpreted: if node is a normal node and we're trying to create a // relationship that we already have as first rel for that node --> error assert firstNode.getNextRel() != rel.getId() || firstNode.isDense(); assert secondNode.getNextRel() != rel.getId() || secondNode.isDense(); if (!firstNode.isDense()) { rel.setFirstNextRel(firstNode.getNextRel()); } if (!secondNode.isDense()) { rel.setSecondNextRel(secondNode.getNextRel()); } if (!firstNode.isDense()) { connect(firstNode, rel, relRecords); } else { connectRelationshipToDenseNode(firstNode, rel, relRecords, relGroupRecords); } if (!secondNode.isDense()) { if (firstNode.getId() != secondNode.getId()) { connect(secondNode, rel, relRecords); } else { rel.setFirstInFirstChain(true); rel.setSecondPrevRel(rel.getFirstPrevRel()); } } else if (firstNode.getId() != secondNode.getId()) { connectRelationshipToDenseNode(secondNode, rel, relRecords, relGroupRecords); } if (!firstNode.isDense()) { firstNode.setNextRel(rel.getId()); } if (!secondNode.isDense()) { secondNode.setNextRel(rel.getId()); } }
private void connect( NodeRecord node, RelationshipRecord rel, RecordAccess<Long, RelationshipRecord, Void> relRecords) { connect(node.getId(), node.getNextRel(), rel, relRecords); }
public NodeCommand init(NodeRecord before, NodeRecord after) { setup(after.getId(), Mode.fromRecordState(after)); this.before = before; this.after = after; return this; }