Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to represent the Data Model of a Graph

So we have been developing some graph based analysis tools, using neo4j as a persistence engine in the background. As part of this we are developing a graph data model suitable for our domain, and we want to use this in the application layer to restrict the types of nodes, or to ensure that nodes of certain types must carry certain properties. Normal data model restrictions.

So thats the background, what I am asking is if there is some standard way to represent a data-model for a graph db? The graph equivalent of an xsd perhaps?

like image 568
phil_20686 Avatar asked Mar 19 '23 13:03

phil_20686


2 Answers

There's an open-source project supporting strong schema definitions in Neo4j: Structr (http://structr.org, see it in action: http://vimeo.com/structr/videos)

Structr's Visual Schema Editor

With Structr, you can define an in-graph schema of your data model including

  • Type inheritance
  • Supported data types: Boolean, String, Integer, Long, Double, Date, Enum (+ values)
  • Default values
  • Cardinality (1:1, 1:*, *:1)
  • Not-null constraints
  • Uniqueness constraints
  • Full type safety
  • Validation
  • Cardinality enforcement

Support for methods (custom action) is currently being added to the schema.

The schema can be edited with an editor, or directly via REST, modifiying the JSON representation of the data model:

{
   "query_time": "0.001618446",
   "result_count": 4,
   "result": [
      {
         "name": "Whisky",
         "extendsClass": null,
         "relatedTo": [
            {
               "id": "96d05ddc9f0b42e2801f06afb1374458",
               "name": "Flavour"
            },
            {
               "id": "28f85dca915245afa3782354ea824130",
               "name": "Location"
            }
         ],
         "relatedFrom": [],
         "id": "df9f9431ed304b0494da84ef63f5f2d8",
         "type": "SchemaNode",
         "_name": "String"
      },
      {
         "name": "Flavour",
         ...
      },
      {
         "name": "Location",
         ...
      },
      {
         "name": "Region",
         ...
      }
   ],
   "serialization_time": "0.000829985"
}

{
   "query_time": "0.001466743",
   "result_count": 3,
   "result": [
      {
         "name": null,
         "sourceId": "28f85dca915245afa3782354ea824130",
         "targetId": "e4139c5db45a4c1cbfe5e358a84b11ed",
         "sourceMultiplicity": null,
         "targetMultiplicity": "1",
         "sourceNotion": null,
         "targetNotion": null,
         "relationshipType": "LOCATED_IN",
         "sourceJsonName": null,
         "targetJsonName": null,
         "id": "d43902ad7348498cbdebcd92135926ea",
         "type": "SchemaRelationship",
         "relType": "IS_RELATED_TO"
      },
      {
         "name": null,
         "sourceId": "df9f9431ed304b0494da84ef63f5f2d8",
         "targetId": "96d05ddc9f0b42e2801f06afb1374458",
         "sourceMultiplicity": null,
         "targetMultiplicity": "1",
         "sourceNotion": null,
         "targetNotion": null,
         "relationshipType": "HAS_FLAVOURS",
         "sourceJsonName": null,
         "targetJsonName": null,
         "id": "bc9a6308d1fd4bfdb64caa355444299d",
         "type": "SchemaRelationship",
         "relType": "IS_RELATED_TO"
      },
      {
         "name": null,
         "sourceId": "df9f9431ed304b0494da84ef63f5f2d8",
         "targetId": "28f85dca915245afa3782354ea824130",
         "sourceMultiplicity": null,
         "targetMultiplicity": "1",
         "sourceNotion": null,
         "targetNotion": null,
         "relationshipType": "PRODUCED_IN",
         "sourceJsonName": null,
         "targetJsonName": null,
         "id": "a55fb5c3cc29448e99a538ef209b8421",
         "type": "SchemaRelationship",
         "relType": "IS_RELATED_TO"
      }
   ],
   "serialization_time": "0.000403616"
}

You can access nodes and relationships stored in Neo4j as JSON objects through a RESTful API which is dynamically configured based on the in-graph schema.

$ curl try.structr.org:8082/structr/rest/whiskies?name=Ardbeg
{
   "query_time": "0.001267211",
   "result_count": 1,
   "result": [
      {
         "flavour": {
            "name": "J",
            "description": "Full-Bodied, Dry, Pungent, Peaty and Medicinal, with Spicy, Feinty Notes.",
            "id": "626ba892263b45e29d71f51889839ebc",
            "type": "Flavour"
         },
         "location": {
            "region": {
               "name": "Islay",
               "id": "4c7dd3fe2779492e85bdfe7323cd78ee",
               "type": "Region"
            },
            "whiskies": [
               ...
            ],
            "name": "Port Ellen",
            "latitude": null,
            "longitude": null,
            "altitude": null,
            "id": "47f90d67e1954cc584c868e7337b6cbb",
            "type": "Location"
         },
         "name": "Ardbeg",
         "id": "2db6b3b41b70439dac002ba2294dc5e7",
         "type": "Whisky"
      }
   ],
   "serialization_time": "0.010824154"
}

In the UI, there's also a data editing (CRUD) tool, and CMS components supporting to create web applications on Neo4j.

Disclaimer: I'm a developer of Structr and founder of the project.

like image 65
Axel Morgner Avatar answered Mar 31 '23 06:03

Axel Morgner


No, there's no standard way to do this. Indeed, even if there were, keep in mind that the only constraints that neo4j currently supports are uniqueness constraints.

Take for example some sample rules:

  • All nodes labeled :Person must have non-empty properties fname and lname
  • All nodes labeled :Person must have >= 1 outbound relationship of type :works_for

The trouble with the present neo4j is that even in the case where you did have a schema language (standardized) that could express these things, there wouldn't be a way that the db engine itself could actually enforce that constraint.

So the simple answer is no, there's no standard way of doing that right now.

A few tricks I've seen people use to simulate the same:

  1. Assemble a list of "test suite" cypher queries, with known results. Query for things you know shouldn't be there; non-empty result sets are a sign of a problem/integrity violation. Query for things you know should be there; empty result sets are a problem.
  2. Application-level control -- via some layer like spring-data or similar, control who can talk to the database. This essentially moves your data integrity/testing problem up into the app, away from the database.

It's a common (and IMHO annoying) aspect of many NoSQL solutions (not specifically neo4j) that because of their schema-weakness, they tend to force validation up the tech stack into the application. Doing these things in the application tends to be harder and more error-prone. SQL databases permit you to implement all sorts of schema constraints, triggers, etc -- specifically to make it really damn hard to put the wrong data into the database. The NoSQL databases typically either aren't there yet, or don't do this as a design decision. There are indeed flexibility/performance tradeoffs. Databases can insert faster and be more flexible to adapt quickly if they aren't burdened with checking each atom of data against a long list of schema rules.

EDIT: Two relevant resources: the metagraphs proposal talks about how you could represent the schema as a graph, and neoprofiler is an application that attempts to infer the actual structure of a neo4j database and show you its "profile".

With time, I think it's reasonable to hope that neo would include basic integrity features like requiring certain labels to have certain properties (the example above), restricting the data types of certain properties (lname must always be a String, never an integer), and so on. The graph data model is a bit wild and wooly though (in the computational complexity sense) and there are some constraints on graphs that people desperately would want, but will probably never get. An example would be the constraint that a graph can't have cycles in it. Enforcing that on the creation of every relationship would be very computationally intensive. (

like image 36
FrobberOfBits Avatar answered Mar 31 '23 06:03

FrobberOfBits