Need help with pagination for a sorted index and UDL resolver

I have a collection of listings that I want to display in my app by the newest added first. I have created an index to sort them via their timestamp like so:

CreateIndex({
  name: "all_Listings_By_Newest",
  serialized: true,
  source: [Collection("Listings")],
  values: [
    {
      field: ["data", "_ts"],
      reverse: true
    },
    {
      field: ["ref"]
    }
  ]
})

This seems to work as intended.

Next, I try to create a UDF to access this index with pagination as per the example in the documentation.

CreateFunction({
  name: "newest_listings",
  body: Query(Lambda(["size", "after", "before"],
    Let(
      {
        match: Match(Index("all_Listings_By_Newest"), true),
        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("ref", Get(Var("ref"))))
    )
  ))
})

Next I try to access this through a resolver with my graphql schema

newestListing: [Listing!]  @resolver(name: "newest_listings", paginated: true)

I can then access the graphQL query in the graphQL playground. I don’t get any errors, but I also don’t get any results. The data array is empty and before and after are both null.

If I alter the UDF to be:

CreateFunction({
  name: "newest_listing",
  body: Query(Lambda(["size", "after", "before"],
    Let(
      {
        match: Match(Index("all_Listings_By_Newest"), true),
        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"))))
    )
  ))
})

I am able to access the data, but before and after continue to be null.

I’m not sure if I have gone wrong in how I created the index or how I’m writing the UDF. If anyone can help point me towards what I’m overlooking, it is greatly appreciated.

In your UDF, your Match statement seems incorrect:

match: Match(Index("all_Listings_By_Newest"), true),

true should be be removed because you have no terms defined.

Hi truehello,

First, welcome to our forums!

I recreated your functions in my own database, and created a simple Listing type to use with it:

type Listing {
 name: String!
 price: Float
 denomination: String
}

type Query {
   newestListing: [Listing!]  @resolver(name: "newest_listing", paginated: true)
}

The function is exactly what you shared, except that I removed the “, true” as it’s unneeded (as @ewan pointed out).

When I run the query on it in the GraphQL playground I see this:

(the query)

query {
  newestListing(_size: 1){
  	data
    {
      name
      price
      denomination
    }
    after
    before
  }
}

(the result)

{
  "data": {
    "newestListing": {
      "data": [
        {
          "name": "This is the first listing",
          "price": 5.25,
          "denomination": "$"
        }
      ],
      "after": "2DOD9tg0cjI5OTIxMzIzNzcyOTEwMDI5OIFoTGlzdGluZ3OBZ2NsYXNzZXOAgICA2DRyMjk5MjEzMjM3NzI5MTAwMjk4gWhMaXN0aW5nc4FnY2xhc3Nlc4CAgIA=",
      "before": null
    }
  }
}

Note that in the query I’m explicitly setting both after and before as elements to display (and also setting a _size of 1 because I only created two sample documents for this).

Double check that you’re setting after and before in the response for the query. Let me know if this solves the issue or if you’re still seeing any problems.

Thanks,
Cory

Also,

don’t confuse the fact that GraphQL presents the ref.id to you as data._id, and ts as data._ts.

That is, the Index value should be field: ["ts"], not field: ["data", "_ts"]

The latter field does not exist on the document, which will be a problem.

If you inspect a document, it looks like this:

{
  ref: Ref(Collection("Products"), "264289785763332628"),
  ts: 1620696631965000,  // <--  `ts` is here!!!
  data: {
    "name": "This is the first listing",
    "price": 5.25,
    "denomination": "$"
  }
}

Thanks to all for the responses. The Match expression was that last piece that I didn’t dig into what the true was for. However, after removing it, recreating the UDF and resolver, I get this erro message in the graphql playground:

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

Then, from Paul’s insight, I thought that perhaps my index was not created properly so I created a new index with the timestamp referenced accordingly. Updated the UDF and resolver, but still get the same error when trying to access it in the graphql playground:

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

What’s the query you’re running on the left side of the GraphQL playground window? Please share the entire query, to make sure nothing is missed.

Cory

1 Like
query latestListings{
  newestListing(_size:3){
    data{
      name
    }
    before
    after
  }
}

thanks

I’m still having trouble with this so I’ll share as actual information. Hopefully it is not counterproductive. I was trying to simplify things earlier, but I’ve gotten no further. First, here is the entire schema.gql


type Video @collection(name: "Video") {
  name: String!
  location: String!
  city: String
  country: String
  continent: String
  latitude: Int
  longitude: Int
  description: String
  image: String
  date: String
  video_url: String
  video_id: String
  video_host: String
  credit: String
  owner: User!
  createdAt: Date
  updatedAt: Date
}



type User {
  email: String! @unique
  videos: [Video!] @relation
  firstName: String
  lastName: String
  createdAt: Date
  updatedAt: Date
}

type Query {
  allVideos: [Video!]
  newestVideos: [Video!]  @resolver(name: "latestVideos", paginated: true)
  findVideosByCountry(country: String!): [Video!]
  findVideosByCity(city: String!): [Video!]
  findVideosByContinent(continent: String!): [Video!]
}

Then, I have a index for videos_newest where the FQL is this:

{
  name: "videos_newest",
  serialized: true,
  source: "Video",
  values: [
    {
      field: ["ts"],
      reverse: true
    },
    {
      field: ["ref"]
    }
  ]
}

Next, I have a function which I want to access with the resolver. The FQL is as follows:

{
  name: "latestVideos",
  role: null,
  body: Query(
    Lambda(
      ["size", "after", "before"],
      Let(
        {
          match: Match(Index("videos_newest")),
          page: If(
            Equals(Var("before"), null),
            If(
              Equals(Var("after"), null),
              Paginate(Var("match"), { size: Var("size") }),
              Paginate(Var("match"), { after: Var("after"), size: Var("size") })
            ),
            Paginate(Var("match"), { before: Var("before"), size: Var("size") })
          )
        },
        Map(Var("page"), Lambda("ref", Get(Var("ref"))))
      )
    )
  )
}

Then, in the graphql playground I attempt the following query:

query newestVids{
  newestVideos(_size:2){
    data{
      name
      location
    }
    before
    after
  }
}

and the result is the following error message:

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

Hopefully that was not too much information. Again, any insight is greatly appreciated. Thanks

The issue is here:

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

The index returns an array with 2 elements for each result. So the lambda needs to agree. You were on the right track with this:

Map(Var("page"), Lambda(['ts',"ref"], Get(Var("ref"))))

I uploaded all of what you shared (thank you for that), and made just that one change to the UDF make it work. I can create some videos and run your desired query

Perhaps there was a compounding issue with providing true to Match?

Praise be, it finally works. Thank you for all the assistance.

As a note to anyone that may come across this in the future, after updating the UDF I needed to update my schema again in the graphQL playground for it to take effect, even though it was exactly the same.

1 Like