How do I loop over an input array using a UDF in FaunaDB?

407 Views Asked by At

I have a question about mapping an array of ID's (inputdata) and returning all related documents to those ID's. I have a UDF set up to retrieve the documents for a single ID and was hoping some tweaking would make that work. I can't seem to figure out how to map over the inputdata and create a variable (data:) to store the new array of documents. Any help is appreciated. Here is the single entry UDF which works:

Query(
  Lambda(
    ["inputdata"],
    Let(
      {
        data: Map(
          Paginate(
            Match(
              Index("certificate_by_dealer"),
              Ref(Collection("Dealers"), Select("dealer", Var("inputdata")))
            )
          ),
          Lambda(["ref"], Get(Var("ref")))
        )
      },
      Select(["data"], Var("data"))
    )
  )
)

Is there a simple...or any solution to make this work for an array of ID's as inputdata?

Call function is:

Call("user_dealers_all_certificates", {
  ids: [301393590798516736, 301393590798516749]
}

Unfortunately I get no results. (Adding quotes solved the issue)

Here is implementing the suggested UDF:

Query(
  Lambda(
    ["inputdata"],
    Let(
      { dataIds: Select("ids", Var("inputdata")) },
      Union(
        Map(
          Var("dataIds"),
          Lambda(
            ["id"],
            Select(
              ["data"],
              Paginate(
                Match(
                  Index("certificate_by_dealer"),
                  Ref(Collection("Dealers"), Var("id"))
                )
              )
            )
          )
        )
      )
    )
  )
)

Adding quotes created a proper response:

Call("user_dealers_all_certificates", {ids: ["302122229239382536", "301394099049595400"]})

[
  Ref(Collection("Certificate"), "302122488174739977"),
  Ref(Collection("Certificate"), "302120872550859273")
]

However the GraphQL query returns bad data:

query {
  allUserDealersCertificate(data: {ids: ["302122229239382536", "301394099049595400"]}){
   data {
_id
  }
}
}

response:

{
  "errors": [
    {
      "message": "Lambda expects an array with 1 elements. Array contains 4.",
      "extensions": {
        "code": "invalid argument"
      }
    }
  ]
}

GraphQL error without paginated: true in schema:

{
  "data": {
    "allUserDealersCertificate": [
      null,
      null
    ]
  },
  "errors": [
    {
      "message": "Cannot return null for non-nullable type (line 3, column 5):\n    _id\n    ^",
      "path": [
        "allUserDealersCertificate",
        0,
        "_id"
      ],
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ]
    },
    {
      "message": "Cannot return null for non-nullable type (line 3, column 5):\n    _id\n    ^",
      "path": [
        "allUserDealersCertificate",
        1,
        "_id"
      ],
      "locations": [
        {
          "line": 3,
          "column": 5
        }
      ]
    }
  ]
}
1

There are 1 best solutions below

0
eskwayrd On

Based on the query you provided, I feel the need to point out that the Match function performs exact matches. It does not (and cannot) unroll a structured array for you. Neither can the Ref function.

You'd need to call Map on the inputdata, and get results for each id. Then you can Union those results together into a single list.

I don't know the exact shape of the data that you're dealing with, so here's a query that works with the pre-populated data available in the Dashboard:

Let(
  {
    // the pre-populated data has 3 stores, with ids 301, 302, and 303
    // here, we want the products in stores 301 and 302
    ids: [ 301, 302 ]
  },
  // this is where we combine all of the results
  Union(
    Map(
      // here is where we loop over the list of ids
      Var("ids"),
      Lambda(
        // for each id, run this function's expression
        "id",
        Select(
          // each Paginate call returns a page of results with its own data
          // field, so we have to select those out
          "data",
          Paginate(
            Match(
              Index("products_by_store"),
              // here we compose a reference to a specific store, using the
              // Lambda function's current id
              Ref(Collection("stores"), Var("id"))
            )
          )
        )
      )
    )
  )
)

Npte that I've used Let to simulate passing an array to the body of a UDF. When you run this query, the result should be:

[
  ["avocados", "Conventional Hass, 4ct bag", 3.99],
  ["cilantro", "Organic, 1 bunch", 1.49],
  ["limes", "Conventional, 1 ct", 0.35],
  ["limes", "Organic, 16 oz bag", 3.49],
  ["cups", "Translucent 9 Oz, 100 ct", 6.98],
  ["pinata", "Giant Taco Pinata", 23.99],
  ["pinata", "Original Classic Donkey Pinata", 24.99]
]