2014-02-18 38 views
2

我正在使用Python Flask構建一個API。我喜歡在RethinkDB上編碼合併查詢的輕鬆方式。更好的是,我注意到可以編寫一個薄層來對用戶輸入進行動態合併查詢編碼。RethinkDB:​​如何動態構建合併查詢?

假設我們正在構建一個聊天應用程序。下面是示例代碼:https://github.com/dogukantufekci/rethinkdb_dynamic_merge

表和字段:

  1. 帳戶: 「ID」, 「created_on」, 「姓名」, 「電子郵件」, 「密碼」
  2. 對話: 「ID」 ,「created_on」,「subject」,「to」(參與者列表)
  3. message:「id」,「created_on」,「text」,「conversation」,「from」
  4. message_readers:「id」, 「message」,「reader」

查詢合併所有4個表:

r.table("accounts").map(lambda account: 
    account.merge({ 
     "conversations": r.table("conversations").filter(lambda conversation: 
      conversation["to"].contains(account["id"])).coerce_to("array").map(lambda conversation: 
      conversation.merge({ 
       "to": conversation["to"].map(lambda account: 
        r.table("accounts").get(account)).coerce_to("array"), 
       "messages": r.table("messages").filter(lambda message: 
        message["conversation"] == conversation["id"]).coerce_to("array").map(lambda message: 
        message.merge({ 
         "from": r.table("accounts").get(message["from"]), 
         "readers": r.table("message_readers").filter(lambda readers: 
          readers["message"] == message["id"]).coerce_to("array"), 
        })) 
      })) 
    })).run(g.db_connection) 

結果:

[{ 
    "id": "account111", 
    "created_on": 1392515093.252, 
    "name": "John Doe", 
    "email": "[email protected]", 
    "conversations": [ 
     { 
      "id": "conversation111", 
      "created_on": 1392515093.252, 
      "subject": "Merging Queries on RethinkDB", 
      "to": [ 
       { 
        "id": "account111", 
        "created_on": 1392515093.252, 
        "name": "John Doe", 
        "email": "[email protected]", 
       }, 
       { 
        "id": "account222", 
        "created_on": 1392515604.123, 
        "name": "Mark Bobby", 
        "email": "[email protected]", 
       }, 
      ], 
      "messages": [ 
       { 
        "id": "message111", 
        "created_on": 1392515604.123, 
        "text": "How do we dynamically build merge queries?", 
        "conversation": "conversation111", 
        "from": { 
         "id": "account111", 
         "created_on": 1392515093.252, 
         "name": "John Doe", 
         "email": "[email protected]", 
        }, 
        "readers": [ 
         { 
          "id": "message_reader111", 
          "created_on": 1392515604.123, 
          "message": "message111", 
          "reader": "account111", 
         }, 
         { 
          "id": "message_reader222", 
          "created_on": 1392515604.123, 
          "message": "message111", 
          "reader": "account222", 
         }, 
        ], 
       }, 
      ], 
     }, 
    ],   
}] 

大爲止!

更簡單的響應需要通過對話返回帳戶數據;沒有消息:

[{ 
    "id": "account111", 
    "created_on": 1392515093.252, 
    "name": "John Doe", 
    "email": "[email protected]", 
    "conversations": [ 
     { 
      "id": "conversation111", 
      "created_on": 1392515093.252, 
      "subject": "Merging Queries on RethinkDB", 
      "to": [ 
       { 
        "id": "account111", 
        "created_on": 1392515093.252, 
        "name": "John Doe", 
        "email": "[email protected]", 
       }, 
       { 
        "id": "account222", 
        "created_on": 1392515604.123, 
        "name": "Mark Bobby", 
        "email": "[email protected]", 
       }, 
      ], 
     }, 
    ],   
}] 

有兩種方法可以得到這樣的結果:

  1. 我們可以重新編寫一個查詢:

    r.table("accounts").map(lambda account: 
        account.merge({ 
         "conversations": r.table("conversations").filter(lambda conversation: 
          conversation["to"].contains(account["id"])).coerce_to("array").map(lambda conversation: 
          conversation.merge({ 
           "to": conversation["to"].map(lambda account: 
            r.table("accounts").get(account)).coerce_to("array"), 
          })) 
        })).run(g.db_connection) 
    

    缺點:如果有必要進一步建立查詢替代字段組合,這不是最佳實踐,因爲它不是動態的,並且有很多重複。

  2. 我們可以通過修改大型查詢的最後一行用勇氣來選擇字段:

    })).pluck(["id", "created_on", "name", "email", {"conversations": ["id", "created_on", "subject", {"to": ["id", "created_on", "name", "email"]}]}]).run(g.db_connection) 
    

    優勢:它是動態的,因爲它可以讓用戶通過URL掐值作爲參數

    http://www.myproject.com/accounts/?pluck=["id", "created_on", "name", "email", {"conversations": ["id", "created_on", "subject", {"to": ["id", "created_on", "name", "email"]}]}] 
    

    缺點:查詢的確消耗了大量的計算能量來合併在最終結果中不需要的表。

所以挑戰在於通過接受來自用戶的pluck值來動態構建查詢。

可以容易地注意到兩個公約:

  1. 每個字典字段具有它接受一個字典對象的查詢:

    "messages": r.table("messages").filter(lambda message: 
        message["conversation"] == conversation["id"]).coerce_to("array").map(lambda message: 
         message.merge({}) 
    
  2. 每個非字典字段具有一個獨立的查詢:

    "from": r.table("accounts").get(message["from"]) 
    

那麼我們如何才能使用所有這些片段信息並構建我們漂亮的動態合併查詢?

+0

你能不能給一個動態合併查詢的例子嗎? (「字段」和輸出的值) – neumino

+0

@neumino我編輯了這個問題。 –

+0

你可以使用一些r.branch,但這並不會讓事情變得更好,因爲動態字段需要不同的子查詢。 – neumino

回答

1

我的建議是放棄動態部分。相反,您應該以RESTful方式設計API。這意味着,舉個例子,如果有人想訪問一個賬戶,他們可以發送GET請求到/ accounts/[identifier]。如果他們想要發送或接收帳戶的所有消息,他們會向/ accounts/[identifier]/messages發送GET請求。

另外,爲什麼你需要一個對話對象?我會改變你的數據庫結構如下:

  1. 帳戶: 「ID」, 「created_on」, 「姓名」, 「電子郵件」, 「密碼」 - 不變
  2. 對話: - 刪除
  3. 消息: 「ID」, 「created_on」, 「文本」, 「發件人」, 「接收器」
  4. message_readers: - 刪除
+0

感謝您的建議,但我不是唯一一個設想API服務動態查詢的人。查看Facebook Graph API Explorer http://i.imgur.com/qiwsCQ4.png –