Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pipe function: transform(select, ...conditions) Arbitrarily replace objects with values or projections #25

Open
xiata opened this issue Jun 11, 2020 · 3 comments
Labels
enhancement New feature or request

Comments

@xiata
Copy link

xiata commented Jun 11, 2020

While object projections are useful, they are not easily done with deeply nested objects. You usually are forced into one of the three camps:

  1. The uber-query. You build conditional projections for every single type at every possible injection point. This usually results in a massive query that is near impossible to maintain, if it is at all possible to cover every point. Or you just give up after a certain level of nesting which may be very problematic in front ends.
  2. Post processing. You accept the limitations of groq and instead build an object traversal function that typically involves dereferencing those deeply nested references, which requires N+1 requests depending on how many times your references involve references. You also write transform functions to make things like @sanity/color-input's color type a simple rgba(r,g,b,a) string instead of all the values you aren't going to use.
  3. Do none of the above. You write out code to handle large responses and ignore meta data you don't care about because it's tedious. It's not great, you could be working with more concise data structures, but it's not worth your development time.

I propose a new pipe function to transform arbitrarily nested objects: transform(selector:string|string[], ...conditionals:conditional[])

An example query:

*[_type == "post"]
  | transform(
    // A path or an array of paths to select objects from. In this case, anywhere
    "**",

    // Each part executed in waterfall manner so we can, e.g. deference all refs 3 levels deep before applying additional transforms
    _type == "reference" => deref(3),

    // Use this as a way of replacing objects with things like strings to be easier consumed by frontends
    _type == "color" => format('rgba(#{r},#{g},#{b},#{a})', rgb),

    // And projections which before would have had to known exact paths
    _type == "image" => { "url": asset->url, "id": asset._ref }
  )

Each conditional should be executed in waterfall manner for all objects selected by selector(s) should be processed, in case a conditional returns an object that can be transformed by a later conditional.

The above query should be equivalent to:

*[_type == "post"]
  | transform(
    "**",
    _type == "reference" => deref(3)
  )
  | transform(
    "**",
    _type == "color" => format('rgba(#{r},#{g},#{b},#{a})', rgb)
  )
  | transform(
    "**",
    _type == "image" => { "url": asset->url, "id": asset._ref }
  )

Note that I have also mentioned two potential functions as well in here that may have been mentioned before:

  • deref(level:number): Dereferences the current object up to the given level times. I like Auto dereferencing pipe function #21's thinking.
  • format(pattern:string, value:object): If value is null, return null, otherwise, replace tokens (type: path) in pattern by navigating value with token. Whichever token format wanted (e.g. {path.subpath}, #{path.subpath}, ${path.subpath}) is up to you
@judofyr judofyr added the enhancement New feature or request label Oct 12, 2020
@judofyr
Copy link
Contributor

judofyr commented Jun 24, 2021

We've been discussing this briefly and one idea we came up with was the concept of "projection functions":

*[_type == "post"]{
  ...,
  // Transform based on a path:
  transform(users[].name => firstName + " " + lastName),
  // Transform with a recursive search:
  transform(anywhere(_type == "color") => format('rgba(#{r},#{g},#{b},#{a})', rgb)
}

@vin-ni
Copy link

vin-ni commented Oct 20, 2021

How to push it? :)
Been writing uber-queries for months

@RavenHursT
Copy link

Oh boy.. Year and a half later and still no traction on finding something on par w/ GraphQL's raw: true?

Just gonna reiterate what I said here: #21 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants