Filter by timestamp with gql resolver

I am trying to fetch a collection filtered by timestamp using a gql resolver.

Therefore I created an index, which maps timestamp to ref(TransactionEvent)

export default {
  name: "all_transaction_events_by_ts",
  source: Collection("TransactionEvent"),
  values: [{ field: ["data", "timestamp"] }, { field: ["ref"] }],
}

Afterwards I tried to create and udf, which runs, but does not return any data.

export default {
  name: "allTransactionEventsByTs",
  body: Query(
    Lambda(
      ["timestamp", "size", "after", "before"],
      Let(
        {
          match: Range(
            Match(Index("all_transaction_events_by_ts")),
            Var("timestamp"),
            Now()
          ),
          page: If(
            Equals(Var("before"), null),
            If(
              Equals(Var("after"), null),
              Paginate(Var("match"), { size: Var("size") }),
              Paginate(Var("match"), { size: Var("size"), after: Var("after") })
            ),
            Paginate(Var("match"), { size: Var("size"), before: Var("before") })
          ),
        },
        Map(Select("data", Var("page")), Lambda(["ts", "ref"], Get(Var("ref"))))
      )
    )
  ),
}

However, running the query in fauna shell works:

Let(
  {
    match: Range(Match(Index("all_transaction_events_by_ts")), 0, Now()),
    page: Paginate(Var("match"), { size: 2 }),
  },
  Map(Select("data", Var("page")), Lambda(["ts", "ref"], Get(Var("ref"))))
)

returns

[
  {
    ref: Ref(Collection("TransactionEvent"), "336715287670819008"),
    ts: 1657375571990000,
    data: {
      timestamp: 1654735525113,
      transactionRevenue: 6,
      sessionCount: 3,
      customer: Ref(Collection("Customer"), "336715287580639424"),
      utmTerm: "67dc5354-8f0b-46b7-afcc-f375b5beebcb",
      offline: false,
      transactionId: "19021526"
    }
  },
  {
    ref: Ref(Collection("TransactionEvent"), "336715288339808455"),
    ts: 1657375572610000,
    data: {
      timestamp: 1654735525113,
      transactionRevenue: 6,
      sessionCount: 3,
      customer: Ref(Collection("Customer"), "336715288252776647"),
      utmTerm: "67dc5354-8f0b-46b7-afcc-f375b5beebcb",
      offline: false,
      transactionId: "79042313"
    }
  }
]

Here is the schema.gql entry

type Query {
  allTransactionEventsByTs(timestamp: Long!): [TransactionEvent!]
    @resolver(name: "allTransactionEventsByTs", paginated: true)
}

I’d highly appreciate any kind of help, since I am working on this for 2 days now.

PS: Using this line at the end of the UDF as written in the docs

Map(Var("page"), Lambda("ref", Get(Var("ref"))))

results in the following error

{
  "errors": [
    {
      "message": "Ref or Set expected, Array provided.\n",
      "extensions": {
        "code": "invalid argument"
      }
    }
  ]
}

Finally I got it working.
Since I have not found any guide or forum post, which explains the issue. I am willing to share what I figured out for future readers.

Here are the working index, gql and udf schema with explaination.

index

export default {
  name: "all_transaction_events_by_ts",
  source: Collection("TransactionEvent"),
  values: [{ field: ["data", "timestamp"] }, { field: ["ref"] }],
}

Note that the index does not use terms, which is usually meant to search for fields.
We use values in that case, for we need the index to return them to the Range() function in the udf.

gql schema:

type Query {
  allTransactionEventsByTs(timestamp: Long!): [TransactionEvent!]
    @resolver(name: "allTransactionEventsByTs", paginated: true)
}

In the schema we use the usual @resolver with the option paginated: true, which enables you to pass size, before and after to the udf and paginate the returned set of indexes. An additional input parameter can be passed. We pass timestamp, which is of type number.

udf

export default {
  name: "allTransactionEventsByTs",
  body: Query(
    Lambda(
      ["timestamp", "size", "after", "before"],
      Let(
        {
          match: Range(
            Match(Index("all_transaction_events_by_ts")),
            Var("timestamp"),
            Now()
          ),
          page: If(
            Equals(Var("before"), null),
            If(
              Equals(Var("after"), null),
              Paginate(Var("match"), { size: Var("size") }),
              Paginate(Var("match"), { size: Var("size"), after: Var("after") })
            ),
            Paginate(Var("match"), { size: Var("size"), before: Var("before") })
          ),
        },
        Map(Var("page"), Lambda(["ts", "ref"], Get(Var("ref"))))
      )
    )
  ),
}

First of all notice the lambda gets the 4 arguments passed from the query defined in the schema.

Here comes the first crucial part.
Let() lets you define variables. We need match and page
match is a set of entries returned by Range() which filter the created index from the passed timestamp until Now(). Range() needs the timestamp “result” to filter. That’s why we needed to define values in the index instead of terms!
Afterwards we can continue with the usual pagination and save the page to the variable page.

Now comes the second crucial part.
The page we are getting returned is not an usual array of Ref()'s. Instead the result is a tupel of [“ts”, “ref”]. The lambda will not be able to run if you just pass “ref”.

The result of Map() will be the desired page of the requested collection.

Example

{
  allTransactionEventsByTs(
    timestamp: 1657434425433
    _size: 2
    _cursor: "2DODGwAAAYHmykhZ2DRyMzM2ODAzMDY5MjUyNjAwMDAwgXBUcmFuc2FjdGlvbkV2ZW50gWtjb2xsZWN0aW9uc4CAgIDYNHIzMzY4MDMwNjkyNTI2MDAwMDCBcFRyYW5zYWN0aW9uRXZlbnSBa2NvbGxlY3Rpb25zgICAgA=="
  ) {
    data {
      _id
      timestamp
    }
    after
  }
}

Result

{
  "data": {
    "allTransactionEventsByTs": {
      "data": [
        {
          "_id": "336803069252600000",
          "timestamp": 1657434425433
        },
        {
          "_id": "336803079715292352",
          "timestamp": 1657434425433
        }
      ],
      "after": "2DODGwAAAYHmykhZ2DRyMzM2ODAzMDkwNTcyMjQ4MjY0gXBUcmFuc2FjdGlvbkV2ZW50gWtjb2xsZWN0aW9uc4CAgIDYNHIzMzY4MDMwOTA1NzIyNDgyNjSBcFRyYW5zYWN0aW9uRXZlbnSBa2NvbGxlY3Rpb25zgICAgA=="
    }
  }
}

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.