Функции query-›ast и ast-›query Om Next

Согласно документации Om Next:

запрос-›аст

(om.next/query->ast '[(:foo {:bar 1})])

Учитывая выражение запроса, возвращайте AST.

ast-›запрос

(om.next/ast->query ast)

Получив выражение запроса AST, разберите его на выражение запроса.

Вопрос: Зачем нужны эти функции? То есть, зачем нужно напрямую манипулировать абстрактным синтаксическим деревом запроса (которое, как я предполагаю, представляет собой карты clojure, представляющие дерево запроса вместе с некоторыми метаданными) в om next?


person George    schedule 27.02.2016    source источник


Ответы (2)


Есть несколько сценариев, в которых вам нужно напрямую манипулировать запросом ast. В режиме удаленного синтаксического анализа синтаксический анализатор ожидает, что ваши функции чтения вернут либо {:remote-name true}, либо (возможно, модифицированный) {:remote-name AST-node} (который в env отображается как :ast). Чаще всего вам придется изменить AST, чтобы реструктурировать его или добавить некоторые данные.

Пример 1: У вас есть запрос: [{:widget {:list [:name :created]}}] Часть :widget связана исключительно с пользовательским интерфейсом, вашему серверу не нужно знать о его существовании, он заботится/знает только о :list. В основном вам придется изменить AST в парсере:

(defmethod read :list
  [{:keys [ast query state]} key _ ]
  (let [st @state]
    {:value (om/db->tree query (get st key) st)
     :remote (assoc ast :query-root true)}))

Если вы используете om/process-roots в своей функции отправки, она возьмет :query-root из ast и перепишет запрос с [{:widget {:list [:name :created]}}] на [{:list [:name :created]}].

Пример 2. Другой пример: вы хотите изменить что-то удаленно:

(defmethod mutate 'item/update
  [{:keys [state ast]} key {:keys [id title]}]
  {:remote (assoc ast :params {:data {:id id :title title })})

Здесь вам нужно явно указать Om включить данные, которые вы хотите отправить в AST. Затем на своем пульте вы выбираете :data, чтобы обновить заголовок с заданным идентификатором.

В большинстве случаев вы не будете напрямую использовать функции, которые вы описали в своих вопросах. env, доступный в каждом методе парсера, содержит в себе ast.

person Seneca    schedule 28.02.2016
comment
Просто примечание: вы никогда не использовали ни одну из функций, о которых просит Джордж :) - person ClojureMostly; 28.02.2016
comment
@Andre: этот ответ все еще был чрезвычайно информативным! Кроме того, по его словам, большую часть времени эти функции не будут использоваться на практике. - person George; 29.02.2016
comment
Должен ли ключ :remote в функции чтения всегда иметь запрос ast в качестве значения? Или он может просто вернуть необработанный запрос (то есть что-то вроде form[ ... ] с использованием синтаксиса Datomic pull)? - person George; 29.02.2016
comment
Это либо ast, либо просто логическое значение true, чтобы пометить его для определенного пульта. В случае ast это должен быть ast, а не запрос. Однако вы можете использовать (om/query->ast), если хотите изменить запрос вместо ast напрямую, прежде чем отправлять этот ast. Просто имейте в виду, что конечный результат должен быть действительным ast. Как только вы освоитесь, модифицировать ast из env будет так же, как пить воду или дышать воздухом;) - person Seneca; 29.02.2016

Что-то, на что я наткнулся, пытаясь использовать Compassus:

Допустим, у вас есть сложный запрос на объединение/объединение, который включает параметрические подзапросы. Что-то вроде этого:

`[({:foo/info
        {:foo/header [:foo-id :name]
        :foo/details [:id :description :title]}} {:foo-id ~'?foo-id
                                                  :foo-desc ~'?foo-desc})]

Теперь предположим, что вы хотите установить параметры, чтобы на сервере вы могли проанализировать их с помощью om/parser и увидеть эти параметры как 3-й аргумент отправки read. Конечно, можно написать функцию, которая найдет в запросе все необходимые параметры и установит значения. Однако это непросто, и, как я уже сказал, представьте, что ваши запросы могут быть довольно сложными.

Итак, что вы можете сделать, так это модифицировать ast, так как ast включает в себя ключ :children :params. Предположим, что фактические значения для :foo-id и :foo-desc находятся в атоме состояния под ключом :route-params:

(defn set-ast-params [children params]
  "traverses given vector of `children' in an AST and sets `params`"
  (mapv
    (fn [c]
      (let [ks (clojure.set/intersection (-> params keys set) 
                                         (-> c :params keys set))]
        (update-in c [:params] #(merge % (select-keys params (vec ks))))))
    children))

(defmethod readf :foo/info
  [{:keys [state query ast] :as env} k params]
  (let [{:keys [route-params] :as st} @state
        ast' (-> ast 
                 (update :children #(set-ast-params % route-params))
                 om/ast->query
                 om.next.impl.parser/expr->ast)]
    {:value  (get st k)
    :remote ast'}))

Итак, в основном вы: - захватываете ast - изменяете его с фактическими значениями, которые, как вы думаете, возможно, вы можете отправить их на сервер прямо сейчас. Увы, нет! Еще нет. Дело в том, что когда вы делаете {:remote ast}, Ом берет :query часть аста, составляет из него аст и потом отправляет на сервер. Итак, вам действительно нужно: превратить ваш измененный ast в запрос, а затем снова преобразовать его обратно в ast.

Примечания:

  • set-ast-params в этом примере будет работать только для первого уровня (если у вас есть вложенные параметризованные запросы - не будет работать), сделайте рекурсивным - это не сложно

  • есть два разных способа превратить ast в запрос и наоборот:

    (om/ast->query) ;; retrieves query from ast and sets the params based 
                    ;; of `:params` key of the ast, BUT. it modifies the query, 
                    ;; if you have a join query it takes only the first item in it. e.g. :
    [({:foo/foo [:id]
        :bar/bar [:id]} {:id ~'?id})]
    ;; will lose its `:bar` part 
    
    (om.next.impl.parser/ast->expr) ;; retrieves query from an ast, 
                                    ;; but doesn't set query params based on `:params` keys of the ast.
    
    ;; there are also
    (om/query->ast) ;; and
    (om.next.impl.parser/expr->ast)
    
person iLemming    schedule 22.10.2016