elasticsearch 使用jq展平嵌套JSON

mqxuamgl  于 2023-02-18  发布在  ElasticSearch
关注(0)|答案(6)|浏览(155)

我想把一个嵌套的json对象扁平化,例如把{"a":{"b":1}}扁平化为{"a.b":1},以便在solr中消化它。
我有11 TB的json文件,这些文件都是嵌套的,并且在字段名中包含点,这意味着elasticsearch(点)和solr(嵌套时没有_childDocument_符号)都不能按原样对其进行摘要。
其他解决方案是将字段名中的点替换为下划线,并将其推送到elasticsearch,但我对solr有更好的经验,因此我更喜欢扁平化解决方案(除非solr可以按原样消化那些嵌套的json??)。
只有当消化过程比solr花费更少的时间时,我才会选择elasticsearch,因为我的首要任务是尽可能快地消化(因此我选择了jq,而不是用python编写脚本)。
好心帮忙。
编辑:
我认为示例3和示例4解决了这个问题:https://lucidworks.com/blog/2014/08/12/indexing-custom-json-data/
我会尽快尝试。

zed5wv10

zed5wv101#

还可以使用以下jq命令以这种方式展平嵌套JSON对象:

[leaf_paths as $path | {"key": $path | join("."), "value": getpath($path)}] | from_entries

它的工作原理是:leaf_paths返回一个数组流,它表示给定JSON文档中出现“leaf元素”(即没有子元素的元素,如数字、字符串和布尔值)的路径。我们将该数组流通过管道传输到具有keyvalue属性的对象中。其中key包含路径数组的元素,作为由点连接的字符串,value包含该路径的元素。最后,我们将整个内容放入一个数组中,并在其上运行from_entries。其将{key, value}对象的数组转换为包含那些键-值对的对象。

hlswsv35

hlswsv352#

这只是圣地亚哥的jq的一个变体:

. as $in 
| reduce leaf_paths as $path ({};
     . + { ($path | map(tostring) | join(".")): $in | getpath($path) })

它避免了键/值构造和销毁的开销。
(If你可以访问jq 1.5以后的jq版本,你可以省略“map(tostring)"。
关于这两种jq解决方案,有两点很重要:
1.数组也是扁平的。例如,给定{"a": {"b": [0,1,2]}}作为输入,输出将是:

{
  "a.b.0": 0,
  "a.b.1": 1,
  "a.b.2": 2
}

1.如果原始JSON中的任何键包含句点,则可能发生键冲突;这样的冲突通常会导致一个值的丢失。2例如,这会在以下输入中发生:

{"a.b":0, "a": {"b": 1}}
oxcyiej7

oxcyiej73#

下面是使用tostreamselectjoinreducesetpath的解决方案

reduce ( tostream | select(length==2) | .[0] |= [join(".")] ) as [$p,$v] (
     {}
     ; setpath($p; $v)
  )
3pvhb19x

3pvhb19x4#

我最近编写了一个名为jqg的脚本,它将任意复杂的JSON扁平化,并使用正则表达式搜索结果;简单地把JSON扁平化,你的正则表达式应该是'.',它匹配所有的东西。2与上面的答案不同,这个脚本将处理嵌入的数组,falsenull值,并且可以选择性地把空数组和对象([]{})作为叶节点。

$ 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是使用jq1.6测试的

**注意:**我是jqg脚本的作者。

tcomlyy6

tcomlyy65#

事实证明,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:Is也可以与xargs并行,并与jq一起添加id字段: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来设置一个id,这样同一个文档的多次上传就不会在集合中创建副本(当我搜索-P的最佳值时,它在集合中创建了副本)

v1uwarro

v1uwarro6#

正如@hraban提到的,leaf_paths没有按预期工作(此外,它已被弃用)。leaf_paths等效于paths(scalars),它返回scalars返回真实值的任何值的路径。scalars返回其输入值(如果它是标量),或者null。问题是nullfalse不是真实值,所以它们将从输出中删除。下面的代码 * 可以 * 工作,直接检查值的类型:

. as $in
     | reduce paths(type != "object" and type != "array") as $path ({};
          . + { ($path | map(tostring) | join(".")): $in | getpath($path) })

相关问题