zoukankan      html  css  js  c++  java
  • 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官方教程 

  • 相关阅读:
    C++primer plus第六版课后编程题答案7.2
    sprintf函数
    sscanf函数
    [转]Visual Studio 2012 编译错误【error C4996: 'scanf': This function or variable may be unsafe. 】的解决方案
    C/C++动态二维数组的内存分配和释放
    malloc动态分配多维数组
    区间重合判断[poj2808 校门外的树]
    C++中的sort函数
    C语言qsort
    [转]Linux统计代码行数
  • 原文地址:https://www.cnblogs.com/blue163/p/5167294.html
Copyright © 2011-2022 走看看