将类似于JSON的数据结构扁平化的意义

今天,只是一篇毫无意义的文章。

数据平坦化

最近一直看到有关”将数据结构扁平化后效果很好”的信息。

首先,数据的扁平化指的是将其转化为下方所示的格式:

{
  "created_at": "Thu Apr 06 15:24:15 +0000 2017",
  "id_str": "850006245121695744",
  "text": "1\/ Today we\u2019re sharing our vision for the future of the Twitter API platform!\nhttps:\/\/t.co\/XweGngmxlP",
  "user": {
    "id": 2244994945,
    "name": "Twitter Dev",
    "screen_name": "TwitterDev",
    "location": "Internet",
    "url": "https:\/\/dev.twitter.com\/",
    "description": "Your official source for Twitter Platform news, updates & events. Need technical help? Visit https:\/\/twittercommunity.com\/ \u2328\ufe0f #TapIntoTwitter"
  },
  "place": {   
  },
  "entities": {
    "hashtags": [      
    ],
    "urls": [
      {
        "url": "https:\/\/t.co\/XweGngmxlP",
        "unwound": {
          "url": "https:\/\/cards.twitter.com\/cards\/18ce53wgo4h\/3xo1c",
          "title": "Building the Future of the Twitter API Platform"
        }
      }
    ],
    "user_mentions": [     
    ]
  }
}

以下是一种表达方式: ↓ 如下所述

created_at="Thu Apr 06 15:24:15 +0000 2017"
id_str="850006245121695744"
text=1\/ Today we\u2019re sharing our vision for the future of the Twitter API platform!\nhttps:\/\/t.co\/XweGngmxlP"
user.id=2244994945
user.name="Twitter Dev"
user.screen_name="TwitterDev"
user.location="Internet"
user.url="https:\/\/dev.twitter.com\/"
user.description="Your official source for Twitter Platform news, updates & events. Need technical help? Visit https:\/\/twittercommunity.com\/ \u2328\ufe0f #TapIntoTwitter"
entities.urls.[0].url="https:\/\/t.co\/XweGngmxlP"
entities.urls.[0].unwound.url="https:\/\/cards.twitter.com\/cards\/18ce53wgo4h\/3xo1c",
entities.urls.[0].unwound.title="Building the Future of the Twitter API Platform"

通过扁平化处理的方式可能略有不同,但在能够用一行表示嵌套数据结构方面是相同的。

例如

非常感谢你的理解。

这是一个 CLI 工具,旨在将 JSON 扁平化,使得可以更容易地进行 grep 操作。
https://github.com/tomnomnom/gron

# Flatten 
$ gron "https://api.github.com/repos/tomnomnom/gron/commits?per_page=1" | fgrep "commit.author"
# json[0].commit.author = {};
# json[0].commit.author.date = "2016-07-02T10:51:21Z";
# json[0].commit.author.email = "mail@tomnomnom.com";
# json[0].commit.author.name = "Tom Hudson";

# Undo
gron "https://api.github.com/repos/tomnomnom/gron/commits?per_page=1" | fgrep "commit.author" | gron --ungron
# [
#   {
#     "commit": {
#       "author": {
#         "date": "2016-07-02T10:51:21Z",
#         "email": "mail@tomnomnom.com",
#         "name": "Tom Hudson"
#       }
#     }
#   }
# ]

grep しやすい
構造をひと目で把握できる
JavaScript のコードで出力される

gron testdata/two.json > tmp.js && nodejs tmp.js で Node は正しく Object として復元できる

普罗米修斯

动态架构度量收集管理工具。
https://prometheus.io/

Prometheus巡回公开指标数据终点,并收集数据,但其格式为平坦数据。

例如,postgres_exporter会公开以下的度量指标。

pg_locks_count{datname="postgres",mode="accessexclusivelock"} 0   // `{}` 内は、Label という集計の為の属性値
pg_settings_autovacuum_analyze_scale_factor 0.1
pg_settings_enable_seqscan 1
pg_settings_log_rotation_age_seconds 86400
...

如果是JSON格式的话,会不会是这样呢?

{
  "pg" : {
    "locks" : {
      "count" : 0
    },
    "settings" : {
      "autovacuum" : {
        "analyze" : {
          "scale_factor" : 0.1
        }
      },
      "enable_seqscan" : 1,
      "log_rotation" : {
        "age_seconds" : 86400
      }
    }
  }
}
    • 人間が読みやすい

 

    • パースコストが低い

