datomic query — функция, которая фильтрует и связывает

Я хотел бы иметь возможность сделать что-то вроде этого:

(defn match? [m] (re-find (re-pattern "so (\\d+)") m))

(datomic.api/q 
  '[:find ?m
    :where [[?e :user/regex-match ?r]
            [(user/match? ?e) ?m]] 
   dbconn)

Это дает мне то, что я ожидаю, но это вызывает "соответствие?" дважды на объект:

(datomic.api/q 
  '[:find ?m
    :where [[?e :user/regex-match ?r]
            [(user/match? ?e) ?m]
            [(user/match? ?e)] 
   dbconn)

person matt_h    schedule 05.05.2013    source источник
comment
добавление (идентификация ?m) вместо вызова user/match? снова кажется лучшим решением, которое я нашел до сих пор. Интересно, идиоматично ли это в datomic запросах?   -  person matt_h    schedule 05.05.2013
comment
Что вы имеете в виду, вызывает match? дважды для каждой сущности? Как вы можете сказать? Не могли бы вы предоставить некоторые примеры данных (например, коллекции Clojure)?   -  person noahlz    schedule 06.05.2013
comment
Кроме того, выражения регулярных выражений Clojure не требуют экранирования обратной косой черты. Мне пришлось изменить его на #"so (\d+)"   -  person noahlz    schedule 08.05.2013


Ответы (2)


Если вы беспокоитесь о производительности, используйте:

(->> (d/datoms (d/db conn) :aevt :user/regex-match)
 (filter #(user/match? (:v %)))
 (map :v))

который использует API datomic.api/datoms для потоковой передачи :user/regex-match значений атрибутов, соответствовать предикату, т.е. user/match?. Это гарантирует, что ваша предикатная функция выполняется только один раз (для каждого объекта). Обратите внимание, что вы можете заменить (map :v) на (map :e), чтобы вместо этого получить идентификаторы их сущностей.

Если вы действительно беспокоитесь о производительности и готовы использовать дополнительную память для ее достижения, используйте:

(def fast-match? (memoize match?))

(->> (d/datoms (d/db conn) :aevt :user/regex-match)
 (filter #(fast-match? (:v %)))
 (map :v))

При этом создается запоминаемая версия вашей функции. Эта версия имеет еще более сильные гарантии производительности, поскольку ваша предикатная функция будет выполняться не более одного раза (т. е. один раз для каждого отдельного значения) и может обеспечить исключительную производительность, если ваши значения атрибутов являются частью конечного набора.

Полный пример кода см. на странице https://gist.github.com/a2ndrade/5651065.

person a2ndrade    schedule 25.05.2013

Похоже, проблема в том, что ?m дает вам совпадения, а не сущность. Вы хотите что-то вроде следующего:

user=> (->> (d/q '[:find ?e ?m :in $ :where [?e :user/regex-match ?r]
                                            [(user/match? ?r) ?m]]
            [[1 :user/regex-match "so 111"]
             [2 :user/regex-match "so 222"]
             [3 :user/regex-match "blah 333"]])
          (filter #(seq (second %))))

([2 ["so 222" "222"]] [1 ["so 111" "111"]])

Обратите внимание, что я смоделировал базу данных, используя коллекции clojure.

Основываясь на этом выводе, у вашего регулярного выражения есть подвыражение, поэтому вы, вероятно, видите его «дважды для каждой сущности».

Интересно, сможете ли вы извлечь выгоду из функций Datomic filter?

person noahlz    schedule 08.05.2013