查询 - > AST
(om.next/query->ast '[(:foo {:bar 1})])
给定查询表达式返回AST。
ast->查询
(om.next/ast->query ast)
给定查询表达式AST,将其解析为查询表达式。
问题:为什么需要这些功能?也就是说,为什么人们需要直接操作查询抽象语法树(我假设它是表示查询树的clojure映射,以及一些元数据)?
答案 0 :(得分:7)
在某些情况下,您需要直接操作查询。在远程解析模式下,解析器期望您的读取函数返回{:remote-name true}或(可能已修改){:remote-name AST-node}(其中包含:env中的ast)。大多数情况下,您必须修改AST以对其进行重组或添加一些数据。
示例1:
您有一个问题:[{:widget {:list [:name :created]}}]
:widget
部分与纯UI相关,您的服务器不需要知道它存在,它只关心/了解: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
,则会从{ast}中选择:query-root
,并将查询从[{: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
以更新给定ID
大多数情况下,您不会直接使用您在问题中描述的功能。解析器的每个方法中都可用的env
中包含ast
。
答案 1 :(得分:2)
我在试图使用Compassus的时候偶然发现了一些事情:
假设您有一个包含参数子查询的复杂联合/连接查询。像这样:
`[({:foo/info
{:foo/header [:foo-id :name]
:foo/details [:id :description :title]}} {:foo-id ~'?foo-id
:foo-desc ~'?foo-desc})]
现在假设您要在服务器上设置参数,您可以使用om / parser解析它,并将这些参数视为read
dispatch的第三个参数。当然,可以编写一个函数,在查询中找到所有必要的参数并设置值。但这并不容易,正如我所说的 - 想象你的查询可能非常复杂。
所以你可以做的是修改ast,ast包括:children :params
键。因此,我们假设:foo-id
和:foo-desc
的实际值位于:route-params
键下的state atom中:
(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'}))
所以基本上你是:
- 抓住了
- 用实际值修改它
你想也许你可以把它发送到服务器吧。唉,不!还没。事情是 - 当你做{:remote ast}
时,Om接受:query
部分的ast,组成它然后将它发送到服务器。因此,您实际需要:将修改后的ast转换为查询,然后再将其转换回ast。
注意:
set-ast-params
函数仅适用于第一级(如果您有嵌套的参数化查询 - 它将无效),
让它递归 - 这并不困难
有两种不同的方式可以转向查询,反之亦然:
(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)