Query, Delete and Set in one operation

Is it possible to make an upsert with a delete in the middle?
Something like:

upsert {
  query {
    list as var(func: eq(xid, 'ID1'))
    item as var(func: eq(xid, 'ID2'))
  }

  mutation {
    delete {
      uid(list) <items> *  . 
    }
    set {
      uid(list) <items> uid(item)  . 
    }
  }
}

Doing this in a single operation may not have a well defined behaviour. Not a good idea, technically, it should not fail but final output is not guaranteed to be the same.

The reason I asked is that if I run the delete first and the upsert later, and the upsert fails, my data gets corrupted.

I understand dgraph does not include the concept of scalable “transactions” that can include multiple sequences of interspersed queries and mutations inside them and are run either-all-or-nothing. Is this correct?

We do have client side transactions. You can use a transaction to do the delete, followed by update and after that, commit the transaction. You’ll get exactly what you want. Let us know if you need more help.

1 Like

Thanks for the answer. I am using js-dgraph.

I am not sure how would I bunch the delete and the upsert transactions into one atomic safe “client side” transaction. Any guideline is useful actually :slight_smile: .

1 Like

Just adding this for reference, because I really struggled with it. This should work with dgraph-js, works with dgraph-js-http for me:

const SLUG = "a-slug";
const txn = dgraphClient.newTxn();

// Delete any existing reviews, and insert new ones from CSV
const importReviews = {
  query: `{
        var(func: type(BlogPost)) @filter(eq(BlogPost.slug, \"${SLUG}\")) {
          ba as BlogPost.author {
            Author.reviews {
              r as uid
            }
          }
        }
      }`,
  delete: [
    // Delete associated predicates for all reviews
    // Delete edge from review -> author
    {
      "uid": "uid(r)",
      "Review.title": null,
      "Review.content": null,
      "Review.numStars": null,
      "Review.author": { "uid": "uid(ba)" }
    },
    // Delete edge from author -> review
    {
      "uid": "uid(ba)",
      "Author.reviews": { "uid": "uid(r)" }
    },
    // Finally, delete the now empty review nodes
    {
      "uid": "uid(r)"
    }
  ],
  set: [
    {
      "uid": "uid(ba)",
      "Author.hasReviews": true
    }
  ]
}

const csv = fs.readStream(process.env.CSV_PATH) // pseudo code

csv.forEach((row, index) => {
  importReviews.set.push(
    {
      "uid": `_:newReview${index}`,
      "dgraph.type": "Review",
      "Review.content": `${row.review_content}`,
      "Review.numStars": `${row.review_number_of_stars}`,
      "Review.title": `${row.review_title}`,
      "Review.author": { "uid": "uid(ba)" }
    },
    {
      "uid": "uid(ba)",
      "Author.reviews": [{ "uid": `_:newReview{index}` }]
    }
  );
})

await txn.mutate({ mutation: JSON.stringify(importReviews) });
await txn.commit();


2 Likes