Adding empty required field *Ref fields in Go

Hi!

Quick question regarding using Go to invoke the “add” mutations for Dgraph.

In this schema

type A {
  id: ID!
  name: String!
  relation: B! @hasInverse(field: relation)
}

type B {
  id: ID!
  name: String!
  relation: A!
}

What would the “empty” value be if I wanted to call an “add” mutation for A where no B values exist in the database (e.g. I add A and then add B) in Go? My app is written in Go and I’m communicating with the Dgraph instance via HTTP requests written in Go but I can’t figure out what the “empty” value here is.

Best,
John

Hi John, AFAIK, there is no pre-defined “empty” value.

Am I required to submit an actual value? It’s a required field but I took that to mean a key just needed to be provided and in some cases (e.g. String!) an empty "" could be submitted. How would this work in my cases where an ID reference is required?

Hey @forstmeier,

Try 0x0 for a default value. Refer to: A reply from @ahsan.

If that doesn’t work, then you can have an empty NIL, node of type B specifically for this purpose.

So whenever you want to create nodes like A without its accompanying B, then you can point it to a NIL node of type B.

e.g. Say you make followers mandatory for any user of your website. Since new users won’t have any followers, you can make it so that the website will be the default follower for the new Users!

P.S. You’ll need to use interface for the second approach, otherwise how can you provide a value for its relation field! :sweat_smile:

Hi John, if you know both A and B, then you can use the following mutation. Please try it through Ratel.

mutation {
  addA(input: [{name : "instanceOfA",  relation : {name: "instanceOfB"}}]){
    numUids
  }
}

If you dont know B, you could create a dummy instance and update it later (as Abhijit mentioned).

@abhijit-kar good ideas unfortunately I’m still having issues (probably due to my misunderstanding the problem).

Here’s the actual schema (truncated since not all of it is necessary).

type Protocol implements Location @auth(
...
) {
	id: ID!
	owner: OwnerOrg!
	name: String! @search(by: [fulltext])
	description: String! @search(by: [fulltext])
	form: ProtocolForm! @hasInverse(field: protocols)
	plan: Plan!
	dobStart: DateTime
	dobEnd: DateTime
	race: [Race]
	sex: [Sex]
	specimens: [Specimen!]! @hasInverse(field: protocol)
}

type Plan @auth(
...
) {
	id: ID!
	owner: OwnerOrg!
	name: String! @search(by: [fulltext])
	labs: [LabOrg!]! @hasInverse(field: plans)
	storages: [StorageOrg!]! @hasInverse(field: plans)
	protocol: Protocol! @hasInverse(field: plan)
}

The Location interface just has some scalar values for specific addresses and the ProtocolForm type is another type that implements a Form interface again with basic scalars.

In my Go code I’m doing something like this:

	owner := map[string]string{
		"id": ownerID,
	}

	protocolInputs := []map[string]interface{}{}
	for _, protocolName := range protocolNames {
		input := map[string]interface{}{
			"street":      randomString(streets),
			"city":        randomString(cities),
			"county":      randomString(counties),
			"state":       randomString(states),
			"zip":         randomInt(zips),
			"country":     randomString(countries),
			"owner":       owner,
			"name":        protocolName,
			"description": randomString(descriptions),
			"form":        empty,
			"plan":        empty,
			"dobStart":    dobStart.String(),
			"dobEnd":      dobEnd.String(),
			"race":        []string{
				randomString(race),
			},
			"sex":         []string{
				randomString(sex),
			},
			"specimens":   make([]map[string]string, 0),
		}

		protocolInputs = append(protocolInputs, input)
	}

For the empty variable, I’ve been trying to set it to different values in order for the “add” Protocol mutation to work (e.g. empty := make(map[string]string)) but this seems to generate errors like:

{"errors":[{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type ProtocolForm requires a value for field owner, but no value present"},{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type Plan requires a value for field owner, but no value present"},{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type ProtocolForm requires a value for field owner, but no value present"},{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type Plan requires a value for field owner, but no value present"},{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type ProtocolForm requires a value for field owner, but no value present"},{"message":"couldn't rewrite mutation addProtocol because failed to rewrite mutation payload because type Plan requires a value for field owner, but no value present"}]...

I also tried the suggestion of

empty := map[string]string{
  "id": "0x0",
}

But got:
{"errors":[{"message":"couldn't rewrite query for mutation addProtocol because ID \"0x0\" isn't a ProtocolForm"}

Any idea what I’m doing wrong here? I definitely prefer the idea of being able to insert an “empty” value that I then update in a subsequent mutation. I don’t actually know the other object values at the time I’m inserting Protocol in the “add” mutation.

CC @anand for reference.

You should be able to do this:

addA(input: [{
  name: "some name"
  relation: {
    name: ""
  }
}]) {
  numUids
}

In essence, you need to construct a valid GraphQL object of type B for relation field in type A while adding object of type A.

2 Likes

@abhimanyusinghgaur that 100% solved it, thanks! The code I’m writing will all be open source but for quicker reference for anyone with the same issue, in the Go code, I wrote out something like this:

	emptyForm := map[string]interface{}{
		"owner": owner,
		"title": "",
		"body": "",
		"protocolID": "",
	}

	owner := map[string]string{
		"id": ownerID,
	}

And emptyForm is passed into the respective key on the mutation payload.

From a modeling perspective, if you know that some of the relations can be ‘empty’ at the time of creation of the entity, why not update the model to reflect the reality and make the relations optional instead of being forced to having workarounds for ‘dummy’ values?

1 Like

@midoc That’s a good point too - right now I’m treating the schema “strictly” based on some of our expectations for the data but we could “loosen” it going forward.