How to replace a list in a mutation using Dgraph Cloud

I am using the pre-built mutations and finding the set part of the mutation isn’t doing what I would expect it to.

For example I have the mutation:

  mutation EditEntryMutation(
    $title: String!
    $genres: [Genre]
  ) {
    updateEntry(
      input: {
        filter: { title: { eq: $title } }
        set: {
          genres: $genres
        }
      }
    ) {
      entry {
        genres
      }
    }
  }

I would expect that this operation replaces the list of genres with the new list, however it appends whatever values missing.

For example:

In my database I have the values genres: ["action", "drama"].

  • If I run this mutation with the values ["action", "drama", "adventure"], the database will now have the values ["action", "drama", "adventure"].
  • If I run this mutation with the values ["action"], the database will continue to have the values ["action", "drama"].
  • If I run this mutation with the values ["romance", "fantasy"], the database will now have the values ["action", "drama", "romance", "fantasy"].
  • If I run this mutation with and empty array [], the database will continue to have the values ["action", "drama"].

How can I do a mutation where it replaces the values rather then appending?

1 Like

First do a get query for that respective array, then do a mutation somewhat as follows →

mutation MyMutation($array: [Int!]!, $array1: [Int!]! ) {
  updatearrayStore(input: {filter: {name: {eq: "anArray"}}, set: {array: $array}, remove: {array: $array1}}) {
    arrayStore {
      array
      name
    }
  }
}

I am not sure I follow… are you able to change my mutation in my example to achieve this goal? For more context I have a form where a user can edit a list of values. It would be a lot of additional code to have to work out which values were removed and run a separate mutation just for this. There is also a number of lists in this form, hence why I am looking to do everything in one mutation.

Is there no way to have this mutation to a simple replace?

Is this what you mean?

mutation EditEntryMutation(
    $title: String!
    $genresToAdd: [Genre]
    $genresToRemove: [Genre]
  ) {
    updateEntry(
      input: {
        filter: { title: { eq: $title } }
        set: {
          genres: $genresToAdd
        }
        remove {
          genres: $genresToRemove
        }
      }
    ) {
      entry {
        genres
      }
    }
  }

This does work however feels like this is something the mutation should handle. Is there another mutation property I should be using instead for a simple replace?

1 Like

Yep, this will work for now. I think a ticket should be created for allowing creating a new array instead of setting values to an existing array. @hardik can look into this and see where it can fit in the roadmap

1 Like

Here is an alternative method which involves two mutation blocks inside a single mutation operation:

mutation EditEntryMutation(
    $title: String!
    $genres: [Genre]
  ) {
    clean: updateEntry(input: {
      filter: { title: { eq: $title } }
      remove: {
        genres: null # removes all edges
      }
    }) { numUid }
    updateEntry(
      input: {
        filter: { title: { eq: $title } }
        set: {
          genres: $genres
        }
      }
    ) {
      entry {
        genres
      }
    }
  }
3 Likes

Are there any updates on this? I have found an awkward situation I have run into a few times.

It is when I am wanting to remove/update a nested array in my type. I am wanting to pass in an array of IDs, but instead I have to pass in an array of TypeRef, which implies I have to have all the data for that type.

Is there a way around this? The below is what I need to pass in at the moment.

 mutation EditEntryMutation(
    $myKey: String
    $seoKeywordsToAdd: [KeywordRef!]
    $seoKeywordsToRemove: [KeywordRef!]
  ) {
    updateEntry(
      input: {
        filter: { myKey: { eq: $myKey } }
        set: {
          seo: { keywords: $seoKeywordsToAdd }
        }
        remove: {
          seo: { keywords: $seoKeywordsToRemove }
        }
      }
    ) {
      entry {
        urlKey
      }
    }
  }

But this is what I would like to pass in:

 mutation EditEntryMutation(
    $myKey: String
    $seoKeywordsToAdd: [ID!]
    $seoKeywordsToRemove: [ID!]
  ) {
    updateEntry(
      input: {
        filter: { myKey: { eq: $myKey } }
        set: {
          seo: { keywords: $seoKeywordsToAdd }
        }
        remove: {
          seo: { keywords: $seoKeywordsToRemove }
        }
      }
    ) {
      entry {
        urlKey
      }
    }
  }
1 Like

@hardik are there any updates on this?

1 Like

@hardik are there any updates on this?

1 Like

@minhaj can you also see if this is anywhere on the accepted road map of features. I just recategorized it to the correct GraphQL issues.

UPDATE:

@charklewis - What you’re really asking is for the ability to pass in nested types. I think this should be a separate feature request, but may be fixed when nested filters are available one day.

Note: If you know the SEO ID (I imagine you do since you know the previous keywords), I believe you can avoid the other hassle by just updating the SEO node directly.

You can also avoid all types by using my easy-dgraph package.

Original Post


While we wait on an answer, I have some thoughts…

Since this is a foreign key problem, it is in relation to my @reference directive request, and perhaps should be added at the same time.

The difference, is that we are just dealing with the foreign keys, not the values themselves. We basically want to NOT merge the foreign keys on update.

I suggest this could be done in two ways:

1.) Going well with my other @reference suggestion:

Type Foo {
  id: ID!
  name: Bar @reference(merge: false)
  ...
}

2.) Or add a new setAll command to update all references…

mutation EditEntryMutation(
    $myKey: String
    $bar: Bar
  ) {
    updateEntry(
      input: {
        filter: { myKey: { eq: $myKey } }
        setAll: {
          bar: $bar
        }
      }
    ) {
      entry {
        urlKey
      }
    }
  }

J

IMO, this is the way.

@naman, do you think this might be a easy API enhancement?

Right now it takes quite a bit of logic on the UI to control the state to know the list of current children and then calculate which ones to add and remove when the user is presented with a list and checkboxes. So I have to keep up with 4 different state sets. The saved set from the last db query, the current active set for checkboxes selected, the set to add, and the set to remove. This gets even more problematic when I build my 4 different states of data off from the first one, but a user might send a second mutation before my original query updates to get the now active set so the ones that are “removed” and “added” in this second mutation are now wrong according to what is stored in the database. It would be a whole lot easier if I could just send the list of references that I want it to be exactly.

1 Like

@Naman @minhaj do you have any updates with this?

@naman do you have any updates?

1 Like

Hi @charklewis , sorry this feature is not in the current roadmap.

Ah dang… is there any chance to add this?

1 Like

FWI, you could probably emulate this feature using a lambda hook to delete everything not in the add.

J