Datomic 中的查询结果分页

Query result pagination in Datomic

我想解决一个假设情况,但找不到理想的答案。假设您有一个可以从查询中 return 编辑的庞大数据集,您如何对其进行分页以使对内存的影响最小? datoms API, iterating over the datoms and filtering one by one? The index-range API,但我必须做与 datoms API 相同的事情,遍历项目并逐一过滤?执行一个 return 只有 id 的初始查询,然后对这些 id 进行分页,以便它们可以在另一个查询中使用以检索整个数据集?

在 SQL 中,您通常可以在查询本身中定义分页:

SELECT col1, col2, ...
 FROM ...
 WHERE ... 
 ORDER BY -- this is a MUST there must be ORDER BY statement
-- the paging comes here
OFFSET     10 ROWS       -- skip 10 rows
FETCH NEXT 10 ROWS ONLY; -- take 10 rows

你看过这个页面吗:http://docs.datomic.com/query.html#memory-usage

好像说所有的中间结果都要装进内存。我认为这也适用于最终结果。

您可以尝试询问:https://forum.datomic.com/


旁注:当 Datomic returns 和 entity 时,它是 "lazy map" 的一种形式,即不完全可见明确使其具体化,例如

(let [plain-map (into {} entity-map) ]
  (println plain-map))

有很多事情要考虑。

首先,在撰写本文时,Datomic 附带的 Datalog 实现是急切的,并且不会溢出到磁盘,这意味着 Datalog 查询的结果集必须适合内存。

这并不意味着 Datalog 与大结果不兼容,因为您可以让每个 Datalog 查询只处理一小部分数据。例如,您可以使用 Datalog 来计算查询的 'logical' 部分(return 的实体),以及实体 API 或 Pull API 到(懒惰地)计算查询的 'content' 部分(每个实体的 return 属性)。鉴于实体 ID 只是一个 Java 长(8 字节),这可以为您节省两个数量级的内存占用量之一。使用实体 API:

的示例
(defn export-customers 
  [db search-criteria]
  (->> 
    ;; logical part - Datalog-based, eager
    (d/q '[:find [?customer ...] :in % $ ?search-criteria :where
           (customer-matches-criteria ?search-criteria ?customer)]
      (my-rules) db search-criteria)
    ;; content part - Entity API based, lazy
    (map (fn [eid]
           (let [customer (d/entity db eid)]
             (select-keys customer 
               [:customer/id 
                :customer/email
                :customer/firstName
                :customer/lastName
                :customer/subscription-time]))))
    ))

您可以通过急切地将整个结果存储在辅助 blob 存储中来补充这种方法,然后针对它进行轮询以进行分页。

如果您的查询逻辑不是太复杂,您也可以想象根本不使用 Datalog,例如使用原始索引访问(例如使用 Datoms API 或索引范围 API)以一种懒惰的方式。

最后,您应该考虑到 Datomic 可能不适合为您的分析查询提供服务。由于 Datomic 的变化检测是微不足道的,因此将派生数据流式传输到二级存储相当容易,二级存储可以更好地计算分析查询(例如 ElasticSearch、Google BigQuery、PostgreSQL 等)