Neo4j
Maven导入
<dependency>
<groupId>org.neo4j</groupId>
<artifactId>neo4j</artifactId>
<version>3.5.0</version>
</dependency>
使用嵌入数据库
File dbpath = new File("target/neo4j.db");
GraphDatabaseService graphDb = new GraphDatabaseFactory().newEmbeddedDatabase(dbpath);
registerShutdownHook(graphDb);
停止数据库
graphDb.shutdown();
添加数据库关闭钩子
private static void registerShutdownHook( final GraphDatabaseService graphDb )
{
// Registers a shutdown hook for the Neo4j instance so that it
// shuts down nicely when the VM exits (even if you "Ctrl-C" the
// running application).
Runtime.getRuntime().addShutdownHook( new Thread()
{
@Override
public void run()
{
graphDb.shutdown();
}
} );
}
GraphDatabaseService
实例可以在多个线程之间共享。但请注意,您无法创建指向同一数据库的多个实例。
加载Neo4j属性文件
GraphDatabaseService graphDb = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder( testDirectory.databaseDir() )
.loadPropertiesFromFile( pathToConfig + "neo4j.conf" )
.newGraphDatabase();
设置配置
GraphDatabaseService graphDb = new GraphDatabaseFactory()
.newEmbeddedDatabaseBuilder( testDirectory.databaseDir() )
.setConfig( GraphDatabaseSettings.pagecache_memory, "512M" )
.setConfig( GraphDatabaseSettings.string_block_size, "60" )
.setConfig( GraphDatabaseSettings.array_block_size, "300" )
.newGraphDatabase();
节点、关系
在进行任何有关数据库的存取操作过程中,都必须开启事务管理。
创建关系类型enum
private enum RelTypes implements RelationshipType
{
KNOWS
}
创建节点
try(Transaction tx = graphDb.beginTx()){
Label label = Label.label("Person");
Node first = graphDb.createNode(label);
first.setProperty("name", "first");
Node second = graphDb.createNode(label);
second.setProperty("name", "second");
Relationship relationship = first.createRelationshipTo(second, RelTypes.KNOWS);
System.out.println("first.getProperties(\"name\") = " + first.getProperties("name"));
System.out.println("second.getProperties(\"name\") = " + second.getProperties("name"));
System.out.println("relationship.getType() = " + relationship.getType());
tx.success();
}
查询、更新
try(Transaction tx = graphDb.beginTx()){
Label label = Label.label("Person");
Node node = graphDb.findNode(label, "name", "second");
System.out.println("node.getProperties(\"name\") = " + node.getProperties("name"));
node.setProperty("name", "My Name");
node.setProperty("sex", "fale");
System.out.println("node.getProperties(\"name\") = " + node.getProperties("name"));
System.out.println("node.getProperties(\"sex\") = " + node.getProperties("sex"));
tx.success();
}
删除关系、节点
删除数据,执行相关实体的delete() 方法即可。在删除数据时,要注意删除数据的执行规则:
如果存在关系,则必须先删除关系,再删除节点,或者同时删除;
try(Transaction tx = graphDb.beginTx()){
Label label = Label.label("Person");
Node first = graphDb.findNode(label, "name", "first");
Relationship relationship = first.getSingleRelationship(RelTypes.KNOWS, Direction.OUTGOING);
System.out.println("delete node name is " + first.getProperty("name")
+ ", relationship is KNOWS, end node name is "
+ relationship.getEndNode().getProperty("name"));
relationship.delete();
relationship.getEndNode().delete();
first.delete();
tx.success();
}
属性
节点和关系都可以具有属性。
属性是命名值,其中名称是字符串。属性值可以是基元或一种基本类型的数组。例如String
,int
和int[]
的值是有效的特性。
类型 | 描述 |
---|---|
boolean |
|
byte |
8位整数 |
short |
16位整数 |
int |
32位整数 |
long |
64位整数 |
float |
32位IEEE 754浮点数 |
double |
64位IEEE 754浮点数 |
char |
表示Unicode字符的16位无符号整数 |
org.neo4j.graphdb.spatial.Point |
给定坐标系中的2D或3D点对象。 |
java.time.LocalDate |
即时捕捉日期,但不是时间,也不是时区。 |
java.time.OffsetTime |
即时捕捉时间和时区偏移,但不是日期。 |
java.time.LocalTime |
即时捕捉时间,但不是日期,也不是时区。 |
java.time.ZonedDateTime |
即时捕捉日期,时间和时区。 |
java.time.LocalDateTime |
即时捕捉日期和时间,但不是时区。 |
java.time.temporal.TemporalAmount |
时间量。这捕获了两个时刻之间的时间差异。 |
标签
与RDBMS相比,标签就相当于表名。标签不仅仅是节点进行分门别类的标识,更为重要的是能对所有节点进行分组,从而更好的管理节点,提高节点的查询性能。
索引
同一事务中不允许进行架构更改和数据更改。每个事务必须更改架构或数据,但不能同时更改两者。
模式索引
private static void createIndex(GraphDatabaseService graphDb){
IndexDefinition indexDefinition;
try(Transaction tx = graphDb.beginTx()){
Schema schema = graphDb.schema();
indexDefinition = schema.indexFor(Label.label("User")).on("name").create();
tx.success();
}
}
首次创建索引时会异步填充索引。可以使用核心API等待索引填充完成:
try ( Transaction tx = graphDb.beginTx() )
{
Schema schema = graphDb.schema();
schema.awaitIndexOnline( indexDefinition, 10, TimeUnit.SECONDS );
}
也可以查询索引填充的进度:
try ( Transaction tx = graphDb.beginTx() )
{
Schema schema = graphDb.schema();
System.out.println( String.format( "Percent complete: %1.0f%%",
schema.getIndexPopulationProgress(indexDefinition)
.getCompletedPercentage()));
}
删除索引
try(Transaction tx = graphDb.beginTx()){
Label label = Label.label("User");
for(IndexDefinition indexDefinition: graphDb.schema().getIndexes(label)){
indexDefinition.drop();
}
tx.success();
}
System.out.println( "Drop index success." );
模式约束
添加唯一约束
try(Transaction tx = graphDb.beginTx()){
Schema schema = graphDb.schema();
schema.constraintFor(Label.label("User")).
assertPropertyIsUnique("name").create();
tx.success();
}
删除约束
try(Transaction tx = graphDb.beginTx()){
Label label = Label.label("User");
for(ConstraintDefinition constraintDefinition:graphDb.schema().getConstraints(label)){
constraintDefinition.drop();
}
tx.success();
}
遍历
遍历框架由除几个主要接口:TraversalDescription
,Evaluator
,Traverser
和Uniqueness
TraversalDescription
所述TraversalDescription是用于定义和初始化遍历的主界面。它并不意味着由遍历框架的用户实现,而是由遍历框架的实现提供,作为用户描述遍历的方式。 TraversalDescription
实例是不可变的,并且TraversalDescription
与使用方法的参数调用方法的对象相比,它的方法返回一个被修改的new 。
Evaluator
评估器用于在每个位置(表示为a Path
)决定:遍历是否继续,和/或节点是否应包括在结果中。给定a Path
,它要求遍历遍历的四个动作之一:
Evaluation.INCLUDE_AND_CONTINUE
:在结果中包含此节点并继续遍历Evaluation.INCLUDE_AND_PRUNE
:在结果中包含此节点,但不继续遍历Evaluation.EXCLUDE_AND_CONTINUE
:从结果中排除此节点,但继续遍历Evaluation.EXCLUDE_AND_PRUNE
:从结果中排除此节点,不要继续遍历
可以添加多个评估者。请注意,将为遍历器遇到的所有位置调用评估程序,即使对于起始节点也是如此。
Traverser
Uniqueness
设置在遍历期间如何在唯一性中重新访问位置的规则。如果未设置,则默认为NODE_GLOBAL。
可以向TraversalDescription提供唯一性,以指示在什么情况下遍历可以重新访问图中的相同位置。可以在Neo4j中使用的各种唯一性级别是:
NONE
:可以重新访问图表中的任何位置。NODE_GLOBAL
唯一性:整个图中的任何节点都不能被访问多次。这可能会消耗大量内存,因为它需要保持内存中的数据结构记住所有访问过的节点。RELATIONSHIP_GLOBAL
唯一性:整个图表中的任何关系都不会被访问多次。出于与NODE_GLOBAL
唯一性相同的原因,这可能耗费大量内存。但由于图形通常具有比节点更多的关系,因此这种唯一性级别的内存开销可能会更快地增长。NODE_PATH
唯一性:节点可能不会出现在达到它的路径中。RELATIONSHIP_PATH
唯一性:之前的关系可能不会出现在达到它的路径中。NODE_RECENT
唯一性:与NODE_GLOBAL
唯一性类似,有一个全局的访问节点集合,每个位置都会被检查。但是,这种唯一性级别确实可以以仅包含最近访问过的节点的集合形式消耗多少内存。可以通过提供一个数字作为TraversalDescription.uniqueness() - 方法的第二个参数以及唯一性级别来指定此集合的大小。RELATIONSHIP_RECENT
唯一性:与NODE_RECENT
唯一性一样,但与关系而不是节点。
广度遍历
TraversalDescription td = graphDb.traversalDescription()
.breadthFirst()
.relationships(RelTypes.KNOWS, Direction.OUTGOING)
.evaluator(Evaluators.excludeStartPosition());
Traverser friendsTraverser = td.traverse(node);
深度遍历
TraversalDescription td = graphDb.traversalDescription()
.depthFirst()
.relationships(RelTypes.KNOWS, Direction.OUTGOING)
.evaluator(Evaluators.excludeStartPosition());
Traverser friendsTraverser = td.traverse(node);
遍历结果
for(Path friendPath : friendsTraverser){
output.append("At depth " + friendPath.length() + " => " +
friendPath.endNode().getProperties("name") + "\n");
numberOfFriends += 1;
}