前言
突然有需求需要用apoc 導入 低版本的圖譜數據,網上資料又比較少,所以就看官網資料并處理了apoc 導入的一些問題。
相關地址
apoc 官方安裝網址??
apoc 官方導出csv 教程地址
apoc 官方 導入 csv 地址
docker 安裝
執行如下命令啟動鏡像
docker run -d --name neo4j \-p 7474:7474 -p 7687:7687 \-v /home/neo4j/data:/var/lib/neo4j/data \-v /home/neo4j/plugins:/var/lib/neo4j/plugins \-v /home/neo4j/logs:/var/lib/neo4j/logs \-v /home/neo4j/conf:/var/lib/neo4j/conf \-v /home/neo4j/import:/var/lib/neo4j/import \-e NEO4J_apoc_export_file_enabled=true \-e NEO4J_apoc_import_file_enabled=true \-e NEO4J_apoc_import_file_use__neo4j__config=true \-e NEO4J_AUTH=neo4j/12345678 \neo4j:5.19.0
apoc安裝
如果沒有數據,可以執行下面的語句插入數據,后面導入導出用
CREATE (TheMatrix:Movie {title:'The Matrix', released:1999, tagline:'Welcome to the Real World'})
CREATE (Keanu:Person {name:'Keanu Reeves', born:1964})
CREATE (Carrie:Person {name:'Carrie-Anne Moss', born:1967})
CREATE (Laurence:Person {name:'Laurence Fishburne', born:1961})
CREATE (Hugo:Person {name:'Hugo Weaving', born:1960})
CREATE (LillyW:Person {name:'Lilly Wachowski', born:1967})
CREATE (LanaW:Person {name:'Lana Wachowski', born:1965})
CREATE (JoelS:Person {name:'Joel Silver', born:1952})
CREATE
(Keanu)-[:ACTED_IN {roles:['Neo']}]->(TheMatrix),
(Carrie)-[:ACTED_IN {roles:['Trinity']}]->(TheMatrix),
(Laurence)-[:ACTED_IN {roles:['Morpheus']}]->(TheMatrix),
(Hugo)-[:ACTED_IN {roles:['Agent Smith']}]->(TheMatrix),
(LillyW)-[:DIRECTED]->(TheMatrix),
(LanaW)-[:DIRECTED]->(TheMatrix),
(JoelS)-[:PRODUCED]->(TheMatrix);
從apoc 插件地址?下載 apoc-core-5.19.0 的jar,放到 /home/neo4j/plugins 下
然后編輯? /home/neo4j/conf/neo4j.conf ,添加如下配置
#沒裝成功的時候需要自己配置插件目錄,成功了就不需要
#server.directories.plugins=/var/lib/neo4j/plugins
#下面兩個官方是建議需要什么開放什么,我是直接全開放了
dbms.security.procedures.allowlist=apoc.*
dbms.security.procedures.unrestricted=apoc.*
#網上有出現上下面的配置,但是我不配置也沒影響
#server.jvm.additional=Dapoc.export.file.enabled=true
#server.jvm.additional=Dapoc.import.file.enabled=true
server.directories.import=/var/lib/neo4j/import
此處有個問題:由于我一開始配置的 neo4j 是別人裝的,我將apoc插件放到掛載的 plugins 下,apoc沒裝成功,也沒有錯誤,把我整蒙了,后面是在 stackoverflow 看到有人給了 server.directories.plugins 配置,試著配一下才成功的,主要是沒裝成功也看不到錯誤,排查起來比較費勁。,需要注意下。自己按上面的步驟應該安裝是不需要配置的。
apoc導出
然后執行如下指令導出數據
?CALL?apoc.export.csv.all("movies.csv",?{})
如果出現如下界面,表示插件安裝成功,數據導出成功,文件會導出到 /home/neo4j/import 下
如果出現下面的錯誤,就是要考慮配置 server.directories.plugins?了
There is no procedure with the name `apoc.export.csv.all` registered for this database instance. Please ensure you've spelled the procedure name correctly and that the procedure is properly deployed.
apoc導入
打開導出的csv 文件,數據格式如下圖所示
但是我當時用別人配置服務的數據導出的時候,csv內容如下所示,包括別人從3.x的版本導出的數據也是這樣的數據,多出了重復的列名_type,_start_,_end
如果像上面一樣有重復的標簽,就需要自己刪除重復的_type,_start_,_end標簽,否則就會出現如下錯誤
Failed to invoke procedure `apoc.import.csv`: Caused by: java.lang.IllegalStateException: Duplicate key _type (attempted merging values apoc.load.Mapping@45da0937 and apoc.load.Mapping@d89fe8
?然后按如下要求修改movies.csv的內容:
1.將 _id 改成 oldId:ID
2.將?_labels 改為 :LABEL
3.將?_start 改為?oldId:START_ID
4.將?_end 改為 oldId:END_ID
5.將?_type 改為?:TYPE
6.將 _labels 那一列下面的值改成 Movie,Person 用逗號分隔多個標簽,去掉所有冒號
?我在基于上述需求改的時候,不小心把?:LABEL??敲成 :LABELS ,于是出現了如下的錯誤,看的我一臉懵逼
Failed to invoke procedure `apoc.import.csv`: Caused by: org.neo4j.internal.kernel.api.exceptions.schema.IllegalTokenNameException: '' is not a valid token name. Token names cannot be empty or contain any null-bytes.
之后如果基于上面的文件進行直接導入,會出現如下的錯誤,也很莫名其妙
Failed to invoke procedure `apoc.import.csv`: Caused by: java.lang.ClassCastException: class java.lang.String cannot be cast to class java.util.List (java.lang.String and java.util.List are in module java.base of loader 'bootstrap'
這是因為關系和節點都在同一個 csv 里面,需要拆成兩個 csv,一個只有關系,一個只有節點,如下面的圖片所示。
然后這兩個文件都需要放到 /home/neo4j/import 里面
之后執行命令下面的指令(此處 labels 和 type 都不要傳值,這兩個配置是指定本次導入的節點配置的 label 和關系配置的 type,我們需要用的label 和 type 都在 csv 里,不需要自己指定)
CALL apoc.import.csv([{fileName: 'file:/node.csv', labels: []}],[{fileName: 'file:/relationship.csv', type: ''}],{ arrayDelimiter: ','}
)
如下結果表示成功
數據正常導入,原數據的?id?被作為?oldId?被插入到?neo4j?中
csv數據修改問題
1.我這個 demo 是因為數據量少,所以直接人工修改,不是很費勁,但是人工修改的時候需要注意編碼問題,有些編輯工具如 excel 修改后再保存,會修改 csv 的編碼,此時導入就會出現亂碼,需要自己通過如 notepad 等其他工作,把編碼改為UTF-8
2.當數據量特別多的時候,手動改就顯得又卡,又難操作了,此時可以考慮參考下面的代碼,然后基于自己的要求調整。(apoc 好像是使用opencsv 解析的,也可以考慮用opencsv)
<dependency><groupId>org.apache.commons</groupId><artifactId>commons-csv</artifactId><version>1.7</version>
</dependency>
try (Reader reader = Files.newBufferedReader(Paths.get("movies.csv"))) {CSVPrinter csvPrinter = null;boolean isFirst = true;Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(reader);int recordIndex = 0;for (CSVRecord record : records) {recordIndex++;if (isFirst) {isFirst = false;List<String> heads = Lists.newArrayList();int index = 0;for (String s : record) {index++;heads.add(s);}csvPrinter = new CSVPrinter(Files.newBufferedWriter(new File("gen.csv").toPath(),StandardOpenOption.CREATE, StandardOpenOption.WRITE), CSVFormat.DEFAULT.withHeader(heads.toArray(new String[heads.size()])));continue;}List<String> bodys = Lists.newArrayList();int index = 0;for (String s : record) {if (index == 1) {s = s.replaceAll(":", ";").replaceFirst(":", "");}index++;bodys.add(s);}csvPrinter.printRecord(bodys.toArray(new String[bodys.size()]));csvPrinter.flush();}
}