Remove orphaned nodes and relationships from the database.
(context: Optional[str] = None)
| 554 | |
| 555 | |
| 556 | def clean_helper(context: Optional[str] = None): |
| 557 | """Remove orphaned nodes and relationships from the database.""" |
| 558 | services = _initialize_services(context) |
| 559 | if not all(services[:3]): |
| 560 | return |
| 561 | |
| 562 | db_manager, _, _, ctx = services |
| 563 | |
| 564 | console.print("[cyan]🧹 Cleaning database (removing orphaned nodes)...[/cyan]") |
| 565 | |
| 566 | try: |
| 567 | total_deleted = 0 |
| 568 | batch_size = 500 |
| 569 | |
| 570 | with db_manager.get_driver().session() as session: |
| 571 | # Layer-by-layer deletion: iteratively delete nodes that lost |
| 572 | # their CONTAINS parent. Each pass peels one layer of the |
| 573 | # Repository → File → Class/Function → Variable hierarchy. |
| 574 | while True: |
| 575 | result = session.run(""" |
| 576 | MATCH (n) |
| 577 | WHERE NOT n:Repository |
| 578 | AND NOT ()-[:CONTAINS]->(n) |
| 579 | WITH n LIMIT $batch_size |
| 580 | DETACH DELETE n |
| 581 | RETURN count(n) as deleted |
| 582 | """, batch_size=batch_size) |
| 583 | record = result.single() |
| 584 | deleted_count = record["deleted"] if record else 0 |
| 585 | total_deleted += deleted_count |
| 586 | |
| 587 | if deleted_count == 0: |
| 588 | break |
| 589 | |
| 590 | console.print(f"[dim] Deleted {deleted_count} orphaned nodes (batch)...[/dim]") |
| 591 | |
| 592 | if total_deleted > 0: |
| 593 | console.print(f"[green]✓[/green] Deleted {total_deleted} orphaned nodes total") |
| 594 | else: |
| 595 | console.print("[green]✓[/green] No orphaned nodes found") |
| 596 | |
| 597 | console.print("[green]✅ Database cleanup complete![/green]") |
| 598 | except Exception as e: |
| 599 | console.print(f"[bold red]An error occurred during cleanup:[/bold red] {e}") |
| 600 | finally: |
| 601 | db_manager.close_driver() |
| 602 | |
| 603 | |
| 604 | def stats_helper(path: str = None, context: Optional[str] = None): |
no test coverage detected