Replace by path

Not sure I am not overlooking something, but is there an easy way to replace only the part of data? Ideally defined by a path.

It seems the only way is to do two queries, the first which sets something to null, and the second with actual data.

Do(
  Update(ref, { data: { state: null } }),
  Update(ref, {
    data: {
      state: {
        type: 'success',
      },
    },
  }),
);

You do not need two queries. Update should take care of it. Here is an example of order document where I add a new object address and then update a value in address.

Get(Ref(Collection("orders"), "262998629034230290"))

{
  ref: Ref(Collection("orders"), "262998629034230290"),
  ts: 1587073887770000,
  data: {
    client: "john",
    items: ["a", "b"],
    price: 10.5,
    status: "new"
  }
}

>> Time elapsed: 569ms

Update(Ref(Collection("orders"), "262998629034230290"), {data: { address: { street: "MLK Ave", City: "NY"}}})

{
  ref: Ref(Collection("orders"), "262998629034230290"),
  ts: 1591989255520000,
  data: {
    client: "john",
    items: ["a", "b"],
    price: 10.5,
    status: "new",
    address: {
      street: "MLK Ave",
      City: "NY"
    }
  }
}

>> Time elapsed: 452ms

Update(Ref(Collection("orders"), "262998629034230290"), {data: { address: {  City: "NJ"}}})

{
  ref: Ref(Collection("orders"), "262998629034230290"),
  ts: 1591989315850000,
  data: {
    client: "john",
    items: ["a", "b"],
    price: 10.5,
    status: "new",
    address: {
      street: "MLK Ave",
      City: "NJ"
    }
  }
}

>> Time elapsed: 315ms

Sorry I did not explain the issue properly. Yes, we can update part of the address, but what if we want to override address entirely, because it’s tagged tagged union / sum type? Imagine:

type Address = { type: 'short', street: string } | { type: 'long', street: string, city: string }

Changing type with Update means, a short type can have a city because it was not deleted.

You can use Merge to curate the object passed to Update.

Update(
  ref,
  {
    data: Merge(
      Select('data', Get(ref)),
      { state: { type: 'success' } }
    )
  }
)

faunadb-fql-lib from @eigilsagafos has a DeepMerge function.

You should be able to use like

Update(
  ref,
  DeepMerge(
    Get(ref),
    { data: { state: { type: 'success' } } } 
  )
)

I started working on a lens library for faunadb, based on shades, where it’s really easy to update a property deep in some object. That’s done by composing Merge down to the desired object.

I doubt you’ll want to refactor everything around this but I’ve enjoyed playing with it. You can use it like this:

set(getRef(), path('data', 'state'))({ type: 'success' })(ref)

Unfortunately no, this does the same thing as Merge. It overrides the state, not replace it.

I will rephrase it again. FQL lacks the method to replace (not merge) the part of data. Therefore, we have to set it to null and then set it to desiderated value.

Ok. I might not have gotten it right for your case.

Merge is a shallow operation, so it will work if you go down to the right level.

Can you provide a precise before and after data object. I can make and actually test the query against it. Previous code was just off the top of my head.

The same as in the first message. I need to replace the data state (property state of a data object). The state is a tagged union, it can have various props. When we want to set a new state, we want to purge everything that has been there.

Do(
  Update(ref, { data: { state: null } }),
  Update(ref, {
    data: {
      state: {
        type: 'success',
      },
    },
  }),
);
  1. It’s a bummer that I can no-longer edit my previous post to highlight what I did wrong :confused:

  2. @Daniel_Steigerwald I did see your original post. I asked about the rest of the structure of the data to make sure you and I were really looking at the same the data, and to understand the complexity of that data… I will make some assumptions.

  3. I definitely messed up my understanding of Merge. To anyone and everyone that I suggested that Update and/or Merge work like Object.assign, I apologize. It’s actually like lodash _.merge function.

  4. Remember that there is the Replace function, in addition to Update.

Ok. That means you still have some options, but will need to compare the complexity with what you are already doing. I am assuming that the document is much more complex, but don’t know without sharing more. The new example should get the point across though.

new option A: if by chance state is the only field under data then there is no added value to the extra object – Everything under state can be directly under data. Document with state wrapper and another without it would be isomorphic. You can use Replace every time instead of Update. My guess is not so easy! Just putting it out there, though! :nerd_face:

new option B: You can use Replace and reconstruct the rest of the document with Select. This might be okay if there is only one, maybe two fields other than state, but it would get out of hand quickly.

Let(
  {
    ref: Ref(Collection('Test'), '268352561226973696'),
    instance: Get(Var('ref'))
  },
  Replace(
    Var('ref'),
    {
      data: {
        state: {
          type: 'success'
        },
        field1: Select(['data', 'field1'], Var('instance')),
        field2: Select(['data', 'field2'], Var('instance')),
      }
    }
  )
)

new Option C: I’m out of ideas.

new Option D: Wait for Fauna to implement Assign function, to complement Merge? I’m out of good ideas anyway. :upside_down_face:

Or just keep on keepin’ on how you’re doing. Only functional difference is [1 Read + 1 Write] vs [2 Write] operations.

As for flatenize, I can’t. It will be the same problem. How to remove obsolete props? Also, we can not flatenize more tagged unions. Algebraic data types exist for a reason.

Two updates and waiting for a new FQL function. Thank you for the answers.

I would abstract it away for now in your own function @Daniel_Steigerwald (if that is an option, not sure if you are using a driver), a new function would probably do more or less the same.