Недавно меня спросили, как обрабатывать «массив» значений внутри столбца в CSV-файле с помощью инструмента LOAD CSV Neo4j, и хотя я изначально думал, что это невозможно, поскольку каждая ячейка рассматривается как строка, Майкл показал мне способ работать вокруг этого, что я думал, было довольно опрятно.
Допустим, у нас есть файл CSV, представляющий людей и их друзей. Это может выглядеть так:
1
2
3
4
|
name,friends "Mark" , "Michael,Peter" "Michael" , "Peter,Kenny" "Kenny" , "Anders,Michael" |
И мы хотим иметь следующие узлы:
- отметка
- Майкл
- Питер
- Kenny
- Андерс
И следующие отношения друзей:
- Марк -> Майкл
- Марк -> Питер
- Михаил -> Питер
- Майкл -> Кенни
- Кенни -> Андерс
- Кенни -> Майкл
Мы начнем с загрузки CSV-файла и возврата каждой строки:
1
2
3
4
5
6
7
8
9
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friends.csv" AS row RETURN row; +------------------------------------------------+ | row | +------------------------------------------------+ | {name -> "Mark" , friends -> "Michael,Peter" } | | {name -> "Michael" , friends -> "Peter,Kenny" } | | {name -> "Kenny" , friends -> "Anders,Michael" } | +------------------------------------------------+ 3 rows |
Как и ожидалось, столбец ‘friends’ рассматривается как String, что означает, что мы можем использовать функцию split, чтобы получить массив людей, с которыми мы хотим дружить:
1
2
3
4
5
6
7
8
9
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friends.csv" AS row RETURN row, split(row.friends, "," ) AS friends; +-----------------------------------------------------------------------+ | row | friends | +-----------------------------------------------------------------------+ | {name -> "Mark" , friends -> "Michael,Peter" } | [ "Michael" , "Peter" ] | | {name -> "Michael" , friends -> "Peter,Kenny" } | [ "Peter" , "Kenny" ] | | {name -> "Kenny" , friends -> "Anders,Michael" } | [ "Anders" , "Michael" ] | +-----------------------------------------------------------------------+ 3 rows |
Теперь, когда мы получили их как массив, мы можем использовать UNWIND, чтобы получить пары друзей, которых мы хотим создать:
01
02
03
04
05
06
07
08
09
10
11
12
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friends.csv" AS row WITH row, split(row.friends, "," ) AS friends UNWIND friends AS friend RETURN row.name, friend; +-----------------------+ | row.name | friend | +-----------------------+ | "Mark" | "Michael" | | "Mark" | "Peter" | | "Michael" | "Peter" | | "Michael" | "Kenny" | | "Kenny" | "Anders" | | "Kenny" | "Michael" | +-----------------------+ 6 rows |
А теперь мы представим несколько операторов MERGE для создания соответствующих узлов и отношений:
1
2
3
4
5
6
7
8
9
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friends.csv" AS row WITH row, split(row.friends, "," ) AS friends UNWIND friends AS friend MERGE (p1:Person {name: row.name}) MERGE (p2:Person {name: friend}) MERGE (p1)-[:FRIENDS_WITH]->(p2); +-------------------+ | No data returned. | +-------------------+ Nodes created: 5 Relationships created: 6 Properties set: 5 Labels added: 5 373 ms |
А теперь, если мы запросим базу данных, чтобы вернуть все узлы + отношения …
01
02
03
04
05
06
07
08
09
10
11
12
|
$ match (p1:Person)-[r]->(p2) RETURN p1,r, p2; +------------------------------------------------------------------------+ | p1 | r | p2 | +------------------------------------------------------------------------+ | Node[ 0 ]{name: "Mark" } | :FRIENDS_WITH[ 0 ]{} | Node[ 1 ]{name: "Michael" } | | Node[ 0 ]{name: "Mark" } | :FRIENDS_WITH[ 1 ]{} | Node[ 2 ]{name: "Peter" } | | Node[ 1 ]{name: "Michael" } | :FRIENDS_WITH[ 2 ]{} | Node[ 2 ]{name: "Peter" } | | Node[ 1 ]{name: "Michael" } | :FRIENDS_WITH[ 3 ]{} | Node[ 3 ]{name: "Kenny" } | | Node[ 3 ]{name: "Kenny" } | :FRIENDS_WITH[ 4 ]{} | Node[ 4 ]{name: "Anders" } | | Node[ 3 ]{name: "Kenny" } | :FRIENDS_WITH[ 5 ]{} | Node[ 1 ]{name: "Michael" } | +------------------------------------------------------------------------+ 6 rows |
… вы увидите, что у нас есть все. Если вместо списка людей через запятую у нас есть буквенный массив в ячейке …
1
2
3
4
|
name,friends "Mark" , "[Michael,Peter]" "Michael" , "[Peter,Kenny]" "Kenny" , "[Anders,Michael]" |
… Нам нужно настроить часть запроса, которая извлекает наших друзей, чтобы убрать первый и последний символы:
1
2
3
4
5
6
7
8
9
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friendsa.csv" AS row RETURN row, split(substring(row.friends, 1 , length(row.friends) - 2 ), "," ) AS friends; +-------------------------------------------------------------------------+ | row | friends | +-------------------------------------------------------------------------+ | {name -> "Mark" , friends -> "[Michael,Peter]" } | [ "Michael" , "Peter" ] | | {name -> "Michael" , friends -> "[Peter,Kenny]" } | [ "Peter" , "Kenny" ] | | {name -> "Kenny" , friends -> "[Anders,Michael]" } | [ "Anders" , "Michael" ] | +-------------------------------------------------------------------------+ 3 rows |
И затем, если мы объединим весь запрос, мы получим следующее:
1
2
3
4
5
6
7
8
|
$ load csv with headers from "file:/Users/markneedham/Desktop/friendsa.csv" AS row WITH row, split(substring(row.friends, 1 , length(row.friends) - 2 ), "," ) AS friends UNWIND friends AS friend MERGE (p1:Person {name: row.name}) MERGE (p2:Person {name: friend}) MERGE (p1)-[:FRIENDS_WITH]->(p2);; +-------------------+ | No data returned. | +-------------------+ Nodes created: 5 Relationships created: 6 Properties set: 5 Labels added: 5 |
Ссылка: | Neo4j: LOAD CSV — Обработка скрытых массивов в ваших CSV-документах от нашего партнера JCG Марка Нидхэма в блоге Марка Нидхэма . |