2

これは、私が Datomic google グループで行った質問の再投稿です (ここを参照してください - 最後の 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. クエリの方法を知っている場合は、実際にすべてのエンティティに対して 1 回クエリを実行してから、通常のシーケンス変換を実行してそれらを除外する方がはるかに優れています (巨大なデータベースの場合は、リデューサーを使用してください!)。これは平均で実行されます。Datalog より 20 倍高速 (基準を使用してベンチマーク)。信じられない場合は、データベースからすべての :post/x エンティティを照会するクエリを作成し、条件に一致する投稿を除外するなどを使用filterします。大規模なデータベースがある場合は、レデューサーをfoldcat.
  3. 私は、一種のクエリ DSL として機能するフィルタリング エンジン (多数のエンティティ用) を自分で作成しました。プロジェクトとデータベース構造に応じて変更します。DSL を記述できることは LISP の能力であり、Datomic では、エンティティがデータ構造のようなハッシュ マップであるため、それが可能になります。Datalog よりもはるかに高速に実行されます (ドメイン固有であるという犠牲を払って)。
于 2013-09-10T15:27:23.367 に答える