Using Schema with Graphs

OrientDB, through the Graph API, offers a number of features above and beyond the traditional Graph Databases given that it supports concepts drawn from both the Document Database and the Object Oriented worlds. For instance, consider the power of graphs, when used in conjunction with schemas and constraints.

Use Case: Car Database

For this example, consider a graph database that maps the relationship between individual users and their cars. First, create the graph schema for the Person and Car vertex classes, as well as the Owns edge class to connect the two:

  1. orientdb> CREATE CLASS Person EXTENDS V
  2.  
  3. orientdb> CREATE CLASS Car EXTENDS V
  4.  
  5. orientdb> CREATE CLASS Owns EXTENDS E

These commands lay out the schema for your graph database. That is, they define two vertex classes and an edge class to indicate the relationship between the two. With that, you can begin to populate the database with vertices and edges.

  1. orientdb> CREATE VERTEX Person SET name = 'Luca'
  2.  
  3. Created vertex 'Person#11:0{name:Luca} v1' in 0,012000 sec(s).
  4.  
  5. orientdb> CREATE VERTEX Car SET name = 'Ferrari Modena'
  6.  
  7. Created vertex 'Car#12:0{name:Ferrari Modena} v1' in 0,001000 sec(s).
  8.  
  9. orientdb> CREATE EDGE Owns FROM ( SELECT FROM Person ) TO ( SELECT FROM Car )
  10.  
  11. Created edge '[e[#11:0->#12:0][#11:0-Owns->#12:0]]' in 0,005000 sec(s).

Querying the Car Database

In the above section, you create a car database and populated it with vertices and edges to map out the relationship between drivers and their cars. Now you can begin to query this database, showing what those connections are. For example, what is Luca’s car? You can find out by traversing from the vertex Luca to the outgoing vertices following the Owns relationship.

  1. orientdb> SELECT NAME FROM ( SELECT EXPAND( OUT('Owns') ) FROM Person
  2. WHERE name='Luca' )
  3.  
  4. ----+-------+-----------------+
  5. # | @RID | name |
  6. ----+-------+-----------------+
  7. 0 | #-2:1 | Ferrari Modena |
  8. ----+-------+-----------------+

As you can see, the query returns that Luca owns a Ferrari Modena. Now consider expanding your database to track where each person lives.

Adding a Location Vertex

Consider a situation, in which you might want to keep track of the countries in which each person lives. In practice, there are a number of reasons why you might want to do this, for instance, for the purposes of promotional material or in a larger database to analyze the connections to see how residence affects car ownership.

To begin, create a vertex class for the country, in which the person lives and an edge class that connects the individual to the place.

  1. orientdb> CREATE CLASS Country EXTENDS V
  2.  
  3. orientdb> CREATE CLASS Lives EXTENDS E
  4.  

This creates the schema for the feature you’re adding to the cars database. The vertex class Country recording countries in which people live and the edge class Lives to connect individuals in the vertex class Person to entries in Country.

With the schema laid out, create a vertex for the United Kingdom and connect it to the person Luca.

  1.  
  2. orientdb> CREATE VERTEX Country SET name='UK'
  3.  
  4. Created vertex 'Country#14:0{name:UK} v1' in 0,004000 sec(s).
  5.  
  6. orientdb> CREATE EDGE Lives FROM ( SELECT FROM Person ) TO ( SELECT FROM Country
  7.  
  8. Created edge '[e[#11:0->#14:0][#11:0-Lives->#14:0]]' in 0,006000 sec(s).

The second command creates an edge connecting the person Luca to the country United Kingdom. Now that your cars database is defined and populated, you can query it, such as a search that shows the countries where there are users that own a Ferrari.

  1. orientdb> SELECT name FROM ( SELECT EXPAND( IN('Owns').OUT('Lives') )
  2. FROM Car WHERE name LIKE '%Ferrari%' )
  3.  
  4. ---+-------+--------+
  5. # | @RID | name |
  6. ---+-------+--------+
  7. 0 | #-2:1 | UK |
  8. ---+-------+--------+

Using in and out Constraints on Edges

In the above sections, you modeled the graph using a schema without any constraints, but you might find it useful to use some. For instance, it would be good to require that an Owns relationship only exist between the vertex Person and the vertex Car.

  1. orientdb> CREATE PROPERTY Owns.out LINK Person
  2.  
  3. orientdb> CREATE PROPERTY Owns.in LINK Car

These commands link outgoing vertices of the Person class to incoming vertices of the Car class. That is, it configures your database so that a user can own a car, but a car cannot own a user.

Using MANDATORY Constraints on Edges

By default, when OrientDB creates an edge that lacks properties, it creates it as a Lightweight Edge. That is, it creates an edge that has no physical record in the database. Using the MANDATORY setting, you can stop this behavior, forcing it to create the standard Edge, without outright disabling Lightweight Edges.

  1. orientdb> ALTER PROPERTY Owns.out MANDATORY=TRUE
  2. orientdb> ALTER PROPERTY Owns.in MANDATORY=TRUE

Using UNIQUE with Edges

For the sake of simplicity, consider a case where you want to limit the way people are connected to cars to where the user can only match to the car once. That is, if Luca owns a Ferrari Modena, you might prefer not to have a double entry for that car in the event that he buys a new one a few years later. This is particularly important given that our database covers make and model, but not year.

To manage this, you need to define a UNIQUE index against both the out and in properties.

  1. orientdb> CREATE INDEX UniqueOwns ON Owns(out,in) UNIQUE
  2.  
  3. Created index successfully with 0 entries in 0,023000 sec(s).

The index returns tells us that no entries are indexed. You have already created the Onws relationship between Luca and the Ferrari Modena. In that case, however, OrientDB had created a Lightweight Edge before you set the rule to force the creation of documents for Owns instances. To fix this, you need to drop and recreate the edge.

  1. orientdb> DELETE EDGE FROM #11:0 TO #12:0
  2. orientdb> CREATE EDGE Owns FROM ( SELECT FROM Person ) TO ( SELECT FROM Car )

To confirm that this was successful, run a query to check that a record was created:

  1. orientdb> SELECT FROM Owns
  2.  
  3. ---+-------+-------+--------+
  4. # | @RID | out | in |
  5. ---+-------+-------+--------+
  6. 0 | #13:0 | #11:0 | #12:0 |
  7. ---+-------+-------+--------+

This shows that a record was indeed created. To confirm that the constraints work, attempt to create an edge in Owns that connects Luca to the United Kingdom.

  1. orientdb> CREATE EDGE Owns FROM ( SELECT FROM Person ) TO ( SELECT FROM Country )
  2.  
  3. Error: com.orientechnologies.orient.core.exception.OCommandExecutionException:
  4. Error on execution of command: sql.create edge Owns from (select from Person)...
  5. Error: com.orientechnologies.orient.core.exception.OValidationException: The
  6. field 'Owns.in' has been declared as LINK of type 'Car' but the value is the
  7. document #14:0 of class 'Country'

This shows that the constraints effectively blocked the creation, generating a set of errors to explain why it was blocked.

You now have a typed graph with constraints. For more information, see Graph Schema.