NOSQL数据库之neo4j

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方式则解决这一不足,但是处理数据的效率相对低下.

猜你喜欢

转载自wangxinhong4468.iteye.com/blog/2162055