Is there possible to have same edge to different type of node?

Hi,

Here is the trick while I am trying to design the schema of my project, to simplify the question, let’s say I have 3 types of node: person, dog, and cat, and I want to use the love edge to present a person “love” dog and cat. How to describe this in the Dgraph? I was trying to use GraphQL to define this schema, then I found myself in trouble. Any suggestion will be appreciated.

type Person {
first_name: String! @search(by: [exact])
last_name: String! @search(by: [exact])
love: dog?cat?
}

type dog {
breed: String!
}

type cat {
color: String!
}

Hi @duckybsd,
I think your requirement will be well supported by Unions. Please make sure that you are running the latest docker image.

  1. Start docker image
docker run --rm -it -p 8000:8000 -p 8080:8080 -p 9080:9080 -p 5080:5080 -p 6080:6080  dgraph/standalone:master
  1. Post the following schema. We introduce a union type pet that can refer to either a Dog or Cat. We set the type of love to pet
type Person {
  id: ID!
  first_name: String! @search(by: [exact])
  last_name: String! @search(by: [exact])
  love: pet 
}

type Dog {
  breed: String! @id @search(by: [hash])
}

type Cat {
  color: String! @id @search(by: [hash])
}

union pet = Dog | Cat 
  1. Let’s post some pets. I use the graphql playground and use the following mutations and queries.
mutation{
  addDog(input:[{breed: "Beagle"},{breed: "Corgi"}]){
    numUids
  }
  
  addCat(input:[{color: "red"}, {color:"black"}]){
    numUids
  }
}
  1. Let’s post some Person nodes, one each as a dog and cat lover.
mutation{
  addPerson(input: [{first_name: "Jim", last_name: "D", love: {dogRef: {breed: "Corgi"}}},
    			    {first_name: "James", last_name: "D", love: {catRef: {color: "black"}}  }]){
    numUids
  }
}
  1. Finally, we can query using fragments. These are the lines with `… on Dog…which helps the query language understand that thebreedbelongs to aDog, and colorto aCat``.
query{
  queryPerson(first: 5){
    first_name
    love {... on Dog {
        breed
      }
      ... on Cat {
        color
      }
    }
  }
}

The query should provide a response as below:

{
  "data": {
    "queryPerson": [
      {
        "first_name": "Jim",
        "love": {
          "breed": "Corgi"
        }
      },
      {
        "first_name": "James",
        "love": {
          "color": "black"
        }
      }
    ]
  }
}
1 Like

Hi @anand,

Thanks for that! It works like a charm. here is another question, if I want to have the reverse edge “loved_by” from Dog and Cat, how should I state in GraphQL?

type Dog {
  breed: String! @id @search(by: [hash])
  loved_by: [Person] @hasInverse(field: love)
}

this throws an error on me:

Field loved_by: inverse field love doesn't exist for type Person

Again, thanks for your help.

1 Like

As per documentation, union types cannot scalars and interfaces. So I had to create a wrapper type APet.

  1. Use this schema to wrap the pet and add the inverse link as below.
type Person {
  id: ID!
  first_name: String! @search(by: [exact])
  last_name: String! @search(by: [exact])
  love: APet @hasInverse(field: lovedBy)
}

type APet{
   lovedBy: Person
   pet: pet 
}


type Dog {
  breed: String! @id @search(by: [hash])
}

type Cat {
  color: String! @id @search(by: [hash])
}

union pet = Dog | Cat 
  1. Mutation to add data is as below.
mutation{
  addPerson(input: [{first_name: "Jim", last_name: "Doe", 
  						love:{pet:{dogRef:{breed: "Corgi"}}}},
  	     			{first_name: "James", last_name: "Doe", 
  						love:{pet:{catRef:{color: "black"}}}}] ){
    numUids
  }
}
  1. Here is a sample query that you might find useful.
query{
  queryAPet(filter:{has: pet}) @cascade{
    pet(filter:{dogFilter: {breed:{eq:"Corgi"}} }){
      ... on Dog{
        breed
      }
    }
    lovedBy{
      first_name
    }
  } 
}

Result:

{
  "data": {
    "queryAPet": [
      {
        "pet": {
          "breed": "Corgi"
        },
        "lovedBy": {
          "first_name": "Jim"
        }
      }
    ]
  }
}
1 Like

No, it seems doesn’t like it. :roll_eyes:

Hi @duckybsd, I used standalone:master, which had the version as below. You might want to refresh your docker image.

Dgraph version   : v20.11.0-rc1-103-g5d162e3a
Dgraph codename  : unnamed
Dgraph SHA-256   : a611c3047cfc530d6c9f81554d94dbb3a6a8aeee3bdf46762ba56fb5ead8c13e
Commit SHA-1     : 5d162e3a
Commit timestamp : 2020-11-27 19:50:12 -0300
Branch           : master
Go version       : go1.14.4
jemalloc enabled : true

I used curl to post the schema as below.

curl -X POST localhost:8080/admin/schema --data-binary '@schema.graphql'

{"data":{"code":"Success","message":"Done"}}%  

Yes, you are right, it’s just my IDE complained about it, the command runs through, thanks.

But a strange thing happened, it does not add reverse to the predicate “love” automatically.

type Person {
  id: ID!
  first_name: String! @search(by: [exact])
  last_name: String! @search(by: [exact])
  love: APet @hasInverse(field: lovedBy)
}

type APet{
   lovedBy: Person
   pet: pet 
}


type Dog {
  breed: String! @id @search(by: [hash])
}

type Cat {
  color: String! @id @search(by: [hash])
}

union pet = Dog | Cat 

love

Hi @duckybsd
The reverse option in DQL Schema and the @hasInverse are not equivalent.

1 Like

Hi @anand,

Thanks, my misunderstood, I thought @hasInverse will add a @reverse option in DQL as well, after all, they seems to do the same thing.

@hasInverse is used to setup up two way edges such that adding a edge in one direction automically adds the one in the inverse direction.

I found more conversation about this in the link: