根据 RethinkDB 中列表值中的外键过滤连接

Filtering a join based on foreign keys in list values in RethinkDB

我正在使用 RethinkDB (2.3.5~0trusty) 和 python (2.7.6) 和 python-rethinkdb 绑定(2.3.0.post6),并尝试查询预先存在的数据库。 (即请假设我无法更改我正在使用的模式)

这是我遇到的问题的简化示例。

tables/etc 的名称已移至人们更常用的域中 到,但我遇到的问题在结构上是等价的(而且更紧凑)。

借款人

Schema: (borrower_id:key (primary), name :string, favourite_authors : list of author_id )

Sample data:
{ "borrower_id": "91a15585-f084-41b3-9df1-1a3b16a8daed",
  "name": "Jo",
  "favourite_authors" : [ "b9503702-8832-43c8-a3f0-34691635419a",
                          "3bae9a66-2de6-4c64-ae95-c5f7caad86bb",
                        ]
}
{ "borrower_id": "23a8a193-c32e-4332-a40b-2ba56d158205",
  "name": "Bob",
  "favourite_authors" : [ "41305d3b-2819-4af5-be62-3c7999c4d747",
                          "d270f08d-aab1-4644-8dea-8f4fdd2d80b4"
                        ]
}
{ "borrower_id": "01031fb0-35de-4324-af47-611fec9ca7ad",
  "name": "Sam",
  "favourite_authors" : [ "b9503702-8832-43c8-a3f0-34691635419a"
                        ]
}

作者

Schema: (author_id:key (primary), name :string, books_written : list of book_id )

Sample data:
{ "author_id": "b9503702-8832-43c8-a3f0-34691635419a",
  "name": "Joanna Smith",
  "books_written" : [ "c1a48e2e-a831-4f5b-95b2-9b429dcf34e5",
                      "8f0e89b6-78e8-45ec-b7db-9cf3e00e0a8d",
                    ]
}
{ "author_id": "3bae9a66-2de6-4c64-ae95-c5f7caad86bb",
  "name": "John Smith",
  "books_written" : [ "8f0e89b6-78e8-45ec-b7db-9cf3e00e0a8d",
                      "b9fb4de0-e3bd-4df1-b192-c9a0ae7fb2e1",
                    ]
}
{ "author_id": "41305d3b-2819-4af5-be62-3c7999c4d747",
  "name": "Jo Smith",
  "books_written" : [ "b9fb4de0-e3bd-4df1-b192-c9a0ae7fb2e1",
                      "37b6eb03-e8ea-43dc-b3e4-ffc0bbfb1154",
                    ]
}
{ "author_id": "d270f08d-aab1-4644-8dea-8f4fdd2d80b4",
  "name": "Jim Smith",
  "books_written" : [ "8f0e89b6-78e8-45ec-b7db-9cf3e00e0a8d",
                      "37b6eb03-e8ea-43dc-b3e4-ffc0bbfb1154",
                    ]
}

书籍

Schema: (book_id:key (primary), name:string, book_info: object, may contain a data
                                                        dict, that has a list of
                                                        repeatable metadata options...)

Sample data:
{ "book_id": "c1a48e2e-a831-4f5b-95b2-9b429dcf34e5",
  "name": "",
  "book_info" : {
      "data" : [
                { "tag": "sf },
                { "period" : "past"}
               ]
        }
}
{ "book_id": "8f0e89b6-78e8-45ec-b7db-9cf3e00e0a8d",
  "name": "",
  "book_info" : {
      "data" : [
                { "tag": "romance },
                { "period" : "present"}
               ]
        }
}
{ "book_id": "89b68f0e-78e8-45ec-b7db-9cf3e00e0a8d",
  "name": "",
  "book_info" : {
      "data" : [
                { "period" : "present"}
               ]
        }
}
{ "book_id": "b9fb4de0-e3bd-4df1-b192-c9a0ae7fb2e1",
  "name": "",
  "book_info" : {
      "data" : [
                { "tag": "sf },
                { "tag": "romance},
                { "period" : "present"}
               ]
        }
}
...
{ "book_id": "37b6eb03-e8ea-43dc-b3e4-ffc0bbfb1154",
  "name": "",
  "book_info" : {
      "data" : [
                { "tag": "sf },
                { "period" : "future"}
               ]
        }
}

现在,我要执行的常见查询等同于:

注意:并非所有图书在图书信息的数据部分都有标签选项...

我正在尝试找出连接和过滤器的组合 RethinkDB 的 ReQL 查询接口——我确信它可以做到这一点—— 但我看不到一个明显的方法。

我的出发点是查看 RethinkDB 的各种连接选项, 但我看不到任何使用属性执行连接的明显方法 它包含一个外键列表而不仅仅是一个原子键。 (我通常会把外键放在得到的字段上 重复或有关系 table,但正如我所说,我无法更改 我得到的结构)

我更喜欢面向 python 的解决方案,但是 javascript(或任何其他语言)会很方便,因为我可以转换 :-)

欢迎提出任何建议。

此代码 (Javascript) 将执行以下操作:

r.db("test").table("Borrowers").filter(function(borrower){ 
  return borrower("favourite_authors").setIntersection(r.db("test").table("Authors").filter(function(author){ 
    return author("books_written").setIntersection(r.db("test").table("Books").filter(function(book){ 
      return book("book_info")("data").contains({"tag": "sf"}); })("book_id").coerceTo("array")).isEmpty().not();})("author_id").coerceTo("array")).isEmpty().not();}) 

但是在仅包含示例数据的数据库上已经很慢了(在我的服务器上为 35-70 毫秒)

它基本上是 3 个子查询的结合:

1:

r.db("test").table("Books").filter(function(book){ 
  return book("book_info")("data").contains({"tag": "sf"}); })("book_id").coerceTo("array")

这是最内在的。它检索一个数组,其中包含所有标记为 sf 的书籍的 id。该数组放入以下子查询中:

r.db("test").table("Authors").filter(function(author){ 
  return author("books_written").setIntersection(<book ids go here>).isEmpty().not();})("author_id").coerceTo("array")

它检索参与一本或多本给定书籍的所有作者 ID 的数组。它通过作者书籍和科幻书籍数组的交集的非空性进行过滤。 (如果交集不为空,则至少作者的一本书被标记为sf)

r.db("test").table("Borrowers").filter(function(borrower){ 
  return borrower("favourite_authors").setIntersection().isEmpty().not();})

最后一部分是基于与第二部分相同的原则,最后returns喜欢写过科幻书的作者的借用者。