本文使用具体示例介绍导入Neo4j数据到GDB流程。
在进行操作前需要您已经准备好以下资源。
Neo4j Server
,保存有待导出数据,能正常提供读写服务。Cypher Shell
,连接到Neo4j Server
,控制台命令交互,导出数据到文件。- 阿里云
GDB
,数据导入的目的端。 - 阿里云
OSS
,用于临时存放Neo4j
导出的中间文件,提供给GDB
导入使用。 - 中间文件格式转换工具 graphml2csv.py,转换graphML格式文件到
GDB
支持的CSV格式。 - 阿里云
GDB
导入API小工具 GdbLoader.py,也可以直接使用curl
命令交互。
1. 写入测试数据到Neo4j
如果待导出的数据已经保存在Neo4j,可以跳过此步骤,本文演示可操作的完整流程。
在Cypher-Shell
执行以下语句,写入测试数据(TinkerPop modern)。
# 写入点数据
create (:person {id:"1", name:"marko", age:29});
create (:person {id:"2", name:"vadas", age:27});
create (:person {id:"3", name:"lop", lang:"java"});
create (:person {id:"4", name:"josh", age:32});
create (:person {id:"5", name:"ripple", lang:"java"});
create (:person {id:"6", name:"peter", age:35});
# 写入边数据
start n=node(*), m=node(*) where n.id="6" and m.id="3" create (n)-[:created {id:"12", weight:0.2}]->(m);
start n=node(*), m=node(*) where n.id="4" and m.id="3" create (n)-[:created {id:"11", weight:0.4}]->(m);
start n=node(*), m=node(*) where n.id="4" and m.id="5" create (n)-[:created {id:"10", weight:1.0}]->(m);
start n=node(*), m=node(*) where n.id="1" and m.id="3" create (n)-[:created {id:"9", weight:0.4}]->(m);
start n=node(*), m=node(*) where n.id="1" and m.id="4" create (n)-[:knowns {id:"8", weight:1.0}]->(m);
start n=node(*), m=node(*) where n.id="1" and m.id="2" create (n)-[:knowns {id:"7", weight:0.5}]->(m);
2. 导出Neo4j数据到文件
以下使用Neo4j apoc
功能导出数据到文件,文件格式是graphml
。注意导出命令的参数。
neo4j> call apoc.export.graphml.all("result.ml",{
readlabels:true, useTypes:true});
+-----------------------------------------------------------------------------------------------------------------------------------------+
| file | source | format | nodes | relationships | properties | time | rows | batchSize | batches | done |
+-----------------------------------------------------------------------------------------------------------------------------------------+
| "result.ml" | "database: nodes(6), rels(6)" | "graphml" | 6 | 6 | 30 | 54 | 0 | -1 | 0 | TRUE |
+-----------------------------------------------------------------------------------------------------------------------------------------+
1 row available after 261 ms, consumed after another 12 ms
由于我们示例的测试数据比较小,以下给出导出文件的完整内容,方便核对。
<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd">
<key id="name" for="node" attr.name="name" attr.type="string"/>
<key id="id" for="node" attr.name="id" attr.type="string"/>
<key id="lang" for="node" attr.name="lang" attr.type="string"/>
<key id="age" for="node" attr.name="age" attr.type="long"/>
<key id="weight" for="edge" attr.name="weight" attr.type="double"/>
<key id="id" for="edge" attr.name="id" attr.type="string"/>
<graph id="G" edgedefault="directed">
<node id="n0" labels=":person"><data key="labels">:person</data><data key="id">1</data><data key="name">marko</data><data key="age">29</data></node>
<node id="n1" labels=":person"><data key="labels">:person</data><data key="id">4</data><data key="name">josh</data><data key="age">32</data></node>
<node id="n2" labels=":person"><data key="labels">:person</data><data key="id">5</data><data key="name">ripple</data><data key="lang">java</data></node>
<node id="n3" labels=":person"><data key="labels">:person</data><data key="id">6</data><data key="name">peter</data><data key="age">35</data></node>
<node id="n20" labels=":person"><data key="labels">:person</data><data key="id">2</data><data key="name">vadas</data><data key="age">27</data></node>
<node id="n21" labels=":person"><data key="labels">:person</data><data key="id">3</data><data key="name">lop</data><data key="lang">java</data></node>
<edge id="e0" source="n3" target="n21" label="created"><data key="label">created</data><data key="id">12</data><data key="weight">0.2</data></edge>
<edge id="e20" source="n1" target="n21" label="created"><data key="label">created</data><data key="id">11</data><data key="weight">0.4</data></edge>
<edge id="e21" source="n1" target="n2" label="created"><data key="label">created</data><data key="id">10</data><data key="weight">1</data></edge>
<edge id="e22" source="n0" target="n21" label="created"><data key="label">created</data><data key="id">9</data><data key="weight">0.4</data></edge>
<edge id="e23" source="n0" target="n1" label="knowns"><data key="label">knowns</data><data key="id">8</data><data key="weight">1</data></edge>
<edge id="e40" source="n0" target="n20" label="knowns"><data key="label">knowns</data><data key="id">7</data><data key="weight">0.5</data></edge>
</graph>
</graphml>
3. 转换导出文件格式
阿里云GDB
目前不支持导入graphml
格式文件数据,可以使用下面工具转换graphml
格式到GDB
支持的Gremlin(CSV)
格式。
python graphml2csv.py -i result.ml
infile = result.ml
Processing result.ml
Wrote 6 nodes and 24 attributes to result-nodes.csv.
Wrote 6 edges and 18 attributes to result-edges.csv.
转换成功后有以下点和边数据文件,可以看到点边都有一个id
的属性字段。
# CSV点数据文件
$ cat result-nodes.csv
~id,~label,name:string,id:string,lang:string,age:long
0,person,marko,1,,29
1,person,josh,4,,32
2,person,ripple,5,java,
3,person,peter,6,,35
20,person,vadas,2,,27
21,person,lop,3,java,
# CSV边数据文件
$ cat result-edges.csv
~id,~from,~to,~label,weight:double,id:string
0,3,21,created,0.2,12
20,1,21,created,0.4,11
21,1,2,created,1,10
22,0,21,created,0.4,9
23,0,1,knowns,1,8
40,0,20,knowns,0.5,7
转换常见问题
- GDB不支持多label。如果数据中包含多label的点,转换脚本去掉前缀冒号(:),剩下字段当单label值输出。例如两个标签
worker
和doctor
的点,转换后标签是worker:doctor
。 - Neo4j默认隐藏点和边的ID字段,内部使用数值表示。导出
graphml
格式数据中包含有内部ID字段。由于格式规范要求,点ID会添加字符'n',边ID会添加字符'e',导出脚本转换时去掉了前缀。 graphml
格式会在开头定义数据schema,但可能不完整。转换时出现KeyError(xxx)
的错误可能就是数据内容不在schema定义的范围内。可参照在文件开头添加该字段的定义。注意点和边是分开定义的。
4. 导入数据文件到GDB
上述转换后得到点和边两个csv数据文件,我们使用GDB导入工具发送导入命令完成数据导入。当然您也可以参照阿里云GDB导入文档使用CURL
操作导入。
上传转换后的点、边CSV文件到OSS bucket。注意检查bucket的地域和GDB实例是否相同,如果不一致,可以申请新的相同地域bucket。以下使用OSS命令行工具ossutil
上传数据到OSS,请替换您的bucket名称。
#点文件
./ossutil cp result-nodes.csv oss://${mybucket}/neo4j-data/result-nodes.csv
#边文件
./ossutil cp result-edges.csv oss://${mybucket}/neo4j-data/result-edges.csv
使用导入工具依次添加导入点任务和导入边任务,注意等导入点完成后再添加导入边任务,可以使用添加任务时返回的任务ID查询任务详情,检查是否完成。
以下命令需要使用到您的GDB实例参数,可以参照导入文档查找参数值
# 添加导入点任务
python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo add_task --source oss://{
mybucket}/neo4j-data/result-nodes.csv --arn acs:ram::${my-uid}:role/${role-name}
# 添加导入边任务
python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo add_task --source oss://${mybucket}/neo4j-data/result-edges.csv --arn acs:ram::${my-uid}:role/${role-name}
# 查询任务详情
python GdbLoader.py --host ${mygdb-endpoint}:8182 --username ${username} --password ${password} --todo get_task --loaderId 552617AF-4F1E-4CD8-9533-A2EC154688DC
5. 验证导入数据
导入完成后,我们就能使用各种SDK接入GDB访问数据,简单起见,下面使用CURL
读取一个边数据验证。其他语言的接入验证可以参照GDB各环境的接入文档。
curl -u ${username}:${password} -X POST -d '{"gremlin":"g.E(\"21\").valueMap(true)"}' ${mygdb-endpoint}:8182 | python -m json.tool