Статьи

Neo4j: обнаружение мошеннических пространств в заголовках CSV с помощью LOAD CSV

На прошлой неделе я помогал кому-то загружать данные из CSV-файла в Neo4j, и у нас были проблемы с фильтрацией строк, содержащих нулевое значение в одном из столбцов.

Вот как выглядели данные:

1
2
load csv with headers from "file:///foo.csv" as row
RETURN row
1
2
3
4
5
6
7
╒══════════════════════════════════╕
│row                               │
╞══════════════════════════════════╡
│{key1: a,  key2: (null),  key3: c}│
├──────────────────────────────────┤
│{key1: d,  key2: e,  key3: f}     │
└──────────────────────────────────┘

Мы бы хотели отфильтровать все строки, у которых ‘key2’ равно нулю, поэтому давайте настроим наш запрос, чтобы сделать это:

1
2
3
load csv with headers from "file:///foo.csv" as row
WITH row WHERE NOT row.key2 is null
RETURN row
1
(no rows)

Хм, это странно, он избавился от обоих рядов. Мы ожидаем увидеть вторую строку, так как она не имеет нулевого значения.

В этот момент мы можем заподозрить, что то, что мы видим на экране, на самом деле не выглядит как данные. Давайте напишем следующий запрос, чтобы проверить наши значения заголовка:

1
2
3
4
load csv with headers from "file:///foo.csv" as row
WITH row LIMIT 1
UNWIND keys(row) AS key
RETURN key, SIZE(key)
1
2
3
4
5
6
7
8
9
╒═════╤═════════╕
│key  │SIZE(key)│
╞═════╪═════════╡
│key1 │4       
├─────┼─────────┤
│ key2│5       
├─────┼─────────┤
│ key3│5       
└─────┴─────────┘

Второй столбец говорит нам, что в столбцах есть несколько дополнительных символов для «key2» и «key3» или, скорее, «key2» и «key3». В данном случае это пробелы, но это может быть другой символ:

1
2
3
4
load csv with headers from "file:///foo.csv" as row
WITH row LIMIT 1
UNWIND keys(row) AS key
RETURN key, replace(key, " ", "_SPACE_") AS spaces
1
2
3
4
5
6
7
8
9
╒═════╤═══════════╕
│key  │spaces     │
╞═════╪═══════════╡
│key1 │key1       │
├─────┼───────────┤
│ key2│_SPACE_key2│
├─────┼───────────┤
│ key3│_SPACE_key3│
└─────┴───────────┘

Если мы очистим наш CSV-файл и попробуем еще раз, все будет работать как положено:

1
2
3
4
load csv with headers from "file:///foo.csv" as row
WITH row LIMIT 1
UNWIND keys(row) AS key
RETURN key, SIZE(key)
1
2
3
4
5
6
7
8
9
╒════╤═════════╕
│key │SIZE(key)│
╞════╪═════════╡
│key1│4       
├────┼─────────┤
│key2│4       
├────┼─────────┤
│key3│4       
└────┴─────────┘
1
2
3
load csv with headers from "file:///foo.csv" as row
WITH row WHERE NOT row.key2 is null
RETURN row
1
2
3
4
5
╒═══════════════════════════╕
│row                        │
╞═══════════════════════════╡
│{key1: d, key2: e, key3: f}│
└───────────────────────────┘