#json #jq
Вопрос:
Я хотел бы сгладить вложенный объект json, например {"a":{"b":1}}
{"a.b":1}
, чтобы переварить его в solr.
У меня есть 11 ТБ файлов json, которые являются вложенными и содержат точки в именах полей, что означает, что ни elasticsearch (точки), ни solr (вложенные без _childDocument_
обозначения) не могут переварить их как есть.
Другими решениями было бы заменить точки в именах полей подчеркиванием и перенести их в elasticsearch, но у меня гораздо лучший опыт работы с solr, поэтому я предпочитаю сглаживающее решение (если только solr не может переварить эти вложенные json, как есть??).
Я предпочту elasticsearch только в том случае, если процесс переваривания займет гораздо меньше времени, чем solr, потому что мой приоритет-переваривать как можно быстрее (поэтому я выбрал jq вместо написания сценария на python).
Пожалуйста, помогите.
Редактировать:
Я думаю, что пара примеров 3 и 4 решает эту проблему для меня: https://lucidworks.com/blog/2014/08/12/indexing-custom-json-data/
Я скоро попробую.
Ответ №1:
Вы также можете использовать следующую команду jq для выравнивания вложенных объектов JSON таким образом:
[leaf_paths as $path | {"key": $path | join("."), "value": getpath($path)}] | from_entries
Это работает следующим образом: leaf_paths
возвращает поток массивов, представляющих пути в данном документе JSON, в котором отображаются «конечные элементы», то есть элементы, у которых нет дочерних элементов, таких как числа, строки и логические значения. Мы передаем этот поток в объекты со key
value
свойствами и, где key
содержит элементы массива путей в виде строки, соединенной точками, и value
содержит элемент на этом пути. Наконец, мы помещаем все это в массив и запускаем from_entries
на нем, который преобразует массив {key, value}
объектов в объект, содержащий эти пары ключ-значение.
Комментарии:
1. Это решение не работает, если JSON содержит массивы. Например:
{"a":{"b":[1]}}
для которого возникает ошибка: jq: ошибка (при <stdin>:1): строка («.») и число (0) не могут быть добавлены<stdin>2. Отличный ответ, хотя это отфильтровывает любые значения , которые оцениваются
false
,false
т. Е.null
, и т. Д. Это связано с тем , чтоleaf_paths
это короткая рука дляpaths(scalars)
, и хотяscalars
она выбирает их,paths
возвращает только те записи, для которых они не являются ложными. Короче говоря, заменитеleaf_paths
,paths(type != "object" and type != "array")
чтобы включить все.3.Чтобы исправить ошибку jq: ошибка (в <stdin>:1): строка («.») и число (0) не могут быть добавлены<stdin>
[leaf_paths as $path | {"key": [$path[] | tostring] | join("."), "value": getpath($path)}] | from_entries
Ответ №2:
Это всего лишь вариант jq Сантьяго:
. as $in
| reduce leaf_paths as $path ({};
. { ($path | map(tostring) | join(".")): $in | getpath($path) })
Это позволяет избежать накладных расходов на создание и уничтожение ключа/значения.
(Если у вас есть доступ к версии jq более поздней, чем jq 1.5, вы можете опустить «map(tostring)».)
Два важных момента в отношении обоих этих решений jq:
- Массивы также сглажены. Например
{"a": {"b": [0,1,2]}}
, в качестве входных данных вывод будет:{ "a.b.0": 0, "a.b.1": 1, "a.b.2": 2 }
- Если какой-либо из ключей в исходном JSON содержит точки, то возможны коллизии ключей; такие коллизии, как правило, приводят к потере значения. Это могло бы произойти, например, при следующем вводе:
{"a.b":0, "a": {"b": 1}}
Комментарии:
1. @SteveAmerige — Ответ был обновлен, чтобы он работал с jq 1.4 и более поздними версиями.
Ответ №3:
Вот решение, которое использует для поиска, выбора, объединения, уменьшения и установки пути
reduce ( tostream | select(length==2) | .[0] |= [join(".")] ) as [$p,$v] (
{}
; setpath($p; $v)
)
Ответ №4:
Недавно я написал сценарий под названием jqg, который сглаживает произвольно сложный JSON и выполняет поиск результатов с помощью регулярного выражения; чтобы просто сгладить JSON, ваше регулярное выражение будет» .
, что соответствует всему. В отличие от ответов выше, сценарий будет обрабатывать встроенные массивы false
и null
значения и может при необходимости обрабатывать пустые массивы и объекты ( []
amp; {}
) в качестве конечных узлов.
$ jq . test/odd-values.json
{
"one": {
"start-string": "foo",
"null-value": null,
"integer-number": 101
},
"two": [
{
"two-a": {
"non-integer-number": 101.75,
"number-zero": 0
},
"true-boolean": true,
"two-b": {
"false-boolean": false
}
}
],
"three": {
"empty-string": "",
"empty-object": {},
"empty-array": []
},
"end-string": "bar"
}
$ jqg . test/odd-values.json
{
"one.start-string": "foo",
"one.null-value": null,
"one.integer-number": 101,
"two.0.two-a.non-integer-number": 101.75,
"two.0.two-a.number-zero": 0,
"two.0.true-boolean": true,
"two.0.two-b.false-boolean": false,
"three.empty-string": "",
"three.empty-object": {},
"three.empty-array": [],
"end-string": "bar"
}
jqg
был протестирован с использованием jq 1.6
Примечание: Я являюсь автором jqg
сценария.
Ответ №5:
Как выясняется, curl -XPOST 'http://localhost:8983/solr/flat/update/json/docs' -d @json_file
делает именно это:
{
"a.b":[1],
"id":"24e3e780-3a9e-4fa7-9159-fc5294e803cd",
"_version_":1535841499921514496
}
ПРАВКА 1: solr 6.0.1 с. bin/solr -e cloud
имя коллекции есть flat
, все остальные по умолчанию (с data-driven-schema
которым также по умолчанию).
ПРАВКА 2: Окончательный сценарий, который я использовал: find . -name '*.json' -exec curl -XPOST 'http://localhost:8983/solr/collection1/update/json/docs' -d @{} ;
.
ПРАВКА 3: Также возможно параллелизировать с xargs и добавить поле идентификатора с jq: find . -name '*.json' -print0 | xargs -0 -n 1 -P 8 -I {} sh -c "cat {} | jq '. {id: .a.b}' | curl -XPOST 'http://localhost:8983/solr/collection/update/json/docs' -d @-"
где -P
коэффициент параллелизма. Я использовал jq для установки идентификатора, чтобы несколько загрузок одного и того же документа не создавали дубликатов в коллекции (когда я искал оптимальное значение -P
, оно создавало дубликаты в коллекции).