シンプルな構造
Validation が最小

蝰蛇

一个能够方便管理Go的配置的库。

为了统一处理通过设置文件、键/值存储、命令行标志和环境变量等提供的配置信息,Viper 使用扁平化的键来标识配置值,以便进行嵌套数据结构的处理。

{
    "host": {
        "address": "localhost",
        "port": 5799
    },
    "datastore": {
        "metric": {
            "host": "127.0.0.1",
            "port": 3099
        },
        "warehouse": {
            "host": "198.0.0.1",
            "port": 2112
        }
    }
}
export DATASTORE_METRIC_HOST="192.168.0.1"
# ただし、コード側に `viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))` が必要
./main --datastore.metric.host="192.168.100.100"
GetString("datastore.metric.host")
// 192.168.100.100
    • フォーマットが違うデータ構造同士の中間表現として利用

階層的なデータ構造なら全て変換可能
グラフデータは表現不可

人们在什么时候将数据扁平化?

易于人类阅读

有时候当查看Docker inspect的结果时,你可能会迷失自己究竟在看什么。
要在jq中进行筛选,首先必须记得原始结构。
因此,如果用gron对Docker inspect结果进行解析,将会得到以下结果。

...
json[0].NetworkSettings.Networks.bridge.Gateway = "192.168.0.1";
json[0].NetworkSettings.Networks.bridge.GlobalIPv6Address = "";
json[0].NetworkSettings.Networks.bridge.GlobalIPv6PrefixLen = 0;
json[0].NetworkSettings.Networks.bridge.IPAMConfig = null;
json[0].NetworkSettings.Networks.bridge.IPAddress = "192.168.0.2";
...

在浏览大而复杂的数据时,将其扁平化可以至少让数据的结构一目了然,并且可以在进行grep搜索的同时,探索性地找到所需的数据。

我认为,就易于编写而言,像Yaml和Toml这样的结构更好。

适合于日志记录

日志中的搜索目标经常被用来进行grep操作。
如果将JSON等格式化为多行输入,将会变得很难查找,而且可能会插入其他日志之间。
一行可以表达意义的扁平数据更加牢固。

如果有智能的记录器,并且可以处理多行日志,那么最好使用它,如果可以的话。

机器上也相对容易阅读

机械的易读性指的是即使没有解析器,也可以通过直观的数据操作来处理最基本的需求。

例如,对于Shell来说,可以使用grep -v “#” | grep user.name | cut -d= -f 2命令来获取目标数据。

在程序端,似乎也可以轻松地进行读取。
例如,对于 JavaScript,可以通过 new Map(text.split(‘\n’).filter(a => !a.startsWith(“#”)).map(a => a.split(‘=’)) 进行映射化,
对于 Python,可以通过 { b[0]:b[1] for b in [a.split(‘=’) for a in text.split(‘\n’) if not t.startswith(‘#’)]} 进行字典化。
当然,考虑到转义等问题会更加困难,但在少于100行的代码中能够实现解析的复制粘贴的高度可用性在某些情况下也是有用的。

在易处理性方面,拥有稳定的解析器和有效的验证机制,并能将数据解析到结构体等数据结构中的情况更为便利。

适合大数据处理

特别是在收集时间序列数据时,通常不需要维护同步的结构,而是保存为时间戳+键值对的形式。

序列化

我发现阅读方很轻松,但对于输出方来说却有很多麻烦事。我找到了一些库和实现示例。

JavaScript- 只需要一个选项,用中文本地语言重新表达以下内容:

用中文,只需要选一个选项来换句:Python

Java:只需要一个选项,请用中文来释义

Scala 可以用中文原生表达为短语 “斯卡拉”.

所有的人/整个团队

由于在每种语言中都进行了不同的实施,所以我认为有必要,但并没有统一的格式。自己和他人都可能有需求,但不知道该怎么样。

总结

就描述而言,结构化的方式更易理解和处理。我会选择使用 Toml 而不是 Java 的 Properties。
然而,就查看和公开数据而言,只要正确选择适用的情况,应用的领域相对较多。我目前正在尝试各种可能适用的领域。

作者在书末附上的话

※ 本篇文章代表个人观点,不代表所属组织立场。

广告
将在 10 秒后关闭
bannerAds