Package: pypropgraph
A Property Graph library for python that supports reading and writing
property graphs in a textual format, reading an writing via Cypher, and
a simple schema language.generate documentation for your property graph structureload, parse, and manipulate PG Schema documentsload saved graphs from a YAML format, schema embeddedgenerate cypher from saved graphs to load property graphsYou can install the package via:pipinstallpypropgraphThe module can be invoked directly and provides a set of basic commands that
allow parsing, inspection, cypher statement generation, and loading ontologies.The invocation is:python-mpropgraph{operation}{file...}?whereoperationis one of:validate- parse and validate the graphcypher- generate cypher create/merge statementsload- load the ontology into a property graph databaseschema.check- check the syntax of a schemaschema.doc- generate Markdown documentation for the schemaIf the file is omitted, the command will read from stdin. Otherwise, each
file specified will be read and operated on in the order they are specified.The module currently supports loading ontologies directly intoRedisGraph.The following options can be specified for connecting to the database:--host {name}|{ip}- the host of the database, defaults to 0.0.0.0--port {port}- the port, defaults to 6379--password {password}- the database password, default is no password--graph {key}- the graph key, defaults to "test"--infer- infer identity and labels from @id and @type, respectivelyAdding the--show-queryoption will allow you to see the Cypher statements as
they are executed.The YAML-based format is a simple dictionary of nodes and edges.At the top-level, agraphis a dictionary whose keys define
the nodes, schema, and edges. The keys can either be:~schema- the schema definition for the property graph~edges- a set of fully qualified edges{label} - a node labelA:~label:Componentid:'A'name:'ComponentA'use:12~edges:-~to:B~label:imports-~to:C~label:importsB:~label:Componentid:'B'name:'ComponentB'use:6C:~label:Componentid:'C'name:'ComponentC'use:7~edges:e1:~from:C~to:B~label:importsWhen a schema is specified via~schema, the properties that establish
the node's identity can be specified.Alternatively, we can use inferencing and the edge label format:A:@type:Component@id:'A'name:'ComponentA'use:12:imports:-~to:B-~to:CB:@type:Component@id:'B'name:'ComponentB'use:6C:@type:Component@id:'C'name:'ComponentC'use:7:imports:-~from:C~to:BAnodeis a simple dictionary whose key/value pairs define properties
all except for two special labels:~labels- the set of Node labels~edges- the edges connected to the node{label} - a propertyApropertycan either be a simple key/value pair where the key will be the
property name. It can also be defined with thename:andvalue:keys for
property name values that are harder to encode as a key:Funky:name:'Town'p1:name:"Meaningoflife"value:42Node can specify an edge via a label and enumerate the target nodes and edge
properties:A:id:'A':child:-~to:Buse:1209-~to:Cuse:432B:id:'B':child:e1:~to:Cuse:128C:id:'C'Nodes can also specify a set of edges that originate at the node via the~edgeskey. The edges are specified as a list or key labeled set:A:id:'A'~edges:-~to:B~label:childuse:1209-~to:C~label:childuse:432B:id:'B'~edges:e1:~to:C~label:childuse:128C:id:'C'Edges can also be specified at the graph level instead of in the node. At the top-level, a single~edgeskey is allowed that can specify edges from and to nodes. The~fromkey must also be specified:A:id:'A'B:id:'B'C:id:'C'~edges:-~from:A~to:B~label:childuse:1209-~from:A~to:C~label:childuse:432-~from:B~to:C~label:childuse:128Alternatively, you can specify a label for the edge and enumerate the edges associated with that label:A:id:'A'B:id:'B'C:id:'C':child:-~from:A~to:Buse:1209-~from:A~to:Cuse:432-~from:B~to:Cuse:128Reusing the '@type' and '@id' properties fromJSON-LD,
if these properties are specified on a node, then the following inferences
can be optionally applied:The value of @type will turn into a node labelThe value of @id will be used as the key for matching the nodeOtherwise, labels and identity must be accomplished via the schema.A schema can be specified at the top-level via the~schemakey. The schema itself is either embedded directly as text or has a singlesourcekey specifying the file location.For example, in the imports graph example, theidproperty can be specified as
property that identifies the node. This can be helpful for generating merge or match queries.Theschema formatis described separately and allows you to define nodes, labels, properties, and their descriptions.The schema can be embedded as text:~schema:|(:Component {id}).id = 'the component identifier'.name = 'the component descriptive name'.use = int 'a count of usage'-[:imports]->(:Component) = 'an imported component'A:~label:Componentid:'A'name:'ComponentA'use:12~edges:-~to:B~label:imports-~to:C~label:importsB:~label:Componentid:'B'name:'ComponentB'use:6C:~label:Componentid:'C'name:'ComponentC'use:7~edges:-~from:C~to:B~label:importsor via reference:~schema:source:schema.pgsA:~label:Componentid:'A'name:'ComponentA'use:12~edges:-~to:B~label:imports-~to:C~label:importsB:~label:Componentid:'B'name:'ComponentB'use:6C:~label:Componentid:'C'name:'ComponentC'use:7~edges:-~from:C~to:B~label:importsThe graph source is just raw YAML and should be loaded directly using theyamlpackage:importyamlwithopen('graph.yaml','r')asinput:graph_data=yaml.load(input,Loader=yaml.Loader)Once you have loaded the graph YAML, you can read the graph into a sequence
of item (NodeItem or EdgeRelationItem):importyamlfrompropgraphimportread_graphwithopen('graph.yaml','r')asinput:graph_data=yaml.load(input,Loader=yaml.Loader)foriteminread_graph(graph_data):print(item)These items can be turned into cypher merge or create statements:importyamlfrompropgraphimportread_graph,graph_to_cypherwithopen('graph.yaml','r')asinput:graph_data=yaml.load(input,Loader=yaml.Loader)forqueryingraph_to_cypher(read_graph(graph_data)):print(query,end=';\n')Finally, the graph can easily be loaded into RedisGraph:importyamlfrompropgraphimportread_graph,graph_to_cypherimportredisfromredisgraphimportGraphr=redis.Redis(host='localhost',port=6379,password='...')rg=Graph('test',r)withopen('graph.yaml','r')asinput:graph_data=yaml.load(input,Loader=yaml.Loader)forqueryingraph_to_cypher(read_graph(graph_data)):rg.query(query)A schema can be loaded from a file:frompropgraphimportSchemaParserparser=SchemaParser()withopen('schema.pgs','r')asinput:schema=parser.parse(input)or a string:frompropgraphimportSchemaParsersource='''(:Component{id}).id = 'the component identifier'.name = 'the component descriptive name'.use = int 'a count of usage'-[:imports]->(:Component) = 'an imported component''''parser=SchemaParser()schema=parser.parse(input)Documentation in Markdown format can be generate from the schema object:importsysfrompropgraphimportSchemaParsersource='''(:Component{id}).id = 'the component identifier'.name = 'the component descriptive name'.use = int 'a count of usage'-[:imports]->(:Component) = 'an imported component''''parser=SchemaParser()schema=parser.parse(input)schema.documentation(sys.stdout)Note: incomplete ...read_graph(source,location=None,schema=None)Reads a graph into a sequence of itemsgraph_to_cypher(stream,merge=True)Transforms a sequence of items into a sequence of cypher statementscypher_for_node(item,merge=True)Returns a cypher statement to create a node from a node item.cypher_for_edge_relation(item,merge=True)Returns a cypher statement to create an edge from a edge relation item.