Using the Object.values() and flatMap() functions in v10 UDFs

I am attempting to incorporate

I have a v10 function that is composed as follows:

@role(admin)
function findAllAllergens() {
RawIngredient.allAllergens().pageSize(20) {
    allergens,potential_allergens
  }
}

It returns the following:

data: [
    {
      allergens: ["foo","bar"],
      potential_allergens: []
    },
    {
      allergens: [],
      potential_allergens: ["loco"]
    },
....
]

How would I use either the Object.values() and flatMap() functions to flatten the returned object to an array?

I don’t quite understand what you are trying to do? Given the example results you shared, how do you want the result to look instead?

You can use fold or reduce methods to combine each of the items in the array into a single object with allergens and potential_allergens or return a single array with all of those combined.

That would likely need neither Object.values nor flatMap, but again, it’s unclear what final result you actually want. Please make sure you provide enough information so folks can understand what you are trying to solve, not just piece of your solution you are working on. https://xyproblem.info/

Thanks @ptpaterson,

I can see the lack of definition in my request. I would like the results to be in the form:

data: [
    "foo",
    "bar",
    "loco",
    ...
]

To elaborate further, I am really struggling to understand how to use the functions referenced in the context of a UDF so if you could show me how the fold, Flatmap or ‘Object.values’ could be used to augment the UDF that would be really helpful.

To transform an array of objects like this:

[
    {
      allergens: ["foo","bar"],
      potential_allergens: []
    },
    {
      allergens: [],
      potential_allergens: ["loco"]
    },
    // ...
]

to a single array like this:

[
    "foo",
    "bar",
    "loco",
    ...
]

You can use the FQL fold method.

If you are familiar with Javascript, FQL’s fold method functions the same as Javascript’s reduce method without a seed value. FQL provides the separate fold method which works like Javascript’s reduce method with a seed value.

You might try something like this:

RawIngredient.allAllergens()
  .fold([], (acc, val) => acc.concat(val.allergens).concat(val.potential_allergens))
  .distinct()

Note that reduce and fold are “greedy” methods. They will attempt to read the ENTIRE set before computing a result. This makes it impractical to use with large and/or unbounded sets.

Fauna is optimized for operational workloads (it’s an OLTP database), not analytics (Not an OLAP database). If you need to scale the Allergens collection and efficiently fetch the list of all distinct values, then you may need to consider denormalizing or precomputing and saving the list, updating it as changes are made.

As the dataset grows to large for Fauna to compute within a single query, you can paginate through all of the documents, loading the data locally in the client, and calculating the distinct list of values there.

Thanks @ptpaterson. The solution proposed provides the output intended. To work on avoiding ‘greedy’ methods, can you confirm that flatMap() and Object.values() cannot be used to achieve the same result (potentially more efficiently)?

The denormalization and pre-computation step is planned as the database input evolves.

You are right. flatmap is actually a cleaner way to perform the operation.

Regarding greediness, the problem is that you want distinct values, so you will need to scan the whole collection because of that.

But otherwise, yes, you could use flatmap.

RawIngredient.allAllergens()
  .flatMap(a => 
    a.allergens.concat(a.potential_allergens)
      .toSet() // .toSet() is required because the function arguments for `<Set>.flatMap()` must also return a `Set`.
  )
  .distinct()

You can paginate lazily, but you’ll have to de-duplicate locally

Calling .distinct() makes the call greedy and will scan the whole Set