@@ -80,6 +80,9 @@ def delete(self) -> None:
80
80
self .g .delete ()
81
81
82
82
83
+ def _query (self , q : str , params : dict ) -> QueryResult :
84
+ return self .g .query (q , params )
85
+
83
86
def get_sub_graph (self , l : int ) -> dict :
84
87
85
88
q = """MATCH (src)
@@ -89,7 +92,7 @@ def get_sub_graph(self, l: int) -> dict:
89
92
90
93
sub_graph = {'nodes' : [], 'edges' : [] }
91
94
92
- result_set = self .g . query (q , {'limit' : l }).result_set
95
+ result_set = self ._query (q , {'limit' : l }).result_set
93
96
for row in result_set :
94
97
src = row [0 ]
95
98
e = row [1 ]
@@ -137,7 +140,7 @@ def get_neighbors(self, node_id: int, rel: Optional[str] = None, lbl: Optional[s
137
140
138
141
try :
139
142
# Execute the graph query with node_id parameter
140
- result_set = self .g . query (query , {'node_id' : node_id }).result_set
143
+ result_set = self ._query (query , {'node_id' : node_id }).result_set
141
144
142
145
# Iterate over the result set and process nodes and edges
143
146
for edge , destination_node in result_set :
@@ -172,7 +175,7 @@ def add_class(self, c: Class) -> None:
172
175
'src_end' : c .src_end ,
173
176
}
174
177
175
- res = self .g . query (q , params )
178
+ res = self ._query (q , params )
176
179
c .id = res .result_set [0 ][0 ]
177
180
178
181
def _class_from_node (self , n : Node ) -> Class :
@@ -193,7 +196,7 @@ def _class_from_node(self, n: Node) -> Class:
193
196
194
197
def get_class_by_name (self , class_name : str ) -> Optional [Class ]:
195
198
q = "MATCH (c:Class) WHERE c.name = $name RETURN c LIMIT 1"
196
- res = self .g . query (q , {'name' : class_name }).result_set
199
+ res = self ._query (q , {'name' : class_name }).result_set
197
200
198
201
if len (res ) == 0 :
199
202
return None
@@ -205,7 +208,7 @@ def get_class(self, class_id: int) -> Optional[Class]:
205
208
WHERE ID(c) = $class_id
206
209
RETURN c"""
207
210
208
- res = self .g . query (q , {'class_id' : class_id })
211
+ res = self ._query (q , {'class_id' : class_id })
209
212
210
213
if len (res .result_set ) == 0 :
211
214
return None
@@ -239,7 +242,7 @@ def add_function(self, func: Function) -> None:
239
242
'ret_type' : func .ret_type
240
243
}
241
244
242
- res = self .g . query (q , params )
245
+ res = self ._query (q , params )
243
246
func .id = res .result_set [0 ][0 ]
244
247
245
248
def _function_from_node (self , n : Node ) -> Function :
@@ -280,7 +283,7 @@ def set_functions_metadata(self, ids: List[int], metadata: List[dict]) -> None:
280
283
281
284
params = {'ids' : ids , 'values' : metadata }
282
285
283
- self .g . query (q , params )
286
+ self ._query (q , params )
284
287
285
288
# get all functions defined by file
286
289
def get_functions_in_file (self , path : str , name : str , ext : str ) -> List [Function ]:
@@ -289,13 +292,13 @@ def get_functions_in_file(self, path: str, name: str, ext: str) -> List[Function
289
292
RETURN collect(func)"""
290
293
291
294
params = {'path' : path , 'name' : name , 'ext' : ext }
292
- funcs = self .g . query (q , params ).result_set [0 ][0 ]
295
+ funcs = self ._query (q , params ).result_set [0 ][0 ]
293
296
294
297
return [self ._function_from_node (n ) for n in funcs ]
295
298
296
299
def get_function_by_name (self , name : str ) -> Optional [Function ]:
297
300
q = "MATCH (f:Function) WHERE f.name = $name RETURN f LIMIT 1"
298
- res = self .g . query (q , {'name' : name }).result_set
301
+ res = self ._query (q , {'name' : name }).result_set
299
302
300
303
if len (res ) == 0 :
301
304
return None
@@ -332,7 +335,7 @@ def prefix_search(self, prefix: str) -> str:
332
335
333
336
try :
334
337
# Execute the query using the provided graph database connection.
335
- completions = self .g . query (query , {'prefix' : search_prefix }).result_set [0 ][0 ]
338
+ completions = self ._query (query , {'prefix' : search_prefix }).result_set [0 ][0 ]
336
339
337
340
# Remove label Searchable from each node
338
341
for node in completions :
@@ -354,7 +357,7 @@ def get_function(self, func_id: int) -> Optional[Function]:
354
357
WHERE ID(f) = $func_id
355
358
RETURN f"""
356
359
357
- res = self .g . query (q , {'func_id' : func_id })
360
+ res = self ._query (q , {'func_id' : func_id })
358
361
359
362
if len (res .result_set ) == 0 :
360
363
return None
@@ -369,7 +372,7 @@ def function_calls(self, func_id: int) -> List[Function]:
369
372
MATCH (f)-[:CALLS]->(callee)
370
373
RETURN callee"""
371
374
372
- res = self .g . query (q , {'func_id' : func_id })
375
+ res = self ._query (q , {'func_id' : func_id })
373
376
374
377
callees = []
375
378
for row in res .result_set :
@@ -384,7 +387,7 @@ def function_called_by(self, func_id: int) -> List[Function]:
384
387
MATCH (caller)-[:CALLS]->(f)
385
388
RETURN caller"""
386
389
387
- res = self .g . query (q , {'func_id' : func_id })
390
+ res = self ._query (q , {'func_id' : func_id })
388
391
389
392
callers = []
390
393
for row in res .result_set :
@@ -407,7 +410,7 @@ def add_file(self, file: File) -> None:
407
410
RETURN ID(f)"""
408
411
params = {'path' : file .path , 'name' : file .name , 'ext' : file .ext }
409
412
410
- res = self .g . query (q , params )
413
+ res = self ._query (q , params )
411
414
file .id = res .result_set [0 ][0 ]
412
415
413
416
def delete_files (self , files : List [dict ], log : bool = False ) -> tuple [str , dict , List [int ]]:
@@ -432,7 +435,7 @@ def delete_files(self, files: List[dict], log: bool = False) -> tuple[str, dict,
432
435
"""
433
436
434
437
params = {'files' : files }
435
- res = self .g . query (q , params )
438
+ res = self ._query (q , params )
436
439
437
440
if log and (res .relationships_deleted > 0 or res .nodes_deleted > 0 ):
438
441
return (q , params )
@@ -464,7 +467,7 @@ def get_file(self, path: str, name: str, ext: str) -> Optional[File]:
464
467
RETURN f"""
465
468
params = {'path' : path , 'name' : name , 'ext' : ext }
466
469
467
- res = self .g . query (q , params )
470
+ res = self ._query (q , params )
468
471
if (len (res .result_set ) == 0 ):
469
472
return None
470
473
@@ -491,7 +494,7 @@ def set_file_coverage(self, path: str, name: str, ext: str, coverage: float) ->
491
494
492
495
params = {'path' : path , 'name' : name , 'ext' : ext , 'coverage' : coverage }
493
496
494
- res = self .g . query (q , params )
497
+ res = self ._query (q , params )
495
498
496
499
def connect_entities (self , relation : str , src_id : int , dest_id : int ) -> None :
497
500
"""
@@ -507,7 +510,7 @@ def connect_entities(self, relation: str, src_id: int, dest_id: int) -> None:
507
510
MERGE (src)-[:{ relation } ]->(dest)"""
508
511
509
512
params = {'src_id' : src_id , 'dest_id' : dest_id }
510
- self .g . query (q , params )
513
+ self ._query (q , params )
511
514
512
515
def function_calls_function (self , caller_id : int , callee_id : int , pos : int ) -> None :
513
516
"""
@@ -524,7 +527,7 @@ def function_calls_function(self, caller_id: int, callee_id: int, pos: int) -> N
524
527
MERGE (caller)-[e:CALLS {pos:$pos}]->(callee)"""
525
528
526
529
params = {'caller_id' : caller_id , 'callee_id' : callee_id , 'pos' : pos }
527
- self .g . query (q , params )
530
+ self ._query (q , params )
528
531
529
532
def add_struct (self , s : Struct ) -> None :
530
533
"""
@@ -548,7 +551,7 @@ def add_struct(self, s: Struct) -> None:
548
551
'fields' : s .fields
549
552
}
550
553
551
- res = self .g . query (q , params )
554
+ res = self ._query (q , params )
552
555
s .id = res .result_set [0 ][0 ]
553
556
554
557
def _struct_from_node (self , n : Node ) -> Struct :
@@ -578,7 +581,7 @@ def _struct_from_node(self, n: Node) -> Struct:
578
581
579
582
def get_struct_by_name (self , struct_name : str ) -> Optional [Struct ]:
580
583
q = "MATCH (s:Struct) WHERE s.name = $name RETURN s LIMIT 1"
581
- res = self .g . query (q , {'name' : struct_name }).result_set
584
+ res = self ._query (q , {'name' : struct_name }).result_set
582
585
583
586
if len (res ) == 0 :
584
587
return None
@@ -590,7 +593,7 @@ def get_struct(self, struct_id: int) -> Optional[Struct]:
590
593
WHERE ID(s) = $struct_id
591
594
RETURN s"""
592
595
593
- res = self .g . query (q , {'struct_id' : struct_id })
596
+ res = self ._query (q , {'struct_id' : struct_id })
594
597
595
598
if len (res .result_set ) == 0 :
596
599
return None
@@ -617,7 +620,7 @@ def rerun_query(self, q: str, params: dict) -> QueryResult:
617
620
Re-run a query to transition the graph from one state to another
618
621
"""
619
622
620
- return self .g . query (q , params )
623
+ return self ._query (q , params )
621
624
622
625
def find_paths (self , src : int , dest : int ) -> List [Path ]:
623
626
"""
@@ -644,7 +647,7 @@ def find_paths(self, src: int, dest: int) -> List[Path]:
644
647
"""
645
648
646
649
# Perform the query with the source and destination node IDs.
647
- result_set = self .g . query (q , {'src_id' : src , 'dest_id' : dest }).result_set
650
+ result_set = self ._query (q , {'src_id' : src , 'dest_id' : dest }).result_set
648
651
649
652
paths = []
650
653
@@ -665,11 +668,29 @@ def stats(self) -> dict:
665
668
"""
666
669
667
670
q = "MATCH (n) RETURN count(n)"
668
- node_count = self .g . query (q ).result_set [0 ][0 ]
671
+ node_count = self ._query (q ).result_set [0 ][0 ]
669
672
670
673
q = "MATCH ()-[e]->() RETURN count(e)"
671
- edge_count = self .g . query (q ).result_set [0 ][0 ]
674
+ edge_count = self ._query (q ).result_set [0 ][0 ]
672
675
673
676
# Return the statistics
674
677
return {'node_count' : node_count , 'edge_count' : edge_count }
675
678
679
+ def unreachable_entities (self , lbl : Optional [str ], rel : Optional [str ]) -> List [dict ]:
680
+ lbl = f": { lbl } " if lbl else ""
681
+ rel = f": { rel } " if rel else ""
682
+
683
+ q = f""" MATCH (n { lbl } )
684
+ WHERE not ()-[{ rel } ]->(n)
685
+ RETURN n
686
+ """
687
+
688
+ result_set = self ._query (q ).result_set
689
+
690
+ unreachables = []
691
+ for row in result_set :
692
+ node = row [0 ]
693
+ unreachables .append (encode_node (node ))
694
+
695
+ return unreachables
696
+
0 commit comments