Neo4j圖數據庫使用


  最近在處理一些圖的數據,主要是有向圖,如果圖的節點不是特別大可以直接加載到內存里來處理,但是當圖的節點個數特別大時,內存就放不下了;我 們牽涉到的圖的節點數最大可以達到數億個節點,已經超出的機器內存的大小,所以必須把這些圖的數據放到外存上,所以我們就選擇了圖數據庫。

   嘗試了2種圖數據庫,IBM System G 和 neo4j, 這兩個數據庫都可以處理上億個節點的圖,起始使用的是System G,但是存在一些問題,當圖的節點數在300多萬個,邊數為1000多萬個時,在創建圖時就特別麻煩,程序老是創建不成功。后來就選擇了 neo4j,neo4j是一個開源的圖數據庫,使用起來也比較方便,在創建比較大的圖時速度遠遠超過System G;接下來把neo4j入門的知識記錄下來,主要介紹neo4j嵌入在java開發中。

1、創建圖(把圖的數據存入neo4j)

  創建圖由兩種方法,一種是直接通過讀取文件,在程序中顯式的創建節點和邊,另一種是通過加載CSV文件來創建。

1.1 程序中顯示的創建圖

  存放圖的文件的格式如下圖,以'v'開圖的是頂點,后面的數字是它的id,id用從0開始順序存放,在后面是label; 以'e'開頭的行是邊,后面第一個數字是邊的起始點的id,第二個數字是邊的終點的id,后面的字符串是邊的label。

  

  創建圖的方法如下:

  

 1 public static void create_graph(GraphDatabaseService graph, File f) throws FileNotFoundException{
 2         Scanner scanner = new Scanner(f);
 3 
 4         while (scanner.hasNextLine()){
 5             String line = scanner.nextLine().trim();
 6 
 7             if (line.equals("") | line.startsWith("t")){
 8                 continue;
 9             } else if (line.startsWith("v")) {
10                 String nodeLabel = line.split(" ")[2];            //得到頂點的label
11                 Label label = DynamicLabel.label(nodeLabel);      //通過頂點的label,創建一個neo4j的Label類型,作為頂點的label, 這樣就不用把label作為屬性
12                 try (Transaction tx = graph.beginTx()){
13                     graph.createNode(label);                    //創建頂點
14                     tx.success();
15                 }
16             } else if (line.startsWith("e")) {
17                 String[] lineSplit = line.split(" ");
18                 int sourceId = Integer.parseInt(lineSplit[1]);        //得到變得起始頂點id和終止頂點id
19                 int targetId = Integer.parseInt(lineSplit[2]);
20                 String edgeLabel = lineSplit[3];                    //得到邊的label
21                 try (Transaction tx = graph.beginTx()){
22                     Relationship edge = graph.getNodeById(sourceId).createRelationshipTo(graph.getNodeById(targetId), R.DIRECTED);  //創建邊
23                     edge.setProperty("label", edgeLabel);    //給邊設置屬性
24                     tx.success();
25                 }
26             }
27         }
28         
29         scanner.close();
30     }

 

 

1.2 通過加載CSV文件來創建圖

  如果使用CSV文件的話,需要通過URL來訪問文件,我們使用兩個URL,一個是頂點的URL,一個是邊的URL,它們的文件格式要符合csv文件的格式.

  可以創建一個本地的apache服務器來存放這些文件,我們使用的頂點和邊的url分別是:

  頂點url: http://127.0.0.1/nodes

  邊url:  http://127.0.0.1/edges

然后存取頂點的代碼如下:

1 String create_node = "USING PERIODIC COMMIT "
2         + "LOAD CSV WITH HEADERS FROM 'http://127.0.0.1/nodes' AS line "
3       + "CREATE (:node {label: line.label});";      //這樣創建時,不能像上一種方法那樣通過變量來指定label, 所以把label作為了頂點的屬性了,第一個冒號前面可以指定頂點的名字,也可以不指定,冒號后面是該頂點的label.
4 graph.execute(create_node);    //執行cypher語言來創建結點

 

其中"USING PERIODIC COMMIT"的作用是分段式的創建頂點,可以認為指定讀取多少行后就寫入數據庫,默認是讀取1000行后寫入數據庫,例如"USING PERIODIC COMMIT 500",就是讀取500行后就存入數據庫.

存放邊的代碼如下:

1 String create_edge = "USING PERIODIC COMMIT "
2         + "LOAD CSV WITH HEADERS FROM 'http://127.0.0.1/edges' AS line "     
3         + "MATCH (p1), (p2) "    //找到邊的兩個頂點
4         + "WHERE id(p1)=toInt(line.source) and id(p2)=toInt(line.target) "
5         + "CREATE (p1)-[:DIRECTED {label: line.label}]->(p2);";   //創建邊
6 graph.execute(create_edge);

 

