【真的假的】2017/4 Mapping refactor

Current structure

為求專注在元件邏輯本身,author 相關 mappings(users、apps)與相關欄位外鍵(userId、from)之間的線省略。

graph mappings{
  rankdir=LR;
  node[shape=record];

  
  users [label="
    𝐮𝐬𝐞𝐫𝐬
    |
    email\l
    name\l
    avatarUrl\l
    facebookId\l
    githubId\l
    twitterId\l
    createdAt\l
    updatedAt\l
  "]
  
  apps [label="
    𝐚𝐩𝐩𝐬
    |
    adminUserId\l
    secret\l
    redirectUrl\l
    createdAt\l
    updatedAt\l
  "]

  replyrequests [fixedsize="true" width="2" height="1.5" label="
    <id>𝐫𝐞𝐩𝐥𝐲𝐫𝐞𝐪𝐮𝐞𝐬𝐭𝐬
    |
    <user>
    userId\l
    from\l
    |
    createdAt\l
    updatedAt\l
  "]

  replyconnections [fixedsize="true" width="2" height="2" label="
    <id>𝐫𝐞𝐩𝐥𝐲𝐜𝐨𝐧𝐧𝐞𝐜𝐭𝐢𝐨𝐧𝐬
    |
    <user>
    userId\l
    from\l
    |
    <reply>
    replyId\l
    |
    <feedbacks>
    feedbackIds\l
    |
    createdAt\l
    updatedAt\l
  "]
  
  replyconnectionfeedbacks [fixedsize="true" width="3" height="2" label="
    <id>𝐫𝐞𝐩𝐥𝐲𝐜𝐨𝐧𝐧𝐞𝐜𝐭𝐢𝐨𝐧𝐟𝐞𝐞𝐝𝐛𝐚𝐜𝐤𝐬
    |
    userId\l
    from\l
    |
    score\l
    comment\l
    createdAt\l
    updatedAt\l
  "]
  
  replies [label="
    <id>𝐫𝐞𝐩𝐥𝐢𝐞𝐬
    |
    createdAt\l
    |
    versions[].userId\l
    versions[].from\l
    |
    versions[].type\l
    versions[].text\l
    versions[].reference\l
    versions[].createdAt\l
  "]
  
  articles [label="
    𝐚𝐫𝐭𝐢𝐜𝐥𝐞𝐬
    |
    <replyconnections>replyConnectionIds\l
    |
    <deletedreplyconnections>deletedReplyConnectionIds\l
    |
    <replyrequests>replyRequestIds\l
    |
    text\l
    createdAt\l
    updatedAt\l
    |
    userId\l
    from\l
    |
    references[].type\l
    references[].permalink\l
    references[].createdAt\l
  "]
  
  articles:replyrequests -- replyrequests:id [headlabel="n",taillabel="1"];
  articles:replyconnections -- replyconnections:id [headlabel="n",taillabel="1"];
  articles:deletedreplyconnections -- replyconnections:id [headlabel="n",taillabel="1"];
  replyconnections:feedbacks -- replyconnectionfeedbacks:id [headlabel="n",taillabel="1"];
  replyconnections:reply -- replies:id [headlabel="1",taillabel="n"];
}

Authentication mappings & fields

當使用者在使用 LINE bot 的時候,使用者並沒有來到 cofacts 網站進行登入。然而,LINE bot 卻能夠送出 article 以及 replyrequest。此時,articles 與 replyrequests 的作者相關欄位,應該要怎麼填寫呢?

LINE bot client 本身拿得到 LINE user ID,因此可以拿 LINE 本身的 user ID 來作為判斷使用者是否為一則 article 或 replyrequest 的 identifier。但是,各個 client (LINE、web、甚至是未來會支援的第三方 client)的 user ID 都不同,因此需要 from 欄位。

from 欄位是由 rumors-api 填寫的。Client app 會送 secret 與想要存入的 userId 到 rumors-api,rumors-api 比對 secret 正確之後,就會把 userId 以及相對應的 from 欄位值填入。未來開放第三方 client 之後,我們可以發放 app ID & secrets(也就是目前沒有作用的 apps index),而 from 欄位可以用拿來填寫 app ID。

當 client app 存取 rumors-api 的 users 相關欄位的時候,rumors-api 會比對 app ID 以及 userId,如果是現在的使用者,才會回傳 users 相關欄位,避免個資外洩。

Design choice

articles 對 replyconnections 的 1:n 關係中,replyConnectionIds 的外鍵之所以會存放在 articles 而非 replyConnections,主要是為了讓「列出文章時可以列出 reply 數」這個 query 可以只要查 articles 即可。

replyconnections - replyconnectionfeedbacks、articles - replyrequests 這兩個 1:n 關係會這樣設計也是同樣的理由,即使實際上採用了這種設計,其實無法保證 1:n 關係不會變成 m:n 關係(因為一個 replyrequests 的 ID 可以出現在複數個 articles 裡頭,所以其實不是完美的 1:n)。

問題:新 filter / sorting 需求

原本用來應付 cofacts 網頁文章列表 的 index mapping 機制,無法應付下面的這些花俏的 requests——但偏偏這些 requests 非常重要。

(From: https://github.com/cofacts/rumors-db/issues/7#issuecomment-293507662 )

上述 filter / sort 希望可以 aggregate 在一起,例如:找出「沒人標記為『等等回應』」的文章中,回應同時有「含有真實資訊」又有「含有不實資訊」的醫療相關文章。

最後,我們也希望能在新的 schema 中放入 segment 的設計,用處是在顯示文章列表時,可以給小編更細緻的、針對個別段落的 reply 連結建議。

Proposed structure

graph mappings{
  rankdir=LR;
  node[shape=record];

  replyrequests [fixedsize="true" width="2" height="1.5" label="
    <id>𝐫𝐞𝐩𝐥𝐲𝐫𝐞𝐪𝐮𝐞𝐬𝐭𝐬
    |
    <user>
    userId\l
    appId\l
    |
    <article>
    _parent\l
    |
    createdAt\l
    updatedAt\l
  "]

  replyconnections [fixedsize="true" width="2.5" height="5" label="
    <id>𝐚𝐫𝐭𝐢𝐜𝐥𝐞𝐫𝐞𝐩𝐥𝐢𝐞𝐬
    |
    <user>
    userId\l
    appId\l
    |
    <article>
    _parent\l
    |
    currentReply.userId\l
    currentReply.appId\l
    currentReply.type\l
    currentReply.text\l
    currentReply.reference\l
    currentReply.createdAt\l
    |
    <reply>
    replyId\l
    |
    segment\l
    segmentRangeStart\l
    segmentRangeEnd\l
    |
    status\l
    createdAt\l
    updatedAt\l
  "]
  
  replyconnectionfeedbacks [fixedsize="true" width="3" height="2" label="
    <id>𝐚𝐫𝐭𝐢𝐜𝐥𝐞𝐫𝐞𝐩𝐥𝐲𝐟𝐞𝐞𝐝𝐛𝐚𝐜𝐤𝐬
    |
    <replyconnection>
    _parent\l
    |
    userId\l
    appId\l
    |
    score\l
    comment\l
    createdAt\l
    updatedAt\l
  "]
  
  replies [label="
    <id>𝐫𝐞𝐩𝐥𝐢𝐞𝐬
    |
    createdAt\l
    |
    {
      versions\n(nested) |
      {
        userId\l
        appId\l
        |
        type\l
        text\l
        reference\l
        createdAt\l
      }
    }
  "]
  
  articles [label="
    <id>
    𝐚𝐫𝐭𝐢𝐜𝐥𝐞𝐬
    |
    text\l
    createdAt\l
    updatedAt\l
    |
    userId\l
    appId\l
    |
    {
      references\n
      (nested)
      |
      {
        type\l
        permalink\l
        createdAt\l
        |
        userId\l
        appId\l
      }
    }
    |
    {
      pendingRepliers
      \n(nested)
      |
      {
        userId\l
        appid\l
        |
        createdAt\l
      }
    }
    |
    <tags>tags\l
  "]
  
  tags[label="
    <id>𝐭𝐚𝐠𝐬
    |
    <title>title\l
    |
    description\l
    |
    userId\l
    appId\l
  "]
  
  
  articles:id -- replyrequests:article [headlabel="n",taillabel="1"];
  articles:id -- replyconnections:article [headlabel="n",taillabel="1"];
  articles:tags -- tags:title [headlabel="m",taillabel="n"];
  replyconnections:id -- replyconnectionfeedbacks:replyconnection [headlabel="n",taillabel="1"];
  replyconnections:reply -- replies:id [headlabel="1",taillabel="n"];
}

Design choice

需求確認