2

我正在使用 Python Flask 构建一个 API。我喜欢在 RethinkDB 上轻松编写合并查询。更好的是,我注意到可以编写一个薄层来编码关于用户输入的动态合并查询。

假设我们正在构建一个聊天应用程序。这是示例代码:https ://github.com/dogukantufekci/rethinkdb_dynamic_merge

表格和字段:

  1. 帐户:“id”、“created_on”、“名称”、“电子邮件”、“密码”
  2. 对话:“id”、“created_on”、“subject”、“to”(参与者列表)
  3. 消息:“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": "john@doe.com",
    "conversations": [
        {
            "id": "conversation111",
            "created_on": 1392515093.252,  
            "subject": "Merging Queries on RethinkDB",
            "to": [
                {
                    "id": "account111",
                    "created_on": 1392515093.252,  
                    "name": "John Doe", 
                    "email": "john@doe.com", 
                }, 
                {
                    "id": "account222",
                    "created_on": 1392515604.123,  
                    "name": "Mark Bobby", 
                    "email": "mark@bobby.com", 
                }, 
            ], 
            "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": "john@doe.com",
                    }, 
                    "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": "john@doe.com",
    "conversations": [
        {
            "id": "conversation111",
            "created_on": 1392515093.252,  
            "subject": "Merging Queries on RethinkDB",
            "to": [
                {
                    "id": "account111",
                    "created_on": 1392515093.252,  
                    "name": "John Doe", 
                    "email": "john@doe.com", 
                }, 
                {
                    "id": "account222",
                    "created_on": 1392515604.123,  
                    "name": "Mark Bobby", 
                    "email": "mark@bobby.com", 
                }, 
            ], 
        }, 
    ],        
}]

有两种方法可以得到这个结果:

  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 修改大查询的最后一行来选择字段:

    })).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. 每个 dict 字段都有一个接受 dict 对象的查询:

    "messages": r.table("messages").filter(lambda message:
        message["conversation"] == conversation["id"]).coerce_to("array").map(lambda message:
            message.merge({})
    
  2. 每个非 dict 字段都有一个独立的查询:

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

那么我们如何使用所有这些信息并构建我们漂亮的动态合并查询呢?

4

1 回答 1

1

我的建议是放弃动态部分。相反,您应该以 RESTful 方式设计 API。这意味着,对于您的示例,如果有人想访问一个帐户,他们可以向 /accounts/[identifier] 发送一个 GET 请求。如果您想要一个帐户发送或接收的所有消息,他们会向 /accounts/[identifier]/messages 发送一个 GET 请求。

另外,为什么需要一个对话对象?我会改变你的数据库结构如下:

  1. 帐户:“id”、“created_on”、“name”、“email”、“password” - 未更改
  2. 对话: - 删除
  3. 消息:“id”、“created_on”、“text”、“sender”、“receivers”
  4. message_readers: - 移除
于 2014-02-19T13:22:58.910 回答