其中,需要注意的是帶有"USING PERIODIC COMMIT "的語句不能放在Transaction中執行,否則會出現如下的錯誤

“org.neo4j.cypher.PeriodicCommitInOpenTransactionException: Executing queries that use periodic commit in an open transaction is not possible.”

 

完整的創建頂點和邊的方法如下:

 1 public static void create_nodes(GraphDatabaseService graph, String node_url) {        //創建頂點
 2         String create_node = "USING PERIODIC COMMIT "
 3                 + "LOAD CSV WITH HEADERS FROM " + node_url + "AS line "
 4                 + "CREATE (:node {label: line.label});";
 5         graph.execute(create_node);
 6         System.out.println("nodes create successfully!");
 7     }
 8 //創建邊
 9 public static void create_edges(GraphDatabaseService graph, String edge_url){
10                 
11               String create_edge = "USING PERIODIC COMMIT "
12             + "LOAD CSV WITH HEADERS FROM " + edge_url + " AS line "
13             + "MATCH (p1), (p2) "
14             + "WHERE id(p1)=toInt(line.source) and id(p2)=toInt(line.target) "
15             + "CREATE (p1)-[:DIRECTED {label: line.label}]->(p2);";
16         graph.execute(create_edge);
17 
18         System.out.println("edges create successfully!");
19     } 

 

 

2.得到一個頂點的所有出邊的終點的id

 1 public static ArrayList<Long> get_out_nodes(GraphDatabaseService graph, Node node){
 2         ArrayList<Long> out = new ArrayList<Long>();
 3         try (Transaction tx = graph.beginTx()){
 4             Traverser tr;
 5             TraversalDescription td = graph.traversalDescription()
 6                     .breadthFirst()
 7                     .relationships(R.DIRECTED, Direction.OUTGOING)
 8                     .evaluator(Evaluators.excludeStartPosition());
 9             tr = td.traverse(node);
10             for (Path path : tr){
11                 if (path.length() == 1){
12                     out.add(path.endNode().getId());
13                 }
14             }
15             tx.success();
16         }
17         return out;
18     }

 

 3.得到一個頂點的所有入邊的起始點的id

  

 1  public static ArrayList<Long> get_in_nodes(GraphDatabaseService graph, Node node){
 2         ArrayList<Long> in = new ArrayList<Long>();
 3         try (Transaction tx = graph.beginTx()){
 4             Traverser tr;
 5             TraversalDescription td = graph.traversalDescription()
 6                     .breadthFirst()
 7                     .relationships(R.DIRECTED, Direction.INCOMING)
 8                     .evaluator(Evaluators.excludeStartPosition());
 9             tr = td.traverse(node);
10             for (Path path : tr){
11                 if (path.length() == 1){
12                     in.add(path.endNode().getId());
13                 }
14             }
15             tx.success();
16         }
17         return in;
18     }

 

 

4.得到圖中所有頂點的個數

 1 public static int getSize(GraphDatabaseService graph){
 2         int size = 0;
 3         try (Transaction tx = graph.beginTx()){
 4             Iterator<Node> it = graph.getAllNodes().iterator();
 5             while(it.hasNext()){
 6                 size++;
 7                 it.next();
 8             }
 9             tx.success();
10         }
11         return size;
12     }

 

 

5.根據頂點的屬性label的值,得到具有相同label值的頂點的個數

 1  public static int getSizeByLabel(GraphDatabaseService graph, String label){
 2         try(Transaction tx = graph.beginTx()){
 3             Label node = DynamicLabel.label("node");         //在創建頂點時,指定了頂點的label為"node",注意這個label是Label類型的,與頂點屬性的label不一樣
 4             ResourceIterator<Node> result = graph.findNodes(node, "label", label);
 5             ArrayList<Node> nodes = new ArrayList<>();
 6             while (result.hasNext()){
 7                 nodes.add(result.next());
 8             }
 9             tx.success();
10             return nodes.size();
11         }
12     }

 

 

6. 給出頂點的id,得到該頂點某個屬性的值,如label屬性的值

1 public static String getNodeLabel(GraphDatabaseService graph, int id){
2         try(Transaction tx = graph.beginTx()){
3             String nodeLabel = graph.getNodeById(id).getProperties("label").toString();    //返回的值的樣式如下:{label=AND2X1}
4             String label = nodeLabel.substring(7, nodeLabel.length()-1);      //對上一步的返回值進行取子串
5             tx.success();
6             return label;
7         }
8     }

 

就先介紹這些基本的操作吧,以后用到新的操作了在做補充!

 

參考鏈接入下:

neo4j官方教程 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM