Revision history and IPFS entry, back to latest
Horo
IPFS What is this

Content Hash

马特市的镜子

Horo
·
·
马特市有一面看的到自己(或者其他人)的镜子。

想借助外部的认知认识(或者审视)自己的时候大家会怎么办?对一部分人来说(可能汝也是其中之一?),其中一种方法就是照镜子。

马特市里也有这么一面镜子,和童话故事里的魔镜不同的是,它只会根据汝等的要求理性的返回对应的数据,大概如此。

它在这里: https://server.matters.news/graphql 。但是在开始之前,咱们还要有一些准备工作。

用 GraphQL 和马特市之镜交谈

就像人类的语言千差万别一样,马特市之镜她自己是不通那些人类在用的自然语言的。相对的,马特市的开拓者们教会了她一种名为 GraphQL 的语言用来和马特市民或者其他啥的(例如其它地方的人类或机器人?)交流。

如果想先练习一下这种语言的话,这里有一块地方说不定正合适(就是 Matters 的 GraphQL Playground 啦) : https://server.matters.news/playground ,它大概像这个样子。

在左边以 GraphQL 写下对镜子说的话,再按下中间的按钮,她就会以相通的方式回答汝啦。如果汝不知道怎么说的话,右边的 Docs 中有相应的文档可以查阅。(嗯?)

Docs 中的 Queries 和 Mutatons 分别代表两种操作(查询和变更,当然想变更啥的话得先用一些方法登录。),那咱们就先从找出自己开始吧。

千山万水人海中

在汝点击 Docs 中的 user(...) 一节后,就可以看到 User 类型能提供的信息,像是用户名和显示的名称什么的,当然啦,她总得知道汝要找的人是谁吧,于是需要汝通过 UserInput 提供一个用户名。

在知道了这些之后,就有方法组织想告诉她的第一句话了。

# 咱们是在查询一些内容,所以最外面是 query。
query {
 # 查询用户名为汝 userName 指定的用户。
 user(input: { userName: "kenookamihoro" }) 
}

但这样是不行的:

"Field \"user\" of type \"User\" must have a selection of subfields. Did you mean \"user { ... }\"?"

这句话的意思就是在告诉汝,汝只是让她找这个人是不行的,汝还得把汝想知道这个人的啥告诉她才行。

比如如果汝想让她告诉咱的显示的名称和 Liker ID 的话:

query {
 user(input: { userName: "kenookamihoro" }) {
 displayName
 likerId
 }
}
----
{
 "data": {
 "user": {
  "displayName": "Horo",
  "likerId": "kenookamihoro"
 }
 }
}

大概就是这个样子。

以及汝大概可能也发现了,她回应汝的话语是一种称作 JSON 的“数据交换语言”。虽然人类读起来稍微有些别扭,但假如汝有学习过一些操纵机器的魔法(以人类通行的语言来说,是各种程序设计语言啦)的话,也可以让它们展示的更直观些,这个要以后再聊了……

接下来,就来看看咱都写了些啥吧。

咱(或者汝所思之人)的马特市之路

就像汝看到的这样,汝在马特市发表的文章在她眼里就是一连串的 ArticlesConnection 组成的集合。所以就可以用和刚才相似的方法让她把它们整理下来告诉汝。

query {
 user(input: { userName: "kenookamihoro" }) {
 # 咱想要咱发表过的所有文章,所以 input 这里是空的。
 articles(input:{}){
  # 当然也得告诉她咱想知道咱所有文章的什么。
  totalCount
 }
 }
}
----
{
 "data": {
 "user": {
  "articles": {
   "totalCount": 84
  }
 }
 }
}

如果汝想知道其中的每一篇文章的话,就从她口中套出 edges 里面的 nodes 吧。(啥?)

query {
 user(input: { userName: "kenookamihoro" }) {
 # 咱想要咱发表过的所有文章,所以 input 这里是空的。
 articles(input: {}) {
  edges {
   node {
    # node 里面就是一个一个的 Article 啦,所以这里面就是汝想了解汝的每一篇文章里的啥咯。
    # 咱这里就是每篇文章的标题和字数啦。
    title
    wordCount
   }
  }
 }
 }
}
----
{
 "data": {
 "user": {
  "articles": {
   "edges": [
    {
     "node": {
      "title": "转载和一点点评论: “药娘QQ群”一步步诱骗 15岁儿子打针吃药“变性”",
      "wordCount": 2150
     }
    },
    # 后面的内容咱就省略掉啦。
   ]
  }
 }
 }
}

其他的 Connection 也可以这样如法炮制啦,那么现在,汝能看出告诉她下面这段话以后,咱会知道些啥了吗?

query {
 user(input: { userName: "kenookamihoro" }) {
 articles(input: {}) {
  edges {
   node {
    title
    appreciationsReceived(input: {}) {
     edges {
      node {
       sender {
        userName
       }
       amount
      }
     }
    }
    transactionsReceivedBy(input: { purpose: donation }) {
     totalCount
     edges {
      node {
       userName
      }
     }
    }
    appreciationsReceivedTotal
   }
  }
 }
 }
}
​

