Update via XTQL: quote `:update` vector

Suppose we interact with an XTDB v2 server over HTTP using the following:

;; Require `xtdb.api` as `xt`, `xtdb.client` as `xtc`

(def http-node-url "http://localhost:3000") ; as example

(def submit
  (with-open [node (xtc/start-client http-node-url)]
    (partial xt/submit-tx node)))

(def q 
  (with-open [node (xtc/start-client http-node-url)]
    (partial xt/q node)))

A document can be created:

(submit [[:put-docs {:into :user}
            {:xt/id   123
             :version 1
             :email   "foo@bar.com"}]])

And can be retrieved:

(q '(from :user [*]))
;; Returns `[{:version 1, :email "foo@bar.com", :xt/id 123}]

Next, I tried to update the document using the example code from the docs:

(submit [[:update {:table :user
                     :bind  [{:email $email}, version],
                     :set   {:version (+ version 1)}}
            {:email "foo@bar.com"}]])

This returns an error: “Unable to resolve symbol: $email in this context”

Unlike :put-docs, :update should be quoted:

(submit ['[:update {:table :user
                      :bind  [{:email $email}, version],
                      :set   {:version (+ version 1)}}
             {:email "foo@bar.com"}]])

Upon querying the :user table once more, the update is confirmed successful:

(q '(from :user [*]))
; Returns `[{:version 2, :email "foo@bar.com", :xt/id 123}]`

Clj-kondo complained about the $email and version symbols, at which point I figured the :update vector should be quoted.

Not sure if the docs should be updated. Just a bit of feedback.

1 Like

I’m a bit surprised those submit and q functions work, given that the node will be closed before you even use them…

I can only assume that (.close node) doesn’t do anything – and that you’ve started two clients, one for submit and one for q?

Yeah, that’s kind of a combination of this from Learn XTQL Today:

(def q (partial xt/q my-node))

And this from the docs:

(with-open [node (xtc/start-client "http://localhost:3000")]
  (xt/status node)

Maybe this would be better:

(defn submit [ops & opts]
  (with-open [node (xtc/start-client http-node-url)]
    (xt/submit-tx node ops opts)))

(defn q [query & opts]
  (with-open [node (xtc/start-client http-node-url)]
    (xt/q node query opts)))
1 Like

Thanks for mentioning this, agreed it looks like we missed a quote in that example. One of our tasks before any GA release is to make sure all snippets in the docs are fully tested :slight_smile:

1 Like

The only way I managed to make :update work was the following:

(xt/submit-tx node [[:update {:table :todos,
                                :bind [{:xt/id id}],
                                :set (remove-nil-values todo)}]])

Not even quoting worked for my case.

I made a CRUD application only using XTQL: todo-api/src/api/service.clj at main · varugasu/todo-api · GitHub

1 Like

Hey @varugasu thanks for sharing your experiences (and code!) - what was happening before you thought to add remove-nil-values there?

Hey @refset. The problem was actually passing the id as a parameter to the :update.

I tried the following:

(defn update-todo [node id]
  (xt/submit-tx node ['[:update {:table :todos,
                                :bind [{:xt/id $id}],
                                :set {:completed true}}
                        {:id id}]]))

This doesn’t work because {:id id} is inside of the '[:update ...]

I also tried the following, but no luck:

(defn update-todo [node id]
  (xt/submit-tx node ['[:update {:table :todos,
                                :bind [{:xt/id $id}],
                                :set {:completed true}}]]
                {:args {:id id}}))

The only way I could pass variables to the XTQL query was directly passing the variables without the quote:

(defn update-todo [node id completed]
  (xt/submit-tx node [[:update {:table :todos,
                                :bind [{:xt/id id}],
                                :set {:completed completed}}]]))

(update-todo node "d57087f9-a23f-4e0c-bc9b-1e166dcf75c1" true)

This way I have no problem passing id and set completed = true

Ah, so I think it may be sufficient to only move the quote here (based on the first snippet in that comment):

(defn update-todo [node id]
  (xt/submit-tx node [[:update '{:table :todos,
                                 :bind [{:xt/id $id}],
                                 :set {:completed true}}
                        {:id id}]]))