Dynamic Projection

I have been trying to consolidate my UDFs for simplicity but a limitation I am finding is that I cant seem to find how to pass a variable for a projection into a UDF.

For example, I want to be able to create a generic UDF where I can pass the collection name and the full projection I want to receive back. I have been able to pass the collection name and get results, but cant figure out how to pass a full projection dynamically.

function recordGetList(coll, proj) {
let collection = Collection(coll)
collection.all() {proj} ← Fauna thinks I want the field proj here
}

I did figure out how to pass a single field into a UDF (example below), but cant figure out how to pass in a full projection for example {id, orderid, name, date, customer {name, custid}}

Here is where I am able to pass the field for the projection using [myvar]

function maxNum(coll, myvar) {
Collection(coll)
.all()
.map(x => x[myvar])
.order(asc(v => v))
.toArray()
.filter((x) => {x != null && x != “”})
.last()
}

I also didnt find the above [myvar] in the documentation anywhere, it was a suggestion from an AI program. If it exists in your documentation, it is really hard to find. I do think the documentation is still a serious limitation for adoption of your platform.

Hi @gametb. Projections are not values. They are a syntactic element: sugar for a kind of transformation function. What you can do, though, is use a lambda function, which can be stored in a variable, or passed as an argument.

Lambda functions

// FSL

function recordGetList(coll_name, format_fn) {
  let collection = Collection(coll_name)
  collection.all().map(format_fn)
}
// FQL

recordGetList("Thing", x => x { id, ordered, /* ... */ })

// -- or if the function is another UDF --
recordGetList("Thing", formatThing)

Be careful about accepting arbitrary functions to be passed into your UDFs. You need to make sure that folks cannot perform arbitrary work.

// Some bad actor's code 😱

recordGetList("Thing", _ => {
  SomeCollectionIShouldNotHaveAccessTo.all()
})

Making snippets available to users by name

You can store snippets in a UDF that returns the function you need by name, then the caller cannot provide arbitrary code to your UDF

// FSL

function getFormatter(coll_name) {
  let formatterMap = {
    Thing: x => x { id, ts, .... },
    OtherThing: x => {
      /* some other arbitrary transformation */
    }
  }

  if (!Object.hasPath(coll_name)) {
    abort("No formatter named '#{coll_name}'")
  }

  formatterMap[coll_name]
}

function recordGetList(coll_name) {
  let collection = Collection(coll_name)
  // call a UDF that returns the desired formatting function
  let format_fn = getFormatter(coll_name)
  collection.all().map(format_fn)
}
// FQL

recordGetList("Thing")

Composition with the driver

You can define projection blocks as FQL snippets in client code and stitch queries together that way, too. It sounds like you want to do this within the UDF.

const THING_PROJECTION = fql`{
  id,
  ordered,
  name,
  date,
  customer { name, custid }
}`

const query = fql`MyColleciton.all() ${THING_PROJECTION}`