NOSQL数据库之neo4j

本文介绍Neo4j图数据库的REST API及嵌入式操作方式,包括节点和关系的增删改查,并对比了两种方式的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NOSQL的图数据库之Neo4j的学习:
neo4j官方表示性能良好,支持上亿级别数量的节点,但是成熟的资料不多。

1、在安装的过程中对jdk有一定的要求,我所安装的neo4j版本就要求jdk是1.7的。

2、对于neo4j的使用有两种方式:一种是嵌入式方式,另一种是提供了Rest访问的
服务器模式。

一、 Rest方式操作Neo4j:
这里应用了Jersey,通过Jersey客户端API调用rest风格的Web服务,这样就不用
关闭neo4j的服务,进行相关数据的增删改查了。

final String SERVER_ROOT_URI = "http://10.0.11.144:7474/db/data/";

1、查询
String cypherUri = SERVER_ROOT_URI + "cypher"; 

String queryStr = "start n = node(*) return n.source_id"; //查询所有的source_id属性。

JSONObject jsObject = new JSONObject();
jsObject.put("query", queryStr);
WebResource resource = Client.create().resource( cypherUri );
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON_TYPE )
.type( MediaType.APPLICATION_JSON_TYPE ) .entity( jsObject.toString() ).post( ClientResponse.class );
String returnStr=String.format( "POST [%s] to [%s], status code [%d], RETURNED DATA: "+ System.getProperty( "line.separator" ) + "%s", queryStr, cypherUri, response.getStatus(), response.getEntity( String.class ) ) ;
String resultData=returnStr.split("RETURNED DATA:")[1];
JSONObject resultObj = JSONObject.fromObject(resultData);
String resultStr = resultObj.getString("data");
if(null != resultStr && !"".equals(resultStr)){
resultStr = resultStr.replace("[", "").replace("]", "").replace("\"", "");
}else{
resultStr = "";
}
response.close();
return resultStr;


2、删除
neo4j的删除,必须先删除其relationship,再删除其节点,否则删除失败!
这里根据source_id = 123进行删除节点,同时也可以进行批量删除:where n.source_id = 123 OR n.source_id = 124 OR n.source_id = 156。
但是应注意:因为是rest方式进行删除,这些参数不能拼接的太长,否则会报错.一般控制在几十个左右。

 String cypherUri = SERVER_ROOT_URI + "cypher";
String queryStr = "START n=node(*) where n.source_id= 123 match n-[r]-() delete n,r;";
JSONObject jsObject = new JSONObject();
jsObject.put("query", queryStr);
WebResource resource = Client.create().resource( cypherUri );
ClientResponse response = resource.accept( MediaType.APPLICATION_JSON_TYPE )
.type( MediaType.APPLICATION_JSON_TYPE ) .entity( jsObject.toString() ).post( ClientResponse.class );
String returnStr=String.format( "POST [%s] to [%s], status code [%d], RETURNED DATA: "+ System.getProperty( "line.separator" ) + "%s", queryStr, cypherUri, response.getStatus(), response.getEntity( String.class ) ) ;
response.close();


3、添加:
注意:在采用rest这种增量式的添加的时候,由于新增的数据与已经存在的一致,但是再次添加的时候,
neo4j仍然能够正常的添加进去,以为neo4j是根据自己维护节点的id的,所以在增加新的数据之前,很有必要
对数据是否已在neo4j中创建进行判断.

 private URI createNode() {
String nodeEntryPointUri = SERVER_ROOT_URI + "node";

WebResource resource = Client.create().resource(nodeEntryPointUri);
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON).entity("{}").post(ClientResponse.class);

URI location = response.getLocation();
response.close();
return location;
}


//这是创建的新节点,返回的URI是什么呢?我们看一下获取已经存在的节点的URI
 private URI getNodesURI(String exits) {	//exits是neo4j中的节点的id.
String nodeEntryPointUri = SERVER_ROOT_URI + "node/" + exits.replace("\"", "");
URI location = URI.create(nodeEntryPointUri);
return location;
}


