private void receiveRelationships( Iterable<RelationshipRecord> rels, ArrayMap<Integer, RelIdArray> newRelationshipMap, List<RelationshipImpl> relsList, DirectionWrapper dir, boolean hasLoops) { for (RelationshipRecord rel : rels) { long relId = rel.getId(); RelationshipImpl relImpl = relCache.get(relId); RelationshipType type = null; int typeId; if (relImpl == null) { typeId = rel.getType(); type = getRelationshipTypeById(typeId); assert type != null; relImpl = newRelationshipImpl( relId, rel.getFirstNode(), rel.getSecondNode(), type, typeId, false); relsList.add(relImpl); } else { typeId = relImpl.getTypeId(); type = getRelationshipTypeById(typeId); } RelIdArray relationshipSet = newRelationshipMap.get(typeId); if (relationshipSet == null) { relationshipSet = hasLoops ? new RelIdArrayWithLoops(typeId) : new RelIdArray(typeId); newRelationshipMap.put(typeId, relationshipSet); } relationshipSet.add(relId, dir); } }
@Override public BatchRelationship apply(RelationshipRecord relRecord) { RelationshipType type = new RelationshipTypeImpl(relationshipTypeTokens.nameOf(relRecord.getType())); return new BatchRelationship( relRecord.getId(), relRecord.getFirstNode(), relRecord.getSecondNode(), type); }
protected Relationship getRelationshipByIdOrNull(long relId) { RelationshipImpl relationship = relCache.get(relId); if (relationship != null) { return new RelationshipProxy(relId, relationshipLookups); } ReentrantLock loadLock = lockId(relId); try { relationship = relCache.get(relId); if (relationship != null) { return new RelationshipProxy(relId, relationshipLookups); } RelationshipRecord data = persistenceManager.loadLightRelationship(relId); if (data == null) { return null; } int typeId = data.getType(); RelationshipType type = getRelationshipTypeById(typeId); if (type == null) { throw new NotFoundException( "Relationship[" + data.getId() + "] exist but relationship type[" + typeId + "] not found."); } final long startNodeId = data.getFirstNode(); final long endNodeId = data.getSecondNode(); relationship = newRelationshipImpl(relId, startNodeId, endNodeId, type, typeId, false); relCache.put(relationship); return new RelationshipProxy(relId, relationshipLookups); } finally { loadLock.unlock(); } }
@Override public BatchRelationship getRelationshipById(long relId) { RelationshipRecord record = getRelationshipRecord(relId); RelationshipType type = new RelationshipTypeImpl(typeHolder.getName(record.getType())); return new BatchRelationship( record.getId(), record.getFirstNode(), record.getSecondNode(), type); }
@Override public void invalidateCache(CacheAccessBackDoor cacheAccess) { cacheAccess.removeRelationshipFromCache(getKey()); /* * If isRecovered() then beforeUpdate is the correct one UNLESS this is the second time this command * is executed, where it might have been actually written out to disk so the fields are already -1. So * we still need to check. * If !isRecovered() then beforeUpdate is the same as record, so we are still ok. * We don't check for !inUse() though because that is implicit in the call of this method. * The above is a hand waiving proof that the conditions that lead to the patchDeletedRelationshipNodes() * in the if below are the same as in RelationshipCommand.execute() so it should be safe. */ if (beforeUpdate.getFirstNode() != -1 || beforeUpdate.getSecondNode() != -1) { cacheAccess.patchDeletedRelationshipNodes( getKey(), beforeUpdate.getFirstNode(), beforeUpdate.getFirstNextRel(), beforeUpdate.getSecondNode(), beforeUpdate.getSecondNextRel()); } if (record.getFirstNode() != -1 || record.getSecondNode() != -1) { cacheAccess.removeNodeFromCache(record.getFirstNode()); cacheAccess.removeNodeFromCache(record.getSecondNode()); } }
/** * 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 BatchRelationship getRelationshipById(long relId) { RelationshipRecord record = getRelationshipRecord(relId).forReadingData(); RelationshipType type = new RelationshipTypeImpl(relationshipTypeTokens.nameOf(record.getType())); return new BatchRelationship( record.getId(), record.getFirstNode(), record.getSecondNode(), type); }
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); } }
@Override public Map<String, Object> getRelationshipProperties(long relId) { RelationshipRecord record = getRelationshipRecord(relId); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { return getPropertyChain(record.getNextProp()); } return Collections.emptyMap(); }
private static RelationshipConnection relChain(RelationshipRecord rel, long nodeId) { if (rel.getFirstNode() == nodeId) { return RelationshipConnection.START_NEXT; } if (rel.getSecondNode() == nodeId) { return RelationshipConnection.END_NEXT; } throw new RuntimeException(nodeId + " neither start not end node in " + rel); }
@Override public Map<String, Object> getRelationshipProperties(long relId) { RelationshipRecord record = recordAccess.getRelRecords().getOrLoad(relId, null).forChangingData(); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { return getPropertyChain(record.getNextProp()); } return Collections.emptyMap(); }
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 void setRelationshipProperties(long rel, Map<String, Object> properties) { RelationshipRecord record = recordAccess.getRelRecords().getOrLoad(rel, null).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(); }
@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 void setRelationshipProperties(long rel, Map<String, Object> properties) { RelationshipRecord record = getRelationshipRecord(rel); if (record.getNextProp() != Record.NO_NEXT_PROPERTY.intValue()) { deletePropertyChain(record.getNextProp()); /* * See setNodeProperties above for an explanation of what goes on * here */ record.setNextProp(Record.NO_NEXT_PROPERTY.intValue()); getRelationshipStore().updateRecord(record); } record.setNextProp(createPropertyChain(properties)); getRelationshipStore().updateRecord(record); }
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()); }
@Test public void shouldReadRelationshipRecords() throws IOException { URL nodeStoreFile = getClass().getResource("exampledb/neostore.relationshipstore.db"); LegacyRelationshipStoreReader relationshipStoreReader = new LegacyRelationshipStoreReader(new File(nodeStoreFile.getFile())); assertEquals(1500, relationshipStoreReader.getMaxId()); Iterable<RelationshipRecord> records = relationshipStoreReader.readRelationshipStore(); int relationshipCount = 0; for (RelationshipRecord record : records) { if (record.inUse()) relationshipCount++; } assertEquals(500, relationshipCount); relationshipStoreReader.close(); }
@Override public long createRelationship( long node1, long node2, RelationshipType type, Map<String, Object> properties) { long id = neoStore.getRelationshipStore().nextId(); int typeId = getOrCreateRelationshipTypeToken(type); relationshipCreator.relationshipCreate(id, typeId, node1, node2, recordAccess); if (properties != null && !properties.isEmpty()) { RelationshipRecord record = recordAccess.getRelRecords().getOrLoad(id, null).forChangingData(); record.setNextProp( propertyCreator.createPropertyChain( record, propertiesIterator(properties), recordAccess.getPropertyRecords())); } recordAccess.commit(); return id; }
public RelationshipCommand init(RelationshipRecord record) { setup(record.getId(), Mode.fromRecordState(record)); this.record = record; // the default (common) case is that the record to be written is complete and not from // recovery or HA this.beforeUpdate = record; return this; }
@Test public void shouldReportPropertyNotReferencedFromRelationshipWithChain() throws Exception { // given PropertyRecord oldProperty = notInUse(new PropertyRecord(42)); PropertyRecord newProperty = inUse(new PropertyRecord(42)); RelationshipRecord rel = add(inUse(new RelationshipRecord(1, 10, 20, 0))); PropertyRecord a = add(inUse(new PropertyRecord(1))); PropertyRecord b = add(inUse(new PropertyRecord(2))); a.setNextProp(b.getId()); b.setPrevProp(a.getId()); rel.setNextProp(a.getId()); newProperty.setRelId(rel.getId()); // when ConsistencyReport.PropertyConsistencyReport report = checkChange(oldProperty, newProperty); // then verify(report).ownerDoesNotReferenceBack(); verifyNoMoreInteractions(report); }
@Override public long createRelationship( long node1, long node2, RelationshipType type, Map<String, Object> properties) { NodeRecord firstNode = getNodeRecord(node1); NodeRecord secondNode = getNodeRecord(node2); int typeId = typeHolder.getTypeId(type.name()); if (typeId == -1) { typeId = createNewRelationshipType(type.name()); } long id = getRelationshipStore().nextId(); RelationshipRecord record = new RelationshipRecord(id, node1, node2, typeId); record.setInUse(true); record.setCreated(); connectRelationship(firstNode, secondNode, record); getNodeStore().updateRecord(firstNode); getNodeStore().updateRecord(secondNode); record.setNextProp(createPropertyChain(properties)); getRelationshipStore().updateRecord(record); return id; }
@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; }
public RelationshipImpl getRelationshipForProxy(long relId, LockType lock) { if (lock != null) { lock.acquire(getTransactionState(), new RelationshipProxy(relId, relationshipLookups)); } RelationshipImpl relationship = relCache.get(relId); if (relationship != null) { return relationship; } ReentrantLock loadLock = lockId(relId); try { relationship = relCache.get(relId); if (relationship != null) { return relationship; } RelationshipRecord data = persistenceManager.loadLightRelationship(relId); if (data == null) { throw new NotFoundException(format("Relationship %d not found", relId)); } int typeId = data.getType(); RelationshipType type = getRelationshipTypeById(typeId); if (type == null) { throw new NotFoundException( "Relationship[" + data.getId() + "] exist but relationship type[" + typeId + "] not found."); } relationship = newRelationshipImpl( relId, data.getFirstNode(), data.getSecondNode(), type, typeId, false); // relCache.put( relId, relationship ); relCache.put(relationship); return relationship; } finally { loadLock.unlock(); } }
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 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 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()); }
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); } }
private void migrateRelationships( RelationshipStore relationshipStore, PropertyWriter propertyWriter) throws IOException { long nodeMaxId = legacyStore.getNodeStoreReader().getMaxId(); Iterable<RelationshipRecord> records = legacyStore.getRelationshipStoreReader().readRelationshipStore(); for (RelationshipRecord relationshipRecord : records) { reportProgress(nodeMaxId + relationshipRecord.getId()); relationshipStore.setHighId(relationshipRecord.getId() + 1); if (relationshipRecord.inUse()) { long startOfPropertyChain = relationshipRecord.getNextProp(); if (startOfPropertyChain != Record.NO_NEXT_RELATIONSHIP.intValue()) { long propertyRecordId = migrateProperties(startOfPropertyChain, propertyWriter); relationshipRecord.setNextProp(propertyRecordId); } relationshipStore.updateRecord(relationshipRecord); } else { relationshipStore.freeId(relationshipRecord.getId()); } } legacyStore.getRelationshipStoreReader().close(); }
@Override public Long apply(RelationshipRecord relRecord) { return relRecord.getId(); }
private void connect( long nodeId, long firstRelId, RelationshipRecord rel, RecordAccess<Long, RelationshipRecord, Void> relRecords) { long newCount = 1; if (firstRelId != Record.NO_NEXT_RELATIONSHIP.intValue()) { locker.getWriteLock(firstRelId); RelationshipRecord firstRel = relRecords.getOrLoad(firstRelId, null).forChangingLinkage(); boolean changed = false; if (firstRel.getFirstNode() == nodeId) { newCount = firstRel.getFirstPrevRel() + 1; firstRel.setFirstPrevRel(rel.getId()); firstRel.setFirstInFirstChain(false); changed = true; } if (firstRel.getSecondNode() == nodeId) { newCount = firstRel.getSecondPrevRel() + 1; firstRel.setSecondPrevRel(rel.getId()); firstRel.setFirstInSecondChain(false); changed = true; } if (!changed) { throw new InvalidRecordException(nodeId + " doesn't match " + firstRel); } } // Set the relationship count if (rel.getFirstNode() == nodeId) { rel.setFirstPrevRel(newCount); rel.setFirstInFirstChain(true); } if (rel.getSecondNode() == nodeId) { rel.setSecondPrevRel(newCount); rel.setFirstInSecondChain(true); } }