Skip to content

Commit

Permalink
Merge pull request #89 from singnet/feature/issue-84-add-delete-atom
Browse files Browse the repository at this point in the history
[#84 ] Add delete_atom method in the InMemoryDB adapter
  • Loading branch information
marcocapozzoli authored Feb 2, 2024
2 parents 5a04c9a + 1c27e11 commit 0dde042
Show file tree
Hide file tree
Showing 6 changed files with 468 additions and 41 deletions.
1 change: 1 addition & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
[#46 #48] Add error log message to adapters and Implement get_all_links()
[#86] Implement reindex() in all AtomDB adapters
[HOTFIX] Add 'type' in documents returned by get_atom()
[#84 ] Add delete_atom method in the InMemoryDB adapter
157 changes: 119 additions & 38 deletions hyperon_das_atomdb/adapters/ram_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ def __init__(self, database_name: str = 'das') -> None:
node={},
link=Link(arity_1={}, arity_2={}, arity_n={}),
outgoing_set={},
incomming_set={},
incoming_set={},
patterns={},
templates={},
)
Expand All @@ -40,12 +40,25 @@ def _get_link(self, handle: str) -> Optional[Dict[str, Any]]:
return link
return None

def _get_and_delete_link(self, link_handle: str) -> Dict[str, Any]:
for link_table in self.db.link.all_tables():
if link_table:
link_document = link_table.pop(link_handle, None)
if link_document:
return link_document

def _build_named_type_hash_template(self, template: Union[str, List[Any]]) -> List[Any]:
if isinstance(template, str):
return ExpressionHasher.named_type_hash(template)
else:
return [self._build_named_type_hash_template(element) for element in template]

def _build_atom_type_key_hash(self, _name: str) -> str:
name_hash = ExpressionHasher.named_type_hash(_name)
type_hash = ExpressionHasher.named_type_hash('Type')
typedef_mark_hash = ExpressionHasher.named_type_hash(":")
return ExpressionHasher.expression_hash(typedef_mark_hash, [name_hash, type_hash])

def _add_atom_type(self, _name: str, _type: Optional[str] = 'Type'):
if _name in self.all_named_types:
return
Expand All @@ -71,53 +84,77 @@ def _add_atom_type(self, _name: str, _type: Optional[str] = 'Type'):
self.db.atom_type[key] = atom_type
self.named_type_table[name_hash] = _name

def _delete_atom_type(self, _name: str) -> None:
key = self._build_atom_type_key_hash(_name)
self.db.atom_type.pop(key, None)
self.all_named_types.remove(_name)

def _add_outgoing_set(self, key: str, targets_hash: Dict[str, Any]) -> None:
self.db.outgoing_set[key] = targets_hash

def _add_incomming_set(self, key: str, targets_hash: Dict[str, Any]) -> None:
def _get_and_delete_outgoing_set(self, handle: str) -> List[str]:
return self.db.outgoing_set.pop(handle, None)

def _add_incoming_set(self, key: str, targets_hash: Dict[str, Any]) -> None:
for target_hash in targets_hash:
incomming_set = self.db.incomming_set.get(target_hash)
if incomming_set is None:
self.db.incomming_set[target_hash] = [key]
incoming_set = self.db.incoming_set.get(target_hash)
if incoming_set is None:
self.db.incoming_set[target_hash] = [key]
else:
self.db.incomming_set[target_hash].append(key)
self.db.incoming_set[target_hash].append(key)

def _delete_incoming_set(self, link_handle: str, atoms_handle: List[str]) -> None:
for atom_handle in atoms_handle:
handles = self.db.incoming_set.get(atom_handle, [])
if len(handles) > 0:
handles.remove(link_handle)

def _add_templates(
self,
composite_type_hash: str,
named_type_hash: str,
key: str,
targets_hash: List[str],
self, composite_type_hash: str, named_type_hash: str, key: str, targets_hash: List[str]
) -> None:
template_composite_type_hash = self.db.templates.get(composite_type_hash)
template_named_type_hash = self.db.templates.get(named_type_hash)

if template_composite_type_hash is not None:
# template_composite_type_hash.append([key, targets_hash])
template_composite_type_hash.append((key, tuple(targets_hash)))
else:
# self.db.templates[composite_type_hash] = [[key, targets_hash]]
self.db.templates[composite_type_hash] = [(key, tuple(targets_hash))]

if template_named_type_hash is not None:
# template_named_type_hash.append([key, targets_hash])
template_named_type_hash.append((key, tuple(targets_hash)))
else:
# self.db.templates[named_type_hash] = [[key, targets_hash]]
self.db.templates[named_type_hash] = [(key, tuple(targets_hash))]

def _delete_templates(self, link_document: dict, targets_hash: List[str]) -> None:
template_composite_type = self.db.templates.get(link_document['composite_type_hash'], [])
if len(template_composite_type) > 0:
template_composite_type.remove((link_document['_id'], tuple(targets_hash)))

template_named_type = self.db.templates.get(link_document['named_type_hash'], [])
if len(template_named_type) > 0:
template_named_type.remove((link_document['_id'], tuple(targets_hash)))

def _add_patterns(self, named_type_hash: str, key: str, targets_hash: List[str]):
pattern_keys = build_patern_keys([named_type_hash, *targets_hash])

for pattern_key in pattern_keys:
pattern_key_hash = self.db.patterns.get(pattern_key)
if pattern_key_hash is not None:
# pattern_key_hash.append([key, targets_hash])
pattern_key_hash.append((key, tuple(targets_hash)))
else:
# self.db.patterns[pattern_key] = [[key, targets_hash]]
self.db.patterns[pattern_key] = [(key, tuple(targets_hash))]

def _delete_patterns(self, link_document: dict, targets_hash: List[str]) -> None:
pattern_keys = build_patern_keys([link_document['named_type_hash'], *targets_hash])
for pattern_key in pattern_keys:
pattern = self.db.patterns.get(pattern_key, [])
if len(pattern) > 0:
pattern.remove((link_document['_id'], tuple(targets_hash)))

def _delete_link_and_update_index(self, link_handle: str) -> None:
link_document = self._get_and_delete_link(link_handle)
self._update_index(atom=link_document, delete_atom=True)

def _filter_non_toplevel(self, matches: list) -> list:
matches_toplevel_only = []
for match in matches:
Expand All @@ -138,26 +175,49 @@ def _build_targets_list(self, link: Dict[str, Any]):
count += 1
return targets

def _update_index(self, atom: Dict[str, Any]):
atom_type = atom['named_type']
self._add_atom_type(_name=atom_type)
if 'name' not in atom:
handle = atom['_id']
def _update_index(self, atom: Dict[str, Any], **kwargs):
if kwargs.get('delete_atom', False):
if atom is None:
raise LinkDoesNotExist('link not exist')

link_handle = atom['_id']

handles = self.db.incoming_set.pop(link_handle, None)

if handles:
for handle in handles:
self._delete_link_and_update_index(handle)

outgoing_atoms = self._get_and_delete_outgoing_set(link_handle)

if outgoing_atoms:
self._delete_incoming_set(link_handle, outgoing_atoms)

targets_hash = self._build_targets_list(atom)

self._delete_templates(atom, targets_hash)

self._delete_patterns(atom, targets_hash)
else:
atom_type = atom['named_type']
self._add_atom_type(_name=atom_type)
self._add_outgoing_set(handle, targets_hash)
self._add_incomming_set(handle, targets_hash)
self._add_templates(
atom['composite_type_hash'],
atom['named_type_hash'],
handle,
targets_hash,
)
self._add_patterns(
atom['named_type_hash'],
handle,
targets_hash,
)
if 'name' not in atom:
handle = atom['_id']
targets_hash = self._build_targets_list(atom)
self._add_atom_type(_name=atom_type)
self._add_outgoing_set(handle, targets_hash)
self._add_incoming_set(handle, targets_hash)
self._add_templates(
atom['composite_type_hash'],
atom['named_type_hash'],
handle,
targets_hash,
)
self._add_patterns(
atom['named_type_hash'],
handle,
targets_hash,
)

def get_node_handle(self, node_type: str, node_name: str) -> str:
node_handle = self.node_handle(node_type, node_name)
Expand Down Expand Up @@ -319,7 +379,7 @@ def get_matched_links(
def get_incoming_links(
self, atom_handle: str, **kwargs
) -> List[Union[Tuple[Dict[str, Any], List[Dict[str, Any]]], Dict[str, Any]]]:
links = self.db.incomming_set.get(atom_handle)
links = self.db.incoming_set.get(atom_handle)

if not links:
return []
Expand Down Expand Up @@ -411,15 +471,15 @@ def count_atoms(self) -> Tuple[int, int]:

def clear_database(self) -> None:
self.named_type_table = {}
self.all_named_types = set()
self.db = Database(
atom_type={},
node={},
link=Link(arity_1={}, arity_2={}, arity_n={}),
outgoing_set={},
incomming_set={},
incoming_set={},
patterns={},
templates={},
names={},
)

def add_node(self, node_params: Dict[str, Any]) -> Dict[str, Any]:
Expand All @@ -437,3 +497,24 @@ def add_link(self, link_params: Dict[str, Any], toplevel: bool = True) -> Dict[s

def reindex(self, pattern_index_templates: Optional[Dict[str, Dict[str, Any]]]):
raise NotImplementedError()

def delete_atom(self, handle: str, **kwargs) -> None:
node = self.db.node.pop(handle, None)

if node:
handles = self.db.incoming_set.pop(handle, [])

if handles:
for handle in handles:
self._delete_link_and_update_index(handle)
else:
try:
self._delete_link_and_update_index(handle)
except LinkDoesNotExist:
logger().error(
f'Failed to delete atom for handle: {handle}. This atom may not exist. - Details: {kwargs}'
)
raise AtomDoesNotExist(
message='This atom does not exist',
details=f'handle: {handle}',
)
3 changes: 3 additions & 0 deletions hyperon_das_atomdb/adapters/redis_mongo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,9 @@ def add_link(self, link_params: Dict[str, Any], toplevel: bool = True) -> Dict[s
self.commit()
return link

def delete_atom(self, handle: str, **kwargs) -> None:
pass

def _apply_index_template(
self, template: Dict[str, Any], named_type: str, targets: List[str], arity
) -> List[List[str]]:
Expand Down
9 changes: 9 additions & 0 deletions hyperon_das_atomdb/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -573,3 +573,12 @@ def reindex(self, pattern_index_templates: Optional[Dict[str, Dict[str, Any]]] =
Similarity(handle1, *)
"""
... # pragma no cover

@abstractmethod
def delete_atom(self, handle: str, **kwargs) -> None:
"""Delete a atom to the database
Args:
handle (str): Atom handle
"""
... # pragma no cover
2 changes: 1 addition & 1 deletion hyperon_das_atomdb/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ class Database:
node: Dict[str, Any]
link: Link
outgoing_set: Dict[str, Any]
incomming_set: Dict[str, Any]
incoming_set: Dict[str, Any]
patterns: Dict[str, List[Tuple]]
templates: Dict[str, List[Tuple]]
Loading

0 comments on commit 0dde042

Please sign in to comment.