Как сгладить дерево и построить путь из свойств? - PullRequest
1 голос
/ 20 марта 2020

Моя цель состоит в том, чтобы объединить структуру, похожую на файловую систему (вложенные каталоги) с исторической информацией для отдельных файлов, в файл CSV для дальнейшей обработки. Здесь - это то, что я пробовал до сих пор.

Упрощенный ввод выглядит следующим образом:

{ "dirs": [
    { 
        "name": "documents",
        "files": [
            {
                "name": "foo.bar",
                "history": [
                    { "hash": "123", "timestamp": "..."},
                    { "hash": "234", "timestamp": "..."}
                ]
            }
        ],
        "subDirs": [
            { "name": "tmp", "files": [...], "subDirs": [...]
            }
        ]        
    }
]}

Хитрая часть состоит в том, что файл CSV должен содержать полные пути к каталогам, а не только имя каталога. Требуемый результат выглядит следующим образом:

"documents","foo.bar","123","..."
"documents","foo.bar","234","..."
"documents","bar.baz","345","..."
"documents","bar.baz","456","..."
"documents/tmp","deleteme","567","..."
"documents/tmp","deleteme","678","..."

выравнивание большей части данных с помощью recurse работает с использованием этого запроса:

.dirs[] | recurse(.subDirs[]?) | . as $d | $d.files[]? as $f | $f.history[]? as $h | [$d.name, $f.name, $h.hash, $h.timestamp] | @csv

... но я не могу обернуть голову как я могу сохранить построить путь к каталогу. Любые предложения будут высоко оценены.

Ответы [ 2 ]

3 голосов
/ 20 марта 2020

Вот подход, который не использует рекурсию явно (*) и не опирается на рекурсивную структуру:

def names($path):
  reduce getpath($path[0:range(0; $path|length)]) as $v ("";
    if $v | type == "object" and has("name") then . + "/" + $v["name"] else . end) ;

paths as $p
| getpath($p) as $v
| select($v | objects | has("history"))
| [names($p), getpath($p + ["name"])]
  + ($v["history"][] | [.hash, .timestamp] )
| @csv

Это создает «абсолютные» пути (например, «/ documents»); опустить ведущий "/" можно достаточно легко.


(*) paths определяется рекурсивно, но таким образом, чтобы использовать преимущества оптимизации хвостового вызова jq (TCO), которая только применяется к рекурсивным функциям arity-0.

2 голосов
/ 20 марта 2020

Я думаю, что для этого нужно определить пользовательскую рекурсивную функцию, как показано ниже; в котором предполагается, что все файлы имеют непустые значения history.

def f(pfix):
    ( [ pfix, .name ] | join("/") ) as $path |
    ( .files[] | .history[] as $hist | [ $path, .name, $hist[] ] ),
    ( .subDirs[] | f($path) );
.dirs[] | f(empty) | @csv
...