Why @hasInverse is not working for m2m edges?

Hi,

I have a many-to-many relationship between Person and Company. I inserted a person and his company but I cannot get the person by the company.

First, I created the schema:

cat > schema.graphql <<EOF
type Person {
  name: String!
  employers: [Company] @hasInverse(field: "employees")
}

type Company {
  ame: String! @search(by: [hash])
  employees: [Person] @hasInverse(field: "employers")
}
EOF

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

And I inserted the initial data:

curl -H "Content-Type: application/json" -XPOST "localhost:8080/mutate?commitNow=true" -d $'{
    "set": {
        "dgraph.type": "Person",
        "Person.name": "Carol",
        "Person.employers": [
            {
                "dgraph.type": "Company",
                "Company.name": "Google"
            }
        ]
    }
}'

The query to get people and their employers, is ok:

curl -H "Content-Type: application/dql" -XPOST "localhost:8080/query" -d $'{
    results(func: type(Person)) {
        uid
        Person.name
        Person.employers {
            Company.name
            Company.employees {
                Person.name
            }
        }
    }
}'

But I cannot get the companies and their employees:

# request
curl -H "Content-Type: application/dql" -XPOST "localhost:8080/query" -d $'{
    results(func: type(Company)) {
        uid
        Company.name
        Company.employees {
            uid
            Person.name
        }
    }
}'

# response
{
    "data": {
        "results": [
            {
                "uid": "0x47",
                "Company.name": "Google"
            }
        ]
    },
    "extensions": {
        ...
    }
}

What I’m doing wrong?
Dgraph v21.03.

Thanks!

You’re starting off with a GraphQL schema using @hasInverse where the inverse relationships are maintained in GraphQL mutations. Because you ran a DQL mutation, the @hasInverse GraphQL directive doesn’t apply. If you run a GraphQL mutation, then you’ll see your inverse relationships.

Say we start with your GraphQL schema (with “name”, not “ame”, for the Company type):

type Person {
  name: String!
  employers: [Company] @hasInverse(field: "employees")
}

type Company {
  name: String! @search(by: [hash])
  employees: [Person] @hasInverse(field: "employers")
}

Then I can run this GraphQL mutation and see the response of this mutation have the relationship from Carol to Google and back to Carol:

mutation {
  addPerson(input: [
    {
      name: "Carol",
      employers: [
        {
        	name: "Google"
      	}
      ]
    }
  ]) {
    person {
      name
      employers {
        name
        employees {
          name
        }
      }
    }
  }
}

Response:

{
  "addPerson": {
    "person": [
      {
        "name": "Carol",
        "employers": [
          {
            "name": "Google",
            "employees": [
              {
                "name": "Carol"
              }
            ]
          }
        ]
      }
    ]
  }
}

Hi,

Thanks again @dmai!
I’m focusing on DQL because I pretend to use Go to run queries. So, I dropped all schema and create it using DQL:

cat > schema.dql <<EOF
name: string @index(term) .
employers: [uid] @reverse .
employees: [uid] .

type Person {
  name
  employers
}

type Company {
  name
  employees
}
EOF

curl -XPOST "localhost:8080/alter" --data-binary '@schema.dql'

And I inserted person and company separated via Ratel UI:

{
    "set": {
        "dgraph.type": "Company",
        "name": "Google"
    }
}

{
    "set": {
        "dgraph.type": "Person",
        "name": "Carol"
    }
}

Then, I created the edge using the correct UIDs:

{
    "set": {
        "dgraph.type": "Company",
        "uid": "0x58",
        "employees": [{ "uid": "0x59"}]
    }
}

But the query does not return employers:

// request
{
 results(func: type(Company)) {
    name
    employees {
        name
        employers {
            name
        }
    }
    
  }
}

// response
{
  "data": {
    "results": [
      {
        "uid": "0x58",
        "name": "Google",
        "employees": [
          {
            "name": "Carol"
          }
        ]
      }
    ]
  }
}

The @reverse in DQL does not behave the same syntactically as @hasInverse in GraphQL.

You can either A) use the reverse predicate on employees and then query under Person for ~employees or B) when create an edge manually create the inverse:

{
    "set": [{
        "uid": "0x58",
        "employees": [{ "uid": "0x59"}]
    },{
        "uid": "0x59",
        "employers": [{ "uid": "0x58" }]
    }]
}

No automatic inverse edges are created in DQL, but the reverse directive does allow you to use the same predicate in both directions using the ~ syntax to indicate the reverse direction.

Thanks, @amaster507 , both solutions are great!
Is there a recommendation on when to use each approach, querying using ~ and create the inverse?

This definitely needs to be in the docs somewhere—pretty big gotcha! :slightly_smiling_face:

EDIT: I think what needs to be more obvious is that DQL isn’t merely a different syntax, but there is insertion logic that isn’t applied when you use DQL. Clear to me why this is, but wasn’t obvious in the beginning.

2 Likes