Передача динамического количества переменных в функции Datomic API

Это репост вопроса, который я задал в группе Datomic google (см. здесь - последние 3 поста лучше).

Эта проблема связана с тем, что я динамически генерирую запросы Datomic. Таким образом, количество параметров (имен и значений) неизвестно до тех пор, пока они не будут переданы в функцию. Я могу сгенерировать запрос просто отлично. Основная проблема заключается в том, что с помощью Clojure Datomic API я не могу вызвать функцию datomic.api/q с динамическим числом аргументов. Таким образом, приведенное ниже выражение ~@unquote-splice не работает. Я попробовал несколько других подходов, в том числе частично и применить, но безрезультатно.

(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 сообщения здесь, чтобы получить более подробную информацию.

Спасибо


person Nutritioustim    schedule 09.09.2013    source источник
comment
Я мог бы придумать 1000 способов решить эту проблему. Почему не выкладываете полный макрос?   -  person Leon Grapenthin    schedule 09.09.2013
comment
Вы пробовали (def invoke-query (partial apply d/q)) с (invoke-query '[:find ?e :in $ ?title :where [?e :posts/title ?title]] [(d/db conn) "Post-Title!"])   -  person Leon Grapenthin    schedule 09.09.2013
comment
@lgrapenthin Конечно, последние попытки есть на github (ссылка здесь). Я хочу иметь возможность передать его с помощью (crud/retrieve-entity conn {:content-type "c/t" :title "t"}) или (crud/retrieve-entity conn {:content-type "c/t"}).   -  person Nutritioustim    schedule 09.09.2013


Ответы (2)


Итак, на основе вашего исходного кода мне удалось немного упростить код:

(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. Если вы знаете, как делать запросы, гораздо эффективнее на самом деле запросить один раз для всех сущностей, а затем отфильтровать их, выполнив обычное преобразование последовательности (а для огромной базы данных используйте редукторы!). Это работает в среднем. В 20 раз быстрее, чем Datalog (оценка с использованием критерия). Если вы мне не верите, напишите запрос, который запрашивает все ваши объекты :post/x из базы данных, а затем отфильтровывает сообщения, соответствующие вашим критериям, используя filter и так далее. Если у вас большая база данных, используйте редьюсеры с foldcat.
  3. Я написал себе механизм фильтрации (для групп сущностей), который служит своего рода DSL-запросом. Я изменяю его в зависимости от проекта и структуры базы данных. Возможность писать DSL — это сила LISP, и Datomic делает это действительно возможным благодаря сущностям, представляющим собой хэш-карту, подобную структурам данных. Он работает намного быстрее, чем Datalog (за счет того, что он зависит от предметной области).
person Leon Grapenthin    schedule 10.09.2013
comment
я) Красиво. Спасибо, что поделился. Помогает. Если вы перейдете к моему коду , вы можете видеть, что я действительно пошел по пути, который вы только что отстаивали. Я создаю предложения in- и where-. Большой победой для меня стала возможность передать массив аргументов. затем попросите форму привязки массива назначить для меня переменные datomic (см. здесь). Так что мой предыдущий цикл unquote/eval исчез. ii) Мне нравится ваш подход к преобразованию запроса/последовательности. Я собираюсь попробовать. - person Nutritioustim; 10.09.2013
comment
? Код, который я разместил, является упрощением кода, на который вы ссылаетесь. Это производное от него. Он генерирует :in и :where. - person Leon Grapenthin; 10.09.2013
comment
Ницца. Я определенно отрефакторил вашу функцию add-entity-ns. Ваш обновленный retrive-entity где-то дает сбой. Но я уверен, что это очень близко. См. обновленный код здесь. Я также возьму запрос на вытягивание, если вам нужен кредит. Рад это сделать. Я повторю попытку retrive-entity сегодня вечером. Спасибо :) - person Nutritioustim; 11.09.2013
comment
Хм, технически ответ на вопрос - это то, что у меня есть в ответе выше. В частности, передайте массив аргументов ["t" "c/t"], а затем используйте форму привязки массива, чтобы извлечь переменные. Поэтому убедитесь, что в вашем ответе есть это. Но мне нравятся ваши оптимизации, поэтому я дам вам баллы, если вы проголосуете и за мою :) Что касается функции retrieve-entity, имейте в виду, что она будет легче использовать форму карты. См. раздел Форма списка и карты здесь. ХТН. - person Nutritioustim; 11.09.2013
comment
Я сделал запрос на вытягивание. Я не уверен, что вы подразумеваете под передачей массива аргументов. Посмотрите, делает ли retrieve-entity-2 то, что вы хотите. - person Leon Grapenthin; 11.09.2013
comment
@LeonGrapenthin Пробовали ли вы писать Datomic-запросы таким образом без DSL? 1) Всеобъемлющее соединение для рассматриваемых сущностей в верхней части запроса. 2) Datomic правила, которые фильтруют объекты, т. е. они никогда не должны ссылаться на объекты выше в иерархии, а только на более низкие. - person m33lky; 05.03.2017

Хорошо, я решил эту проблему в группах Datomic Google (см. здесь). В принципе, рис.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

person Nutritioustim    schedule 09.09.2013