Need schema design help: hasInverse + interface

Hello everybody,
I’m new to Dgraph and GraphQL and so far everything was quite impressive in my first tests :slight_smile: But now that I’m trying to create an advanced schema for my database, I encountered a problem. Maybe you can help me to wrap my head around this:

I want to create a very flexible way to store and connect data in the database (my approach is inspired by RDF). For that, I created 3 different types of base nodes: EntityNode, ValueNode, and ConnectionNode. The EntityNode is only an empty node that is used as a starting point for all connections to other entities and the connected values to this entity. A ValueNode stores only one piece of information. And between them, there is always a ConnectionNode, which connects them with a special type.
My problem is, that the ConnectionNOde should not only connect one EntityNode to one ValueNode but should connect two EntityNodes or two ValueNode as well. I thought “easy, I just use an Interface”, but I want to make the connections always two-edged for easy traversal, so I have a problem.
Here is my last approach, maybe someone can give me a hint on how I can update my schema so I can add @hasInverse to the Nodes.

# base entity node
interface EntityNode {
  id: ID!
  connections: [ConnectionNode] #hasInverse <- but what field should I name???
}

# base value node
interface ValueNode {
  connections: [ConnectionNode] #hasInverse <- but what field should I name???
}

interface ConnectionNode {
  connectionType: ConnectionType! @search
 ...
}

type EntityConnectionNode implements ConnectionNode {
  node1: EntityNode!
  node2: EntityNode!
}

type ValueConnectionNode implements ConnectionNode {
  node1: ValueNode!
  node2: ValueNode!
}

type EntityValConnectionNode implements ConnectionNode{
  entityNode: EntityNode!
  valueNode: ValueNode!
}

enum ConnectionType {
...
}

I hope my explanation of the problem was understandable, if not, pls. ask any questions :slight_smile:

1 Like

Hi @ECV_Node,

Welcome to Dgraph! Can I ask that you back up a bit and give an example of some data that would live inside this schema?

@matthewmcneely Thanks for getting back to me :slight_smile:

For example, I like to store some metadata information of a file. But I don’t want to store it in the traditional way, where I create a Node type like ImageNode and store the data directly in the node (as attributes). I want a much more flexible way, therefore my schema is inspired by RDF.

If we look at the Image example, my graph will look something like this:
A: EntityNode(id: xxx, connections[B])
B: EntityValConnectionNode(connectionType: StoragePath, node1: A, node2: C)
C: StringValueNode (connections[B], value: “my/awesome/path”)

But my problem is, that I want to model the connections as “hasInverse” to fit subtypes of the parent node type ConnectionNode.

I hope this clarifies my question a little bit? :slight_smile:

Hey @ECV_Node,

Apologies for the late reply…

Because of the ambiguity of the interfaces, you won’t be able to use @hasInverse in the way you requested. However, keep in mind that @hasInverse in some ways is a just convenience mechanism to tell the GraphQL processor to bind edges automagically.

If you take care when you populate the graph by manually binding inverse edges, I think you can still accomplish this “abstraction” concept.

Here’s an example insert record that creates a two connections to an EntityNode

[
    {
        "dgraph.type": "EntityNode",
        "uid": "_:entity1",
        "EntityNode.connections": [
            {
                "dgraph.type": "EntityValConnectionNode",
                "ConnectionNode.connectionType": "STORAGE_PATH",
                "EntityValConnectionNode.entityNode": {
                    "id": "_:entity1"
                },
                "EntityValConnectionNode.valueNode": {
                    "dgraph.type": "StringValueNode",
                    "StringValueNode.value": "/my/awesome/path"
                }
            },
            {
                "dgraph.type": "EntityValConnectionNode",
                "ConnectionNode.connectionType": "ARTIFACT_SIZE",
                "EntityValConnectionNode.entityNode": {
                    "id": "_:entity1"
                },
                "EntityValConnectionNode.valueNode": {
                    "dgraph.type": "IntegerValueNode",
                    "IntegerValueNode.value": 64232
                }
            }
        ]
    }
]

And here’s the query that extracts it, followed by the result:

query ExampleQuery {
  queryEntityNode {
    id
    connections {
      connectionType
      ... on EntityValConnectionNode {
        connectionType
        entityNode {
          id
        }
        valueNode {
          id
          ... on StringValueNode {
            stringValue: value
          }
          ... on IntegerValueNode {
            integerValue: value
          }
        }
      }
    }
  }
}

Results:

    "queryEntityNode": [
      {
        "id": "0x14fb18c",
        "connections": [
          {
            "connectionType": "STORAGE_PATH",
            "entityNode": {
              "id": "0x152becc"
            },
            "valueNode": {
              "id": "0x154456c",
              "stringValue": "/my/awesome/path"
            }
          },
          {
            "connectionType": "ARTIFACT_SIZE",
            "entityNode": {
              "id": "0x14fb18d"
            },
            "valueNode": {
              "id": "0x15752ac",
              "integerValue": 64232
            }
          }
        ]
      }
    ]

Note I added some additional types to your example schema. I’ve uploaded all of this in a branch at my Dgraph sandbox: GitHub - matthewmcneely/dgraph-sandbox at example/abstraction-experiments