Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions graphiti_core/driver/falkordb/operations/graph_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,24 @@ async def get_community_clusters(
async def remove_communities(
self,
executor: QueryExecutor,
group_ids: list[str] | None = None,
) -> None:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)
if group_ids:
await executor.execute_query(
"""
MATCH (c:Community)
WHERE c.group_id IN $group_ids
DETACH DELETE c
""",
group_ids=group_ids,
)
else:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)

async def determine_entity_community(
self,
Expand Down
8 changes: 6 additions & 2 deletions graphiti_core/driver/graph_operations/graph_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -768,14 +768,18 @@ async def get_community_clusters(
async def remove_communities(
self,
driver: Any,
group_ids: list[str] | None = None,
) -> None:
"""
Delete all community nodes from the graph.
Delete community nodes from the graph.

This removes all Community-labeled nodes and their relationships.
This removes Community-labeled nodes and their relationships. When
group_ids is provided, only communities in those groups are removed;
otherwise all communities are removed.

Args:
driver: GraphDriver instance
group_ids: Optional list of group ids to scope the removal to
"""
raise NotImplementedError

Expand Down
23 changes: 17 additions & 6 deletions graphiti_core/driver/kuzu/operations/graph_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,24 @@ async def get_community_clusters(
async def remove_communities(
self,
executor: QueryExecutor,
group_ids: list[str] | None = None,
) -> None:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)
if group_ids:
await executor.execute_query(
"""
MATCH (c:Community)
WHERE c.group_id IN $group_ids
DETACH DELETE c
""",
group_ids=group_ids,
)
else:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)

async def determine_entity_community(
self,
Expand Down
23 changes: 17 additions & 6 deletions graphiti_core/driver/neo4j/operations/graph_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,13 +148,24 @@ async def get_community_clusters(
async def remove_communities(
self,
executor: QueryExecutor,
group_ids: list[str] | None = None,
) -> None:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)
if group_ids:
await executor.execute_query(
"""
MATCH (c:Community)
WHERE c.group_id IN $group_ids
DETACH DELETE c
""",
group_ids=group_ids,
)
else:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)

async def determine_entity_community(
self,
Expand Down
23 changes: 17 additions & 6 deletions graphiti_core/driver/neptune/operations/graph_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,13 +153,24 @@ async def get_community_clusters(
async def remove_communities(
self,
executor: QueryExecutor,
group_ids: list[str] | None = None,
) -> None:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)
if group_ids:
await executor.execute_query(
"""
MATCH (c:Community)
WHERE c.group_id IN $group_ids
DETACH DELETE c
""",
group_ids=group_ids,
)
else:
await executor.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)

async def determine_entity_community(
self,
Expand Down
1 change: 1 addition & 0 deletions graphiti_core/driver/operations/graph_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ async def get_community_clusters(
async def remove_communities(
self,
executor: QueryExecutor,
group_ids: list[str] | None = None,
) -> None: ...

@abstractmethod
Expand Down
4 changes: 2 additions & 2 deletions graphiti_core/graphiti.py
Original file line number Diff line number Diff line change
Expand Up @@ -1500,8 +1500,8 @@ async def build_communities(
if driver is None:
driver = self.clients.driver

# Clear existing communities
await remove_communities(driver)
# Clear existing communities (scoped: only the groups being rebuilt)
await remove_communities(driver, group_ids=group_ids)

community_nodes, community_edges = await build_communities(
driver, self.llm_client, group_ids
Expand Down
28 changes: 20 additions & 8 deletions graphiti_core/utils/maintenance/community_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,19 +241,31 @@ async def limited_build_community(cluster):
return community_nodes, community_edges


async def remove_communities(driver: GraphDriver):
async def remove_communities(driver: GraphDriver, group_ids: list[str] | None = None):
if driver.graph_operations_interface:
try:
return await driver.graph_operations_interface.remove_communities(driver)
return await driver.graph_operations_interface.remove_communities(
driver, group_ids=group_ids
)
except NotImplementedError:
pass

await driver.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)
if group_ids:
await driver.execute_query(
"""
MATCH (c:Community)
WHERE c.group_id IN $group_ids
DETACH DELETE c
""",
group_ids=group_ids,
)
else:
await driver.execute_query(
"""
MATCH (c:Community)
DETACH DELETE c
"""
)


async def determine_entity_community(
Expand Down
83 changes: 83 additions & 0 deletions tests/utils/maintenance/test_remove_communities.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
"""
Copyright 2024, Zep Software, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""

from unittest.mock import AsyncMock, MagicMock

import pytest

from graphiti_core.utils.maintenance.community_operations import remove_communities


def _mock_driver():
driver = MagicMock()
driver.graph_operations_interface = None
driver.execute_query = AsyncMock()
return driver


@pytest.mark.asyncio
async def test_remove_communities_unscoped_deletes_all():
driver = _mock_driver()

await remove_communities(driver)

driver.execute_query.assert_awaited_once()
query = driver.execute_query.await_args.args[0]
kwargs = driver.execute_query.await_args.kwargs
assert 'MATCH (c:Community)' in query
assert 'WHERE' not in query
assert 'group_ids' not in kwargs


@pytest.mark.asyncio
async def test_remove_communities_scoped_deletes_only_selected_groups():
driver = _mock_driver()

await remove_communities(driver, group_ids=['group_a', 'group_b'])

driver.execute_query.assert_awaited_once()
query = driver.execute_query.await_args.args[0]
kwargs = driver.execute_query.await_args.kwargs
assert 'WHERE c.group_id IN $group_ids' in query
assert kwargs['group_ids'] == ['group_a', 'group_b']


@pytest.mark.asyncio
async def test_remove_communities_empty_group_ids_deletes_all():
# group_ids=[] means "no scoping requested" (matches build_communities'
# None/blank semantics), not "delete nothing".
driver = _mock_driver()

await remove_communities(driver, group_ids=[])

driver.execute_query.assert_awaited_once()
query = driver.execute_query.await_args.args[0]
assert 'WHERE' not in query


@pytest.mark.asyncio
async def test_remove_communities_scoped_via_graph_operations_interface():
driver = MagicMock()
driver.graph_operations_interface = MagicMock()
driver.graph_operations_interface.remove_communities = AsyncMock()
driver.execute_query = AsyncMock()

await remove_communities(driver, group_ids=['group_a'])

driver.graph_operations_interface.remove_communities.assert_awaited_once_with(
driver, group_ids=['group_a']
)
driver.execute_query.assert_not_awaited()
Loading