@@ -1818,14 +1818,14 @@ def get_by_metadata(
18181818 raise ValueError (f"Unsupported operator: { op } " )
18191819
18201820 # Build user_name filter with knowledgebase_ids support (OR relationship) using common method
1821- user_name_conditions = []
1822- if user_name_flag :
1823- user_name_conditions = self ._build_user_name_and_kb_ids_conditions_cypher (
1824- user_name = user_name ,
1825- knowledgebase_ids = knowledgebase_ids ,
1826- default_user_name = self ._get_config_value ("user_name" ),
1827- )
1828- print (f"[get_by_metadata ] user_name_conditions: { user_name_conditions } " )
1821+ # Build user_name filter with knowledgebase_ids support (OR relationship) using common method
1822+ # Build user_name filter with knowledgebase_ids support (OR relationship) using common method
1823+ user_name_conditions = self ._build_user_name_and_kb_ids_conditions_cypher (
1824+ user_name = user_name ,
1825+ knowledgebase_ids = knowledgebase_ids ,
1826+ default_user_name = self ._get_config_value ("user_name" ),
1827+ )
1828+ print (f"[111get_by_metadata ] user_name_conditions: { user_name_conditions } " )
18291829
18301830 # Add user_name WHERE clause
18311831 if user_name_conditions :
@@ -1837,26 +1837,16 @@ def get_by_metadata(
18371837 # Build filter conditions using common method
18381838 filter_where_clause = self ._build_filter_conditions_cypher (filter )
18391839
1840- # Build WHERE clause: if where_conditions is empty, filter_where_clause should not have " AND " prefix
1841- if where_conditions :
1842- where_str = " AND " .join (where_conditions ) + filter_where_clause
1843- else :
1844- # If no other conditions, remove " AND " prefix from filter_where_clause if present
1845- if filter_where_clause .startswith (" AND " ):
1846- where_str = filter_where_clause [5 :] # Remove " AND " prefix
1847- else :
1848- where_str = filter_where_clause
1840+ where_str = " AND " .join (where_conditions ) + filter_where_clause
18491841
18501842 # Use cypher query
1851- # Only include WHERE clause if where_str is not empty
1852- where_clause = f"WHERE { where_str } " if where_str else ""
18531843 cypher_query = f"""
1854- SELECT * FROM cypher('{ self .db_name } _graph', $$
1855- MATCH (n:Memory)
1856- { where_clause }
1857- RETURN n.id AS id
1858- $$) AS (id agtype)
1859- """
1844+ SELECT * FROM cypher('{ self .db_name } _graph', $$
1845+ MATCH (n:Memory)
1846+ WHERE { where_str }
1847+ RETURN n.id AS id
1848+ $$) AS (id agtype)
1849+ """
18601850
18611851 ids = []
18621852 conn = self ._get_connection ()
@@ -4008,6 +3998,7 @@ def process_condition(condition):
40083998 @timed
40093999 def delete_node_by_prams (
40104000 self ,
4001+ writable_cube_ids : list [str ],
40114002 memory_ids : list [str ] | None = None ,
40124003 file_ids : list [str ] | None = None ,
40134004 filter : dict | None = None ,
@@ -4016,61 +4007,102 @@ def delete_node_by_prams(
40164007 Delete nodes by memory_ids, file_ids, or filter.
40174008
40184009 Args:
4010+ writable_cube_ids (list[str]): List of cube IDs (user_name) to filter nodes. Required parameter.
40194011 memory_ids (list[str], optional): List of memory node IDs to delete.
40204012 file_ids (list[str], optional): List of file node IDs to delete.
40214013 filter (dict, optional): Filter dictionary to query matching nodes for deletion.
40224014
40234015 Returns:
40244016 int: Number of nodes deleted.
40254017 """
4026- # Collect all node IDs to delete
4027- ids_to_delete = set ()
4018+ logger .info (
4019+ f"[delete_node_by_prams] memory_ids: { memory_ids } , file_ids: { file_ids } , filter: { filter } , writable_cube_ids: { writable_cube_ids } "
4020+ )
4021+ print (
4022+ f"[delete_node_by_prams] memory_ids: { memory_ids } , file_ids: { file_ids } , filter: { filter } , writable_cube_ids: { writable_cube_ids } "
4023+ )
40284024
4029- # Add memory_ids if provided
4030- if memory_ids and len (memory_ids ) > 0 :
4031- ids_to_delete .update (memory_ids )
4025+ # Validate writable_cube_ids
4026+ if not writable_cube_ids or len (writable_cube_ids ) == 0 :
4027+ raise ValueError ("writable_cube_ids is required and cannot be empty" )
4028+
4029+ # Build user_name condition from writable_cube_ids (OR relationship - match any cube_id)
4030+ user_name_conditions = []
4031+ for cube_id in writable_cube_ids :
4032+ # Escape single quotes in cube IDs
4033+ escaped_cube_id = str (cube_id ).replace ("'" , "\\ '" )
4034+ user_name_conditions .append (f"n.user_name = '{ escaped_cube_id } '" )
40324035
4033- # Add file_ids if provided (treating them as node IDs)
4036+ # Build WHERE conditions separately for memory_ids and file_ids
4037+ where_conditions = []
4038+
4039+ # Handle memory_ids: query n.id
4040+ if memory_ids and len (memory_ids ) > 0 :
4041+ memory_id_conditions = []
4042+ for node_id in memory_ids :
4043+ # Escape single quotes in node IDs
4044+ escaped_id = str (node_id ).replace ("'" , "\\ '" )
4045+ memory_id_conditions .append (f"'{ escaped_id } '" )
4046+ if memory_id_conditions :
4047+ where_conditions .append (f"n.id IN [{ ', ' .join (memory_id_conditions )} ]" )
4048+
4049+ # Handle file_ids: query n.file_ids field
4050+ # All file_ids must be present in the array field (AND relationship)
40344051 if file_ids and len (file_ids ) > 0 :
4035- ids_to_delete .update (file_ids )
4052+ file_id_and_conditions = []
4053+ for file_id in file_ids :
4054+ # Escape single quotes in file IDs
4055+ escaped_id = str (file_id ).replace ("'" , "\\ '" )
4056+ # Check if this file_id is in the file_ids array field
4057+ file_id_and_conditions .append (f"'{ escaped_id } ' IN n.file_ids" )
4058+ if file_id_and_conditions :
4059+ # Use AND to require all file_ids to be present
4060+ where_conditions .append (f"({ ' AND ' .join (file_id_and_conditions )} )" )
40364061
40374062 # Query nodes by filter if provided
4063+ filter_ids = set ()
40384064 if filter :
40394065 # Parse filter to validate and transform field names (e.g., add "info." prefix if needed)
40404066 parsed_filter = self .parse_filter (filter )
40414067 if parsed_filter :
40424068 # Use get_by_metadata with empty filters list and parsed filter
4043- filter_ids = self .get_by_metadata (
4044- filters = [],
4045- user_name = None ,
4046- filter = parsed_filter ,
4047- knowledgebase_ids = None ,
4048- user_name_flag = False ,
4069+ filter_ids = set (
4070+ self .get_by_metadata (
4071+ filters = [],
4072+ user_name = None ,
4073+ filter = parsed_filter ,
4074+ knowledgebase_ids = writable_cube_ids ,
4075+ )
40494076 )
4050- ids_to_delete .update (filter_ids )
40514077 else :
40524078 logger .warning (
40534079 "[delete_node_by_prams] Filter parsed to None, skipping filter query"
40544080 )
40554081
4056- # If no IDs to delete, return 0
4057- if not ids_to_delete :
4058- logger .warning ("[delete_node_by_prams] No nodes to delete" )
4082+ # If filter returned IDs, add condition for them
4083+ if filter_ids :
4084+ filter_id_conditions = []
4085+ for node_id in filter_ids :
4086+ # Escape single quotes in node IDs
4087+ escaped_id = str (node_id ).replace ("'" , "\\ '" )
4088+ filter_id_conditions .append (f"'{ escaped_id } '" )
4089+ if filter_id_conditions :
4090+ where_conditions .append (f"n.id IN [{ ', ' .join (filter_id_conditions )} ]" )
4091+
4092+ # If no conditions (except user_name), return 0
4093+ if not where_conditions :
4094+ logger .warning (
4095+ "[delete_node_by_prams] No nodes to delete (no memory_ids, file_ids, or filter provided)"
4096+ )
40594097 return 0
40604098
4061- # Convert to list for easier handling
4062- ids_list = list (ids_to_delete )
4063- logger .info (f"[delete_node_by_prams] Deleting { len (ids_list )} nodes: { ids_list } " )
4064-
4065- # Build WHERE condition for collected IDs (query n.id)
4066- id_conditions = []
4067- for node_id in ids_list :
4068- # Escape single quotes in node IDs
4069- escaped_id = str (node_id ).replace ("'" , "\\ '" )
4070- id_conditions .append (f"'{ escaped_id } '" )
4099+ # Build WHERE clause
4100+ # First, combine memory_ids, file_ids, and filter conditions with OR (any condition can match)
4101+ data_conditions = " OR " .join ([f"({ cond } )" for cond in where_conditions ])
40714102
4072- # Build WHERE clause for IDs
4073- ids_where = f"n.id IN [{ ', ' .join (id_conditions )} ]"
4103+ # Then, combine with user_name condition using AND (must match user_name AND one of the data conditions)
4104+ user_name_where = " OR " .join (user_name_conditions )
4105+ ids_where = f"({ user_name_where } ) AND ({ data_conditions } )"
40744106
40754107 # Use Cypher DELETE query
40764108 # First count matching nodes to get accurate count
@@ -4093,13 +4125,11 @@ def delete_node_by_prams(
40934125 $$) AS (result agtype)
40944126 """
40954127
4096- # Calculate total count for logging
4097- total_count = len (ids_list )
40984128 logger .info (
40994129 f"[delete_node_by_prams] Deleting nodes - memory_ids: { memory_ids } , file_ids: { file_ids } , filter: { filter } "
41004130 )
41014131 print (
4102- f"[delete_node_by_prams] Deleting { total_count } nodes - memory_ids: { memory_ids } , file_ids: { file_ids } , filter: { filter } "
4132+ f"[delete_node_by_prams] Deleting nodes - memory_ids: { memory_ids } , file_ids: { file_ids } , filter: { filter } "
41034133 )
41044134 logger .info (f"[delete_node_by_prams] delete_query: { delete_query } " )
41054135 print (f"[delete_node_by_prams] delete_query: { delete_query } " )
@@ -4111,11 +4141,11 @@ def delete_node_by_prams(
41114141 # Count nodes before deletion
41124142 cursor .execute (count_query )
41134143 count_results = cursor .fetchall ()
4114- expected_count = total_count
4144+ expected_count = 0
41154145 if count_results and len (count_results ) > 0 :
41164146 count_str = str (count_results [0 ][0 ])
41174147 count_str = count_str .strip ('"' ).strip ("'" )
4118- expected_count = int (count_str ) if count_str .isdigit () else total_count
4148+ expected_count = int (count_str ) if count_str .isdigit () else 0
41194149
41204150 # Delete nodes
41214151 cursor .execute (delete_query )
0 commit comments