1、利用上面创建新节点的方法创建两个节点.
URI nodes = createNode();
URI nodeo = createNode();
2、为这两个节点增加source_id属性.
addProperty(nodes,"source_id",99);
addProperty(nodeo,"source_id",100);
方法addProperty的具体实现如下:
private void addProperty(URI nodeUri, String propertyName, int propertyValue) {
String propertyUri = nodeUri.toString() + "/properties/" + propertyName;
WebResource resource = Client.create().resource(propertyUri);

ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON)
.entity("\"" + propertyValue + "\"").put(ClientResponse.class);

response.close();
}


3、创建好了两个节点并为其增加了属性,那么如何为他们添加关联关系呢.

/**
* 增加neo4j的两个节点之间的关系.
* @param startNode 起始节点的URI
* @param endNode 指向节点的URI
* @param relationshipType 关系类型:一般为来自关系型数据库的数字.
* @param jsonAttributes "{ \"from\" : " + "\"" + sid + "\",\" until\" : " + "\"" + oid + "\"}"
* 其中:sid为起始节点的neo4j的id,oid为指向节点的neo4j的id.
* @return
* @throws URISyntaxException
*/
private URI addRelationship(URI startNode, URI endNode, String relationshipType,
String jsonAttributes) throws URISyntaxException {
URI fromUri = new URI(startNode.toString() + "/relationships");
String relationshipJson = generateJsonRelationship(endNode,relationshipType, jsonAttributes);
WebResource resource = Client.create().resource(fromUri);
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON).entity(relationshipJson)
.post(ClientResponse.class);

final URI location = response.getLocation();

response.close();
return location;
}


4、获取节点nodes和节点nodeo之间的关系
URI relationshipUri = addRelationship(nodes, nodeo, String.valueOf(p), "{ \"from\" : " + "\"" + sid + "\",\" until\" : " + "\"" + oid + "\"}");
5、为关系relationshipUri添加"rel_sounce_id"属性
String relationshipId = "123";
addMetadataToProperty(relationshipUri, "rel_source_id", relationshipId);

具体的实现方法如下:
private void addMetadataToProperty(URI relationshipUri, String name,String value) throws URISyntaxException {
URI propertyUri = new URI(relationshipUri.toString() + "/properties");
String entity = toJsonNameValuePairCollection(name, value);
WebResource resource = Client.create().resource(propertyUri);
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
.type(MediaType.APPLICATION_JSON).entity(entity)
.put(ClientResponse.class);
response.close();
}


4、更新(略)

由此可见,无论是增删还是改查,rest都是通过执行Cypher语言来操作neo4j数据库的,我们来介绍一下什么是Cypher语言:
1、通过Cypher创建节点:注意每个node,系统会自动建立一个唯一的id,不可修改的。
create n={name:'Motion',ID:'M001'} return n; //这里的ID是节点的一个属性.
2、创建关系:
start n=node(14),m=node(20) create m-[r:KNOWS]-n return r;
3、查询:
按系统的id查询: start n = node(20) return n;
查询所有节点的name: start n = node(*) return n.name;
根据属性查找: start n = node(*) where n.name = '王新红' return n;
按照关系查找:
1、查询所有与起始节点关联的信息
START n=node(14) MATCH (n)–[r]-(x) RETURN n,x (没有任何指向)
START n=node(14) MATCH (n)–[r]->(x) RETURN n,x (从n节点指向x节点)
START n=node(14) MATCH (n)<–[r]-(x) RETURN n,x (从x节点指向n节点)

2、查询定向关系
START n=node(14) MATCH (n)<-[r]-() RETURN r (查询所有指向n节点的relationship)
START n=node(14) MATCH (n)-[r]->() RETURN r (查询所有n节点指出的relationship)

