2

这是我在 Datomic google group 上提出的一个问题的转贴(见这里 - 最后 3 个帖子最好)。

这个问题围绕着我动态生成 Datomic 查询这一事实。所以参数(名称和值)的数量在传递给函数之前是未知的。我可以很好地生成查询。核心问题是,使用 Clojure Datomic API,我无法使用动态数量的参数调用datomic.api/q函数。所以下面的~@unquote-splice表达式失败了。我尝试了其他几种方法,包括partialapply,但无济于事。

(def expression-final `(datomic.api/q ~expression-intermediate ~db-conn ~@param-values))   ;; gives the error in question
(eval expression-final)


 java.lang.Exception: processing rule: (q__34868 ?e)
    at datomic.datalog$eval_rule$fn__4687.invoke(datalog.clj:977)
    at datomic.datalog$eval_rule.invoke(datalog.clj:957)
    at datomic.datalog$eval_query.invoke(datalog.clj:999)
    at datomic.datalog$qsqr.invoke(datalog.clj:1053)
    at datomic.datalog$qsqr.invoke(datalog.clj:1021)
    at datomic.query$q.invoke(query.clj:453)
    at datomic.api$q.doInvoke(api.clj:31)
    ... 1 stack levels elided ...
    at user$eval34866.invoke(crud_spec.clj:32)
    ... 3 stack levels elided ...
    at stefon_datomic.crud$retrieve_entity.invoke(crud.clj:95)
    ...
 Caused by: java.lang.Exception: processing clause: [?e :posts/title (quote ?title)]      ;; this fails with or without the (quote ...)
    at datomic.datalog$eval_clause$fn__4667.invoke(datalog.clj:934)
    at datomic.datalog$eval_clause.invoke(datalog.clj:900)
    at datomic.datalog$eval_rule$fn__4687.invoke(datalog.clj:972)
    at datomic.datalog$eval_rule.invoke(datalog.clj:957)
    ...
 Caused by: java.lang.UnsupportedOperationException: nth not supported on this type: Symbol
    ... 2 stack levels elided ...
    at datomic.datalog$extrel_coll$fn__4384.invoke(datalog.clj:197)
    ... 4 stack levels elided ...
    at datomic.datalog$iterator.invoke(datalog.clj:30)

我想知道这是否是 Datomic Clojure API 中的错误?或者,如果有更简单的方法来传递动态数量的变量。硬编码传入的变量数量,违背了动态生成查询的目的。请参阅此处的最后3 个帖子,以获取更多详细信息。

谢谢

4

2 回答 2

6

因此,根据您的源代码,我设法简化了代码:

(defn add-entity-ns
  [ekey datom-map]
  (reduce-kv (fn [a k v]
               (assoc a (keyword
                         (name ekey)
                         (name k))
                      v))
             {}
             datom-map))

(defn retrieve-entity
  [conn constraint-map]
  (let [name-fn (comp symbol
                      (partial str "?")
                      name)
        param-names (map name-fn
                         (keys constraint-map))
        param-vals (vals constraint-map)
        constraint-map (add-entity-ns :posts constraint-map)
        where-clause (map #(vector '?e % %2)
                      (keys constraint-map)
                      param-names)
        in-clause (conj param-names '$)
        final-clause (concat [:find '?e]
                             [:in] in-clause
                             [:where] where-clause)]
    (apply d/q final-clause (d/db conn) param-vals)))

注意:当我开始使用 datomic 时,我自己编写了类似的函数来生成查询。我最终把它们扔掉了,有几个原因:

  1. 一团糟。Datalog 是为进行逻辑查询而设计的。生成逻辑查询并调用它需要更长的时间,即使 datomic 进行缓存也是如此。这是因为 Datalog 必须在逻辑上解决查询,这需要相当长的时间。
  2. 如果您知道如何查询,那么对所有实体实际查询一次然后将它们过滤掉,执行通常的序列转换(对于大型数据库,使用减速器!)会更好。这执行平均。比 Datalog 快 20 倍(使用标准进行基准测试)。如果您不相信我,请编写一个查询,从数据库中查询您的所有 :post/x 实体,然后过滤掉与您的条件匹配的帖子filter等等。如果您有一个大型数据库,请使用带有foldcat.
  3. 我为自己编写了一个过滤引擎(用于实体束),用作查询 DSL。我根据项目和数据库结构对其进行修改。能够编写 DSL 是 LISP 的强大功能,而 Datomic 使其真正成为可能,这要归功于实体是像数据结构一样的哈希映射。它的执行速度比 Datalog 快(以特定于域为代价)。
于 2013-09-10T15:27:23.367 回答
2

好的,我在 Datomic Google Groups 上解决了这个问题(见这里)。基本上,图 1有效。而且,我能够运行生成的代码(图 2)而无需可怕的引用。希望这对其他人有帮助。

(datomic.api/q '{:find [?e]
                 :in [$ [?title ?content-type]]
                 :where [[?e :posts/title ?title] [?e :posts/content-type ?content-type]]}
               (datomic.api/db conn)
               ["t" "c/t"])

图。1

(datomic.api/q expression-intermediate-0 (datomic.api/db conn) (into [] param-values))

图2

于 2013-09-09T19:23:42.423 回答