Om Next的查询 - > ast和ast->查询函数

时间:2016-02-27 21:38:48

标签: clojure clojurescript om

根据Om Next's documentation

  

查询 - > AST

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

给定查询表达式返回AST。

     

ast->查询

(om.next/ast->query ast)
     

给定查询表达式AST,将其解析为查询表达式。

问题:为什么需要这些功能?也就是说,为什么人们需要直接操作查询抽象语法树(我假设它是表示查询树的clojure映射,以及一些元数据)?

2 个答案:

答案 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)