3、路径远近查询
START n=node(14) MATCH (n)<–[r]-(x)<-[*1..3]-y RETURN n,x,y (查询所有指向n几点并且路径为2到4的关系)
start a=node(14),b=node(2472) match p=a-[*0..4]-b return p; (查询节点14和节点2472之间路径关系为0到4的所有节点)

分页查询: start n=node(*) return n skip 18 limit 3 //每次skip相当于第几页,limit相当于每页多少条。

4、修改:
start a = node(*) where a.name="a" set a.name="A" return a,a.name ;
start n=node(0) set n.name="Root",n.ID="001" ;
5、删除:
删除所有的节点和关系:start n = node(*) match n-[r]-() delete n,r;
6、同事查询多个节点
start a = node(0),b = node(1) return a,b


二、嵌入式方式操作neo4j(利用一段程序了解其API)
 private void copyInfoFromFactToNeo4j(){

GraphDatabaseService graphDB = new GraphDatabaseFactory().newEmbeddedDatabase(DPATH);
registerShutdownHook(graphDB); //发生意外的时候关闭,释放锁.
try {
Connection con = connectDB();
String sql = "select * from ont_fact o where o.neo4jRelationshipID is null and p in (select distinct id from ont_element where element_type=5) and p not in (46, 32,82,57,54,49,24,100,93,96,106)";

Statement st = con.createStatement();
ResultSet rs = st.executeQuery(sql);
int i = 0;
while (rs.next()) {
//封装jdbc获取的关系型数据库的信息.
Statement pst = con.createStatement();
int id = rs.getInt("id");
int s = rs.getInt("s");
int p = rs.getInt("p");
int o = rs.getInt("o");
int neo4jRelationId = rs.getInt("neo4jRelationshipID");

//neo4j开启事务
Transaction tx = graphDB.beginTx();
try {


Index<Node> nodeIndex = graphDB.index().forNodes("nodes");
Index<Relationship> relIndex = graphDB.index().forRelationships("relationships");

//根据属性获取节点
Node node1 = getNode(graphDB, s);
Node node2 = getNode(graphDB, o);

//创建关系,并设置关系属性.
RelationshipType reltype = DynamicRelationshipType.withName(p + "");

//创建节点之间的relationship关系
Relationship rel = node1.createRelationshipTo(node2,reltype);
rel.setProperty("rel_source_id", id);
relIndex.add(rel, "rel_source_id", id);

//同步关系型数据库数据,使得图数据库与关系型数据库数据一致.
String updatesql = "update [ont_fact] set neo4jRelationshipID='"
+ rel.getId() + "' where id=" + id;
pst.executeUpdate(updatesql);


tx.success();
} finally {
tx.finish();
}
}
rs.close();
st.close();
con.close();
graphDB.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}

private void registerShutdownHook(final GraphDatabaseService graphDB2) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
graphDB2.shutdown();
}
});
}

//获取节点
public Node getNode(GraphDatabaseService graphDB, int obj) {
Node node;
Transaction tx = graphDB.beginTx();
try {
nodeIndex = graphDB.index().forNodes("nodes");
IndexHits<Node> node_results = nodeIndex.query("source_id", obj);
// System.out.println(node_results.size());

if (node_results.size() > 0) {
System.out.println("节点【" + obj + "】确实存在...");
node = node_results.getSingle();
} else {

//如果不存在,则创建并添加节点属性信息.
node = graphDB.createNode();
// long NodeId = node.getId();
node.setProperty("source_id", obj);
nodeIndex.add(node, PRIMARY_KEY, node.getProperty("source_id"));

}

tx.success();
} finally {
tx.finish();
}
return node;
}


总结:Neo4j对外提供两种方式,其中的嵌入式方式处理数据快,但是不能实现在线的备份和更新,必须先停掉neo4j服务。而rest方式则解决这一不足,但是处理数据的效率相对低下.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值