其它魔法的引子

假如汝有学习过一些操纵机器的魔法(以人类通行的语言来说,是各种程序设计语言啦)的话,汝就能用她的话语作为汝的魔法的一部分材料。

点击右侧的 “Copy cURL” 可以把这部分的咒语复制下来,大概就像这个样子。

curl 'https://server.matters.news/graphql' -H 'Accept-Encoding: gzip, deflate, br' -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'Connection: keep-alive' -H 'DNT: 1' -H 'Origin: https://server.matters.news' --data-binary '{"query":"query {\n  user(input: { userName: \"kenookamihoro\" }) {\n    articles(input: {}) {\n      edges {\n        node {\n          title\n          wordCount\n        }\n      }\n    }\n  }\n}\n"}' --compressed

当然啦,根据汝学习的魔法流派的不同(具体可以看看 GraphQL 基金会的介绍),汝说不定可以通过汝学习的魔法中的法术直接和她交谈。但这次咱就先偷懒一下……

咱这次用的是咱比较顺手的 Python 和 临场发挥的 Maplotlib ,虽然因为是一次性的缘故所以代码写的很粗糙,咱还是会试图解释一下。

(以及这就是上一篇文章那个图是怎么画出来的)

首先,咱用的 GraphQL 的查询是这样:

query {
 user(input: { userName: "kenookamihoro" }) {
 articles(input: {}) {
  edges {
   node {
    title
    createdAt
    wordCount
    appreciationsReceivedTotal
   }
  }
 }
 }
}
​

咱用来画图的代码大概就是这个样子:

#!/bin/env python3
​
import json
import matplotlib.pyplot as plt
​
# 咱之前已经提前把结果下载下来了,这里就是读取它。
with open("matters.json") as fin:
  raw = json.load(fin)
​
# nodes 就是咱的每一篇文章的抽象啦。
nodes = raw['data']['user']['articles']['edges'][::-1]
# likes 就是咱每一篇文章的获得赞的数量啦。
likes = [i['node']['appreciationsReceivedTotal'] for i in nodes]
# 这个 x 就是 图上的横轴。
# 文章发表的时间是 ‘2021-03-30T09:49:01.965Z’ 这样的形式。
# 考虑到咱没有在同一天内写过两篇文章,所以这里咱就偷懒用 'T' 前面的部分来做横轴的标题了。
x = [i['node']['createdAt'].split("T")[0] for i in nodes]
# mean 和 Median 是中位数和众数,因为图上它们其实是两条直线,
# 所以其实是一串中位数和众数组成的列表。
mean = [sum(likes)/len(likes)]*len(likes)
median = [(likes[len(likes)//2]+likes[(len(likes)//2)+1])/2]*len(likes)
​
fig, ax = plt.subplots()
# 开始画图啦,分别是横轴,纵轴和标记点。
data_line = ax.plot(x, likes, marker='o')
# 因为时间挺长的,所以让它们垂直显示。
plt.xticks(x, x, rotation='vertical')
​
# 画中位数和众数的直线,下面那个 annotate 就是旁边的注解。
mean_line = ax.plot(x, mean, label='Mean', linestyle='--')
plt.annotate(f'Mean = {mean[0]:.1f}',
      (x[0], mean[0]), 
      textcoords="offset points", 
      xytext=(-150, 10),
      ha='left')
​
median_line = ax.plot(x, median, label='Median', linestyle='--')
plt.annotate(f'Median = {median[0]:.1f}', 
      (x[0], median[0]), 
      textcoords="offset points", 
      xytext=(-150, -10),
      ha='left')
​
# 给折线图上的点标记数值
for i in range(len(nodes)):
  plt.annotate(likes[i], # this is the text
        (x[i], likes[i]), # this is the point to label
        textcoords="offset points", # how to position the text
        xytext=(10, 5), # distance from text to points (x,y)
        ha='center')
​
# 在右上角加上图例。
legend = ax.legend(loc='upper right')
​
# 最后把画好的图展示出来。
plt.show()
​

然后就是汝等在上一篇文章里看到的图啦。


这篇文章本来是要发在咱的围炉里的,但是目前围炉不能发表持久公开的文章,咱又有咱那点小小的原则在……所以还是先放在外面了。

如果汝等觉得咱这种有点改变(改变在那里啊?)的风格可以接受,又希望咱这样聊些不同的话题的话,欢迎来咱的围炉 「狼与马特市的幻想物语」,主题不定,汝说不定能在里面推荐咱写些啥。

这篇文章写完以后咱会给追踪者和咱觉得比较有趣的家伙,以及但愿以后会有的咱的赞赏公民送出邀请,就这样。(所以围炉啥时候有永久免费的邀请,以及咱和其他炉主聊天的时候吐槽过加入围炉要填信用卡这一步好像难倒了不少人的样子,当然 @Matty 在群里说有在想改进这个步骤了,那咱就拭目以待了。)

以及如果汝真的觉得咱的笔头不值得汝掏出那笔钱的话,在邀请结束前自己推出和取消追踪啥的只有随汝等喜欢了。

以上。

CC BY-NC-ND 2.0