Я запрашиваю ElasticSearch и сортирую документы локально в Bash с помощью jq
, так как сортировка в ES для меня слишком медленная.
Исходная цель — создать файл CSV.
Но я обнаружил, что сортировка не работает должным образом, кажется, шаг sort
ничего не делает.
Когда я запускаю запросы cURL
, я подумал, что неправильный порядок из-за того, что контент разбит на части, поэтому я сохранил некоторые результаты в локальный файл test.json
и попробовал еще раз, но это все равно не работает.
test.json
:
{
"took": 680,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"max_score": 1.0,
"hits": [
{
"_index": "my-index",
"_type": "_doc",
"_id": "111111113584925",
"_score": 1.0,
"fields": {
"field2": [
"FOO"
],
"field1": [
"111111113584925"
]
}
},
{
"_index": "my-index",
"_type": "_doc",
"_id": "111111121254059",
"_score": 1.0,
"fields": {
"field2": [
"FOO"
],
"field1": [
"111111121254059"
]
}
}
]
}
}
(Есть еще много записей - отредактировано для краткости.)
Команда, которую я использую:
jq '.hits.hits[].fields | [.field1[0] + "," + .field2[0]] | sort | .[0]' -r test.json
Результат:
111111113584925,FOO
111111121254059,FOO
111111116879444,FOO
и т. д.
Почему?
Стоит ли мне полагаться на сортировку jq
? Правильно ли я его использую? Я имею в виду, что я хочу выполнить сравнение строк в алфавитном порядке, и все field1
имеют уникальные значения, поэтому никогда не будет ничьей, и я начну сравнивать значения field2
(оно также может иметь различные значения, но я хочу сортировать только по field1
)
Должен ли я вместо этого использовать Bash sort -k 1
? Что быстрее, когда речь идет о 100 тыс. строк?
См. unix.stackexchange.com/questions/613779/…
Зачем вы оборачиваете один элемент массива, сортируете, а затем снова разворачиваете элемент массива?
sort
работает с массивом в качестве входных данных. Ваши массивы содержат только один элемент, поэтому сортировать нечего. Для сортировки вы передаете не один большой массив массивов, а множество одноэлементных массивов.
Я не очень хорошо знаком с сортировкой в jq, но мне кажется, что было бы так же просто использовать jq '.hits.hits[].fields | .field1[0] + "," + .field2[0]' -r test.json | сортировать -n
@knittl Я хотел построить из этого массив, но, думаю, это, видимо, не удалось.
@WesternGun .hits.hits[].fields | [.field1[0] + "," + .field2[0]]
создает поток массивов, например. ["1,FOO"]["2,FOO"]["3,FOO"]
. Если вы хотите ["1,FOO","2,FOO","3,FOO"]
, вам нужно [.hits.hits[].fields | .field1[0] + "," + .field2[0]]
или map(…)
Для сортировки по .fields.field1[0]
используйте sort_by(.fields.field1[0])
. Но поскольку вы хотите вывести те же значения, которые используются в качестве критериев, уменьшите заранее, используя map
, и просто используйте sort
в массиве результатов:
jq -r '.hits.hits | map(.fields.field1[0]) | sort[]' file.json
Обратите внимание, что при этом выполняется сортировка строк, поскольку все первые элементы массива .field1
на самом деле являются строками (несмотря на то, что 11
содержит только цифры, 2
будет отсортирован раньше tonumber
). Если вам нужна числовая сортировка (в том смысле, что два меньше одиннадцати), сначала преобразуйте элементы в числа, используя -r
. В этом случае вы также можете удалить флаг field1
, поскольку числа JSON не имеют специальной кодировки.
jq -r '.hits.hits | map(.fields.field1[0] | tonumber) | sort[]' file.json
Поскольку демонстрационные данные содержат только строковые элементы одинаковой длины, состоящие только из цифр, оба подхода дают одинаковый результат:
111111113034864
111111113034877
111111113034894
111111113046053
111111113046124
:
Мне нужен CSV-файл, в котором
field2
и,
разделеныsort_by(.fields.field1[0])
.
Поскольку элементы вывода отличаются от критериев сортировки, я бы вернулся к использованию @csv
, поскольку это дает вам больше свободы для смешивания и сопоставления, и заставил бы .fields.field1[0]
сконструировать вывод, который также позаботится об экранировании специальных символов:
.hits.hits | sort_by(.fields.field1[0])[].fields
| [.field1[0], .field2[0]] | @csv
"111111113034864","FOO"
"111111113034877","FOO"
"111111113034894","FOO"
"111111113046053","FOO"
"111111113046124","FOO"
Спасибо, я думаю, что карта - это путь. Извините, мне придется принять другой вариант, так как мне нужен CSV-файл, в котором field1
и field2
разделены ,
; но все равно спасибо
В последнем редактировании добавлено еще несколько уровней труб, и это кажется длинным и запутанным; и, наконец, в нем все еще есть цитаты.
Вы ищете что-то вроде этого:
.hits.hits | map(.fields | .field1[0] + "," + .field2[0]) | sort[]
О, спасибо, это быстро и именно то, что я хочу. Я не знал, что смогу разделить большой массив с помощью sort[]
; только с sort
у меня получился один большой массив. И кажется, мне нужно остановиться на уровень выше, чтобы начать картографирование. Что за язык. Я не знаю, какая операция выполняется над целым, а какая над каждым элементом.
Кстати, у тебя есть представление о перформансе? jq sort
против Баша sort
Вы всегда можете это сделать … | sort | .[]
, если так будет легче понять :)
@WesternGun Автономная утилита сортировки будет быстрее для больших входных данных.
@oguzismail да, я только что проверил материалы сортировки, кажется, это сортировка слиянием и так далее.
Если вы посмотрите на промежуточные результаты, вы увидите, что после первого шага у вас есть отдельные объекты, а не массив в качестве элемента верхнего уровня. Затем сортировка применяется к каждому элементу отдельно. Полученные вами ответы позволяют убедиться, что у вас действительно есть массив перед сортировкой.