How to update a document using values from it?

I cannot seem to get into the FQL mindset as I’m trying to do very trivial tasks like updating a document field by using the current value of the field.

For example, a simple counter increment is very easy to do in SQL

UPDATE someTable 
SET someColumn = someColumn + 1
WHERE someId = 123

The documentation simply shows how to update a document with literal values.

Update(
  Select("ref",
    Get(
      Match(Index("dept_by_deptno"), 10)
    )
  ),
  {
    data: { loc: "AUSTIN" }
  }
)
Update(
  Select("ref",
    Get(
      Match(Index("dept_by_deptno"), 10)
    )
  ),
  {
    data: { counter: counter + 1 } // HOW?
  }
)

Ideally, I’d want to achieve this without having to retrieve the document in my application in one query and then submitting a second one to update it.

Actually, what I’m trying to do is not incrementing a counter, but appending a value to an array.

I have a users collection. One of the fields is called logins and contains an array of timestamps.

{
  "ref": Ref(Collection("users"), "274834080210616839"),
  "ts": 1598362322247000,
  "data": {
    "user_id": 123,
    "name": "John Doe",
    "email": "john@doe.com",
    "logins": [
      Time("2020-08-25T13:32:02.067015Z"),
      Time("2020-08-25T13:31:49.457671Z")
    ]
  }
}

I have an index by the user_id so that I can find users by that term.

I just simply want to:

Update(
  Select("ref",
    Get(
      Match(Index("users_by_id"), 123)
    )
  ),
  {
    data: { logins: Append(Now(), logins) } // HOW?
  }
)

How can reference the matched document in the second parameter of the Update function?

In this case I would get to know the Let function. To update your counter you would do something like this:

Let(
  {
    doc: Get(Match(Index("dept_by_deptno"), 10)),
  },
  Update(
    Select(["ref"], Var("doc"),
    {
      data: { 
        counter: Add(1, Select(["data", "counter"], Var("DOC")))
      }
    }
  )
)

You can also create multiple variables in let, and you have previously declared variables available in scope. Like so:

Let(
  {
    doc: Get(Match(Index("dept_by_deptno"), 10)),
    ref: Select(["ref"], Var("doc")),
    counter: Select(["data", "counter"], Var("doc")),
    updatedCounter: Add(1, Var("counter"))
  },
  Update(
    Var("ref"),
    { data: { counter: Var("updatedCounter") } }
  )
)
2 Likes

Thanks. That’s quite verbose for a simple increment though…

Agree :slight_smile: But once you start to think of FQL as a functional language you will hopefully appreciate it. What I do all the time is to create functions that generate FQL in my host language.

I have put many of the functions I commonly use here: https://github.com/shiftx/faunadb-fql-lib/tree/master/src/functions

1 Like

once you start to think of FQL as a functional language

I wonder if usability/clarity wouldn’t be improved if it were more like,

fLet(
  {
    doc: Get(Match(Index("dept_by_deptno"), 10)),
  },
  ({doc}) => fLet(
    {
      counter: Select(["data", "counter"], doc),
      ref: Select(["ref"], doc),
    },
    ({ref, counter}) => Update(
      ref,
      { data: { counter: counter + 1 } }
    )
  )
)

In particular, the dependency of one field in the Let bindings on another of the same seems potentially confusing - can reordering the bindings break it?