1919 If no file is specified, the tool will print the class definitions to stdout.
2020
2121 options:
22- -h, --help show this help message and exit
23- --db bolt://neo4j:neo4j@localhost:7687
22+ -h, --help show this help message and exit
23+ --db bolt://neo4j:neo4j@localhost:7687
2424 Neo4j Server URL
25- -T, --write-to someapp/models.py
25+ -T, --write-to someapp/models.py
2626 File where to write output.
27+ --no-rel-props Do not inspect relationship properties
28+ --no-rel-cardinality Do not infer relationship cardinality
2729"""
2830
2931import argparse
@@ -116,13 +118,20 @@ def get_indexed_properties_for_label(label):
116118
117119class RelationshipInspector :
118120 @classmethod
119- def outgoing_relationships (cls , start_label ):
120- query = f"""
121- MATCH (n:`{ start_label } `)-[r]->(m)
122- WITH DISTINCT type(r) as rel_type, head(labels(m)) AS target_label, keys(r) AS properties, head(collect(r)) AS sampleRel
123- ORDER BY size(properties) DESC
124- RETURN rel_type, target_label, apoc.meta.cypher.types(properties(sampleRel)) AS properties LIMIT 1
125- """
121+ def outgoing_relationships (cls , start_label , get_properties : bool = True ):
122+ if get_properties :
123+ query = f"""
124+ MATCH (n:`{ start_label } `)-[r]->(m)
125+ WITH DISTINCT type(r) as rel_type, head(labels(m)) AS target_label, keys(r) AS properties, head(collect(r)) AS sampleRel
126+ ORDER BY size(properties) DESC
127+ RETURN rel_type, target_label, apoc.meta.cypher.types(properties(sampleRel)) AS properties LIMIT 1
128+ """
129+ else :
130+ query = f"""
131+ MATCH (n:`{ start_label } `)-[r]->(m)
132+ WITH DISTINCT type(r) as rel_type, head(labels(m)) AS target_label
133+ RETURN rel_type, target_label, {{}} AS properties LIMIT 1
134+ """
126135 result , _ = db .cypher_query (query )
127136 return [(record [0 ], record [1 ], record [2 ]) for record in result ]
128137
@@ -222,7 +231,9 @@ def parse_imports():
222231 return imports
223232
224233
225- def build_rel_type_definition (label , outgoing_relationships , defined_rel_types ):
234+ def build_rel_type_definition (
235+ label , outgoing_relationships , defined_rel_types , infer_cardinality : bool = True
236+ ):
226237 class_definition_append = ""
227238 rel_type_definitions = ""
228239
@@ -241,9 +252,12 @@ def build_rel_type_definition(label, outgoing_relationships, defined_rel_types):
241252 rel_type
242253 )
243254
244- cardinality = RelationshipInspector .infer_cardinality (rel_type , label )
255+ cardinality_string = ""
256+ if infer_cardinality :
257+ cardinality = RelationshipInspector .infer_cardinality (rel_type , label )
258+ cardinality_string += f", cardinality={ cardinality } "
245259
246- class_definition_append += f' { clean_class_member_key (rel_name )} = RelationshipTo("{ target_label } ", "{ rel_type } ", cardinality= { cardinality } '
260+ class_definition_append += f' { clean_class_member_key (rel_name )} = RelationshipTo("{ target_label } ", "{ rel_type } "{ cardinality_string } '
247261
248262 if rel_props and rel_type not in defined_rel_types :
249263 rel_model_name = generate_rel_class_name (rel_type )
@@ -265,7 +279,11 @@ def build_rel_type_definition(label, outgoing_relationships, defined_rel_types):
265279 return class_definition_append
266280
267281
268- def inspect_database (bolt_url ):
282+ def inspect_database (
283+ bolt_url ,
284+ get_relationship_properties : bool = True ,
285+ infer_relationship_cardinality : bool = True ,
286+ ):
269287 # Connect to the database
270288 print (f"Connecting to { bolt_url } " )
271289 db .set_connection (bolt_url )
@@ -284,23 +302,32 @@ def inspect_database(bolt_url):
284302 indexed_properties = NodeInspector .get_indexed_properties_for_label (label )
285303
286304 class_definition = f"class { class_name } (StructuredNode):\n "
287- class_definition += "" .join (
288- [
289- build_prop_string (
290- unique_properties , indexed_properties , prop , prop_type
291- )
292- for prop , prop_type in properties .items ()
293- ]
294- )
305+ if properties :
306+ class_definition += "" .join (
307+ [
308+ build_prop_string (
309+ unique_properties , indexed_properties , prop , prop_type
310+ )
311+ for prop , prop_type in properties .items ()
312+ ]
313+ )
295314
296- outgoing_relationships = RelationshipInspector .outgoing_relationships (label )
315+ outgoing_relationships = RelationshipInspector .outgoing_relationships (
316+ label , get_relationship_properties
317+ )
297318
298319 if outgoing_relationships and "StructuredRel" not in IMPORTS :
299320 IMPORTS .append ("RelationshipTo" )
300- IMPORTS .append ("StructuredRel" )
321+ # No rel properties = no rel classes
322+ # Then StructuredRel import is not needed
323+ if get_relationship_properties :
324+ IMPORTS .append ("StructuredRel" )
301325
302326 class_definition += build_rel_type_definition (
303- label , outgoing_relationships , defined_rel_types
327+ label ,
328+ outgoing_relationships ,
329+ defined_rel_types ,
330+ infer_relationship_cardinality ,
304331 )
305332
306333 if not properties and not outgoing_relationships :
@@ -353,6 +380,20 @@ def main():
353380 help = "File where to write output." ,
354381 )
355382
383+ parser .add_argument (
384+ "--no-rel-props" ,
385+ dest = "get_relationship_properties" ,
386+ action = "store_false" ,
387+ help = "Do not inspect relationship properties" ,
388+ )
389+
390+ parser .add_argument (
391+ "--no-rel-cardinality" ,
392+ dest = "infer_relationship_cardinality" ,
393+ action = "store_false" ,
394+ help = "Do not infer relationship cardinality" ,
395+ )
396+
356397 args = parser .parse_args ()
357398
358399 bolt_url = args .neo4j_bolt_url
@@ -364,12 +405,22 @@ def main():
364405 # Before connecting to the database
365406 if args .write_to :
366407 with open (args .write_to , "w" ) as file :
367- output = inspect_database (bolt_url = bolt_url )
408+ output = inspect_database (
409+ bolt_url = bolt_url ,
410+ get_relationship_properties = args .get_relationship_properties ,
411+ infer_relationship_cardinality = args .infer_relationship_cardinality ,
412+ )
368413 print (f"Writing to { args .write_to } " )
369414 file .write (output )
370415 # If no file is specified, print to stdout
371416 else :
372- print (inspect_database (bolt_url = bolt_url ))
417+ print (
418+ inspect_database (
419+ bolt_url = bolt_url ,
420+ get_relationship_properties = args .get_relationship_properties ,
421+ infer_relationship_cardinality = args .infer_relationship_cardinality ,
422+ )
423+ )
373424
374425
375426if __name__ == "__main__" :
0 commit comments