一直以为neo4j是使用十字链表(Orthogonal List)来存储图数据的,后面跟踪源代码发现并不是,因为
十字链表是:
结点中单独存放“入边”、“出边”两个指针分别指向结点的第一个入边出边(注意这里的“第一个”指有顺序可以按结点的物理id来排列,因为这会影响到后续边上的“同弧头”的指向)
边上两个指针域名,一个指向现该边同终点的下一条边,一个指向与该边同起点的下一条边
而neo4j中的结构更像是邻接表, 其并没有把入边,出边分开存储,它在结点中只放了一个指向第一个关系的指针,顺着这第一个关系遍历链表得到该结点的所有关系,如果需要指定得到某个结点的入度或出度,需要遍历所有关系通过判断边上的起点终点是否和中心结点相等来判断方向:
代码如下(org\neo4j\kernel\impl\api\store\StoreNodeRelationshipCursor.java):
public boolean next()
{
while ( relationshipId != NO_NEXT_RELATIONSHIP.intValue() )
{
relationshipRecordCursor.next( relationshipId, relationshipRecord, FORCE );
// If we end up on a relationship record that isn't in use there's a good chance there
// have been a concurrent transaction deleting this record under our feet. Since we don't
// reuse relationship ids we can still trust the pointers in this unused record and try
// to chase a used record down the line.
try
{
// Direction check
if ( relationshipRecord.inUse() )
{
if ( direction != Direction.BOTH )
{
switch ( direction )
{
case INCOMING:
{
if ( relationshipRecord.getSecondNode() != fromNodeId )
{
continue;
}
break;
}
case OUTGOING:
{
if ( relationshipRecord.getFirstNode() != fromNodeId )
{
continue;
}
break;
}
default:
throw new IllegalStateException( "Unknown direction: " + direction );
}
}
// Type check
if ( !allowedTypes.test( relationshipRecord.getType() ) )
{
continue;
}
return true;
}
}
finally
{
// Pick next relationship
if ( relationshipRecord.getFirstNode() == fromNodeId )
{
relationshipId = relationshipRecord.getFirstNextRel();
}
else if ( relationshipRecord.getSecondNode() == fromNodeId )
{
relationshipId = relationshipRecord.getSecondNextRel();
}
else
{
throw new InvalidRecordException(
"While loading relationships for Node[" + fromNodeId + "] a Relationship[" +
relationshipRecord.getId() + "] was encountered that had startNode:" + " " +
relationshipRecord.getFirstNode() + " and endNode: " +
relationshipRecord.getSecondNode() + ", i.e. which had neither start nor end node " +
"as the node we're loading relationships for" );
}
// If there are no more relationships, and this is from a dense node, then
// traverse the next group
if ( relationshipId == NO_NEXT_RELATIONSHIP.intValue() && isDense )
{
relationshipId = nextChainStart();
}
}
}
return false;
}