Время от времени я испытываю желание импортировать файл CSV в Neo4j, и я всегда путаюсь с тем, как обрабатывать различные нулевые значения, которые могут скрываться внутри.
Давайте начнем с примера, в котором нет файла CSV. Рассмотрим следующий список и мою попытку вернуть только нулевые значения:
1
2
3
4
5
6
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value = null RETURN value (no changes, no records) |
WITH [null, “null”, “”, “Mark”] AS значения UNWIND значения AS значение WITH значение WHERE value = нулевое значение RETURN (без изменений, без записей)
Хм, это странно. Я бы ожидал, что хотя бы сохраню первое значение в коллекции. А что если мы сделаем обратное?
1
2
3
4
5
6
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value <> null RETURN value (no changes, no records) |
WITH [null, “null”, “”, “Mark”] AS значения UNWIND значения AS значение WITH значение WHERE value <> null RETURN value (без изменений, без записей)
Еще ничего! Давайте попробуем вернуть результат наших сравнений, а не фильтровать строки:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value RETURN value = null AS outcome ╒═══════╤═════════╕ │ "value" │ "outcome" │ ╞═══════╪═════════╡ │ null │ null │ ├───────┼─────────┤ │ "null" │ null │ ├───────┼─────────┤ │ "" │ null │ ├───────┼─────────┤ │ "Mark" │ null │ └───────┴─────────┘ |
Итак, это не то, что мы ожидали. У всего есть «результат» «ноль»! Как насчет того, чтобы проверить, является ли значение строкой «Mark»?
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value RETURN value = "Mark" AS outcome ╒═══════╤═════════╕ │ "value" │ "outcome" │ ╞═══════╪═════════╡ │ null │ null │ ├───────┼─────────┤ │ "null" │ false │ ├───────┼─────────┤ │ "" │ false │ ├───────┼─────────┤ │ "Mark" │ true │ └───────┴─────────┘ |
Из выполнения этого запроса мы узнаем, что если одна сторона сравнения равна нулю, то возвращаемое значение всегда будет нулевым.
Так как же исключить строку, если она нулевая?
Оказывается, мы должны использовать ключевое слово is вместо оператора равенства. Давайте посмотрим, как это выглядит:
01
02
03
04
05
06
07
08
09
10
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value is null RETURN value ╒═══════╕ │ "value" │ ╞═══════╡ │ null │ └───────┘ |
И положительный случай:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value is not null RETURN value ╒═══════╕ │ "value" │ ╞═══════╡ │ "null" │ ├───────┤ │ "" │ ├───────┤ │ "Mark" │ └───────┘ |
Что если мы хотим избавиться от пустых строк?
01
02
03
04
05
06
07
08
09
10
11
12
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value <> "" RETURN value ╒═══════╕ │ "value" │ ╞═══════╡ │ "null" │ ├───────┤ │ "Mark" │ └───────┘ |
Интересно, что это также избавляет от нулевого значения, которое я не ожидал. Но если мы ищем значения, соответствующие пустой строке:
01
02
03
04
05
06
07
08
09
10
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value WITH value WHERE value = "" RETURN value ╒═══════╕ │ "value" │ ╞═══════╡ │ "" │ └───────┘ |
Этого тоже нет! Хм, что здесь происходит:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
WITH [ null , "null" , "" , "Mark" ] AS values UNWIND values AS value RETURN value, value = "" AS isEmpty, value <> "" AS isNotEmpty ╒═══════╤═════════╤════════════╕ │ "value" │ "isEmpty" │ "isNotEmpty" │ ╞═══════╪═════════╪════════════╡ │ null │ null │ null │ ├───────┼─────────┼────────────┤ │ "null" │ false │ true │ ├───────┼─────────┼────────────┤ │ "" │ true │ false │ ├───────┼─────────┼────────────┤ │ "Mark" │ false │ true │ └───────┴─────────┴────────────┘ |
нулевые значения, по-видимому, отфильтровываются для каждого типа совпадения на равенство, если только мы явно не проверим, что значение равно нулю.
Итак, как мы используем эти знания при анализе файлов CSV с помощью инструмента загрузки CSV Neo4j?
Допустим, у нас есть файл CSV, который выглядит следующим образом:
1
2
3
4
5
6
|
$ cat nulls.csv name,company "Mark" , "Michael" , "" "Will" ,null "Ryan" , "Neo4j" |
$ cat nulls.csv name, компания «Марк», «Майкл», »,« Уилл », ноль« Райан »,« Neo4j »
Таким образом, ни одна из первых трех строк не имеет значения для «компании». У меня вообще нет никакого значения, у Майкла пустая строка, а у Уилла нулевое значение. Давайте посмотрим, как LOAD CSV интерпретирует это:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
load csv with headers from "file:///nulls.csv" AS row RETURN row ╒═════════════════════════════════╕ │ "row" │ ╞═════════════════════════════════╡ │{ "name" : "Mark" , "company" : null } │ ├─────────────────────────────────┤ │{ "name" : "Michael" , "company" : "" } │ ├─────────────────────────────────┤ │{ "name" : "Will" , "company" : "null" } │ ├─────────────────────────────────┤ │{ "name" : "Ryan" , "company" : "Neo4j" }│ └─────────────────────────────────┘ |
Мы получили полный охват всех комбинаций сверху. Мы хотели бы создать узел Person для каждой строки, но только создать узел Company и связанный с ним «WORKS_FOR» Relationshp, если определена фактическая компания — мы не хотим создавать пустую компанию.
Поэтому мы хотим создать только узел компании и отношение «WORKS_FOR» для строки Райана.
Следующий запрос делает трюк:
1
2
3
4
5
6
7
8
|
load csv with headers from "file:///nulls.csv" AS row MERGE (p:Person {name: row.name}) WITH p, row WHERE row.company <> "" AND row.company <> "null" MERGE (c:Company {name: row.company}) MERGE (p)-[:WORKS_FOR]->(c) Added 5 labels, created 5 nodes, set 5 properties, created 1 relationship, statement completed in 117 ms. |
загрузить csv с заголовками из «file: ///nulls.csv» как строка MERGE (p: Person {name: row.name}) WITH p, строка WHERE row.company <> «» И row.company <> «null ”MERGE (c: Company {name: row.company}) MERGE (p) — [: WORKS_FOR] -> (c) Добавлено 5 меток, создано 5 узлов, установлено 5 свойств, создано 1 отношение, оператор завершен за 117 мс.
И если мы представим, что было создано:
Отлично. Возможно, это поведение очевидно, но оно всегда сбивает меня с толку, так что, надеюсь, это будет полезно и кому-то еще!
Ссылка: | Neo4j: Как работают нулевые значения? от нашего партнера JCG Марка Нидхэма в блоге Марка Нидхэма . |