Upsert should always return the uid

Moved from GitHub dgraph-js/73

Posted by pepoospina:

Experience Report

What you wanted to do

I am using an upsert operation as described in dgraph-js to get or create a new node with a given external id called did and return its uid.

What you actually did

This is my code. It works the first time the node with the external id did does not exist. But it fails if it does exist.

    const mu = new dgraph.Mutation();
    const req = new dgraph.Request();

    let query = `profile as var(func: eq(did, "${did}"))`;
  
    req.setQuery(`query{${query}}`);

    let nquads = `uid(profile) <did> "${did}" .`;
    nquads = nquads.concat(`\nuid(profile) <dgraph.type> "${PROFILE_SCHEMA_NAME}" .`);

    mu.setSetNquads(nquads);
    req.setMutationsList([mu]);
    req.setCommitNow(true);

    let response = await this.client.newTxn().doRequest(req);
    let uid = response.getUidsMap().get("uid(profile)")
    return uid;

Why that wasn’t great, with examples

I will have to add new logic to my function to consider the case in which the result is empty and run a new query to get the uid of that did.

I don’t like how that if looks inside my function. Is that a code smell? Should the upsert return the uid even if it did not create it?

This is my updated function

    const mu = new dgraph.Mutation();
    const req = new dgraph.Request();

    let query = `profile as var(func: eq(did, "${did}"))`;
  
    req.setQuery(`query{${query}}`);

    let nquads = `uid(profile) <did> "${did}" .`;
    nquads = nquads.concat(`\nuid(profile) <dgraph.type> "${PROFILE_SCHEMA_NAME}" .`);

    mu.setSetNquads(nquads);
    req.setMutationsList([mu]);
    req.setCommitNow(true);

    let response = await this.client.newTxn().doRequest(req);
    let uid = response.getUidsMap().get("uid(profile)")

    if (!uid) {
        // it already existed. query for it.
        const didQuery = `
        query {
            profile(func: eq(did, "${did}")) {
              uid
            }
        }
        `;
        let result = await this.client.newTxn().query(didQuery);
        let profile = result.getJson().profile[0];
        uid = profile.uid;
    }

    return uid;

Any external references to support your case

campoy commented :

Thanks for your report, @pepoospina

Moving this issue to the dgraphjs repository.

pepoospina commented :

@campoy I am not sure is an issue of dgraph-js. The standard dgraph upsert operation will not return the uid if the entry already existed.

prashant-shahi commented :

@pepoospina We can retrieve UID of the existing node by including uid to the query part of the upsert operation.

const mu = new dgraph.Mutation();
const req = new dgraph.Request();

let query = `profile as all(func: eq(did, "${did}")) {
  uid
  did
}`;

req.setQuery(`query{${query}}`);

let nquads = `uid(profile) <did> "${did}" .`;
nquads = nquads.concat(`\nuid(profile) <dgraph.type> "${PROFILE_SCHEMA_NAME}" .`);

mu.setSetNquads(nquads);
req.setMutationsList([mu]);
req.setCommitNow(true);

let response = await dgraphClient.newTxn().doRequest(req);
let uid = response.getUidsMap().get("uid(profile)");
if (uid === undefined) {
  uid = response.getJson().all[0].uid;
}
console.log(uid);