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.

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: .

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();