Skip to content

Commit

Permalink
Merge pull request #65 from singnet/feature/query-engine-issue-64-add…
Browse files Browse the repository at this point in the history
…-traverseengine

[#62] Add get_incoming_links
  • Loading branch information
marcocapozzoli authored Jan 10, 2024
2 parents 45b6af5 + 43b3400 commit 4ba2063
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 21 deletions.
31 changes: 29 additions & 2 deletions hyperon_das_atomdb/adapters/ram_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,24 @@ def get_matched_links(

return patterns_matched

def get_incoming_links(
self, atom_handle: str, handles_only: bool = False
) -> List[Dict[str, Any]]:
links = self.db.incomming_set.get(atom_handle)

if not links:
return []

if handles_only:
return links

links_document = []
for handle in links:
document_atom = self.get_atom(handle, targets_type=True, targets_document=True)
links_document.append(document_atom)

return links_document

def get_matched_type_template(
self,
template: List[Any],
Expand All @@ -311,19 +329,28 @@ def get_matched_type(
return self._filter_non_toplevel(templates_matched)
return templates_matched

def get_atom(self, handle: str) -> Dict[str, Any]:
def get_atom(self, handle: str, **kwargs) -> Dict[str, Any]:
document = self.db.node.get(handle)
if document is None:
document = self._get_link(handle)
if document:
atom = self._convert_atom_format(document)
atom = self._convert_atom_format(document, **kwargs)
return atom
else:
raise AtomDoesNotExist(
message='This atom does not exist',
details=f'handle: {handle}',
)

def get_atom_type(self, handle: str) -> str:
atom = self.db.node.get(handle)

if atom is None:
atom = self._get_link(handle)

if atom is not None:
return atom['named_type']

def get_atom_as_dict(self, handle: str, arity: Optional[int] = 0) -> Dict[str, Any]:
atom = self.db.node.get(handle)
if atom is not None:
Expand Down
29 changes: 27 additions & 2 deletions hyperon_das_atomdb/adapters/redis_mongo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,26 @@ def get_matched_links(

return patterns_matched

def get_incoming_links(
self, atom_handle: str, handles_only: bool = False
) -> List[Dict[str, Any]]:
answer = self._retrieve_key_value(KeyPrefix.INCOMING_SET, atom_handle)

if not answer:
return []

links = [h.decode() for h in answer]

if handles_only:
return links

links_document = []
for handle in links:
document_atom = self.get_atom(handle, targets_type=True, targets_document=True)
links_document.append(document_atom)

return links_document

def get_matched_type_template(
self,
template: List[Any],
Expand Down Expand Up @@ -453,17 +473,22 @@ def get_link_type(self, link_handle: str) -> str:
document = self.get_atom(link_handle)
return document["named_type"]

def get_atom(self, handle: str) -> Dict[str, Any]:
def get_atom(self, handle: str, **kwargs) -> Dict[str, Any]:
document = self._retrieve_mongo_document(handle)
if document:
atom = self._convert_atom_format(document)
atom = self._convert_atom_format(document, **kwargs)
return atom
else:
raise AtomDoesNotExist(
message='This atom does not exist',
details=f'handle: {handle}',
)

def get_atom_type(self, handle: str) -> str:
atom = self._retrieve_mongo_document(handle)
if atom is not None:
return atom['named_type']

def get_atom_as_dict(self, handle, arity=-1) -> dict:
answer = {}
document = self._retrieve_mongo_document(handle, arity)
Expand Down
26 changes: 21 additions & 5 deletions hyperon_das_atomdb/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def link_handle(link_type: str, target_handles: List[str]) -> str:
named_type_hash = ExpressionHasher.named_type_hash(link_type)
return ExpressionHasher.expression_hash(named_type_hash, target_handles)

def _convert_atom_format(self, document: Dict[str, Any]) -> Dict[str, Any]:
def _convert_atom_format(self, document: Dict[str, Any], **kwargs) -> Dict[str, Any]:
answer = {'handle': document['_id']}

for key, value in document.items():
Expand All @@ -44,6 +44,12 @@ def _convert_atom_format(self, document: Dict[str, Any]) -> Dict[str, Any]:
else:
answer[key] = value

if kwargs.get('targets_document', False):
answer['targets_document'] = [self.get_atom(target) for target in answer['targets']]

if kwargs.get('targets_type', False):
answer['targets_type'] = [self.get_atom_type(target) for target in answer['targets']]

return answer

def _recursive_link_split(self, params: Dict[str, Any]) -> (str, Any):
Expand Down Expand Up @@ -318,6 +324,12 @@ def get_matched_links(self, link_type: str, target_handles: List[str]):
"""
... # pragma no cover

@abstractmethod
def get_incoming_links(
self, atom_handle: str, handles_only: bool = False
) -> List[Dict[str, Any]]:
... # pragma no cover

@abstractmethod
def get_matched_type_template(self, template: List[Any]) -> List[str]:
"""
Expand All @@ -344,6 +356,14 @@ def get_matched_type(self, link_type: str):
"""
... # pragma no cover

@abstractmethod
def get_atom(self, handle: str, **kwargs) -> Dict[str, Any]:
... # pragma no cover

@abstractmethod
def get_atom_type(self, handle: str) -> str:
... # pragma no cover

def get_atom_as_dict(self, handle: str, arity: int):
"""
Get an atom as a dictionary representation.
Expand Down Expand Up @@ -491,9 +511,5 @@ def add_link(self, link_params: Dict[str, Any], toplevel: bool = True) -> Dict[s
"""
... # pragma no cover

@abstractmethod
def get_atom(self, handle: str) -> Dict[str, Any]:
... # pragma no cover

def commit(self) -> None:
... # pragma no cover
28 changes: 28 additions & 0 deletions tests/unit/adapters/test_ram_only.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,3 +687,31 @@ def test_get_atom_as_dist(self, database: InMemoryDB):
atom = database.get_atom_as_dict(handle=s)
assert atom['handle'] == s
assert atom['targets'] == [h, m]

def test_get_incoming_links(self, database: InMemoryDB):
h = database.get_node_handle('Concept', 'human')
m = database.get_node_handle('Concept', 'monkey')
s = database.get_link_handle('Similarity', [h, m])

links = database.get_incoming_links(atom_handle=h, handles_only=False)
atom = database.get_atom(handle=s, targets_type=True, targets_document=True)
assert atom in links

links = database.get_incoming_links(atom_handle=h, handles_only=True)
assert links == database.db.incomming_set.get(h)
assert s in links

links = database.get_incoming_links(atom_handle=m, handles_only=True)
assert links == database.db.incomming_set.get(m)

links = database.get_incoming_links(atom_handle=s, handles_only=True)
assert links == []

def test_get_atom_type(self, database: InMemoryDB):
h = database.get_node_handle('Concept', 'human')
m = database.get_node_handle('Concept', 'mammal')
i = database.get_link_handle('Inheritance', [h, m])

assert 'Concept' == database.get_atom_type(h)
assert 'Concept' == database.get_atom_type(m)
assert 'Inheritance' == database.get_atom_type(i)
59 changes: 47 additions & 12 deletions tests/unit/adapters/test_redis_mongo_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -867,22 +867,22 @@
},
{
'incomming_set:1cdffc6b0b89ff41d68bec237481d1e1': [
'abe6ad743fc81bd1c55ece2e1307a178',
'31535ddf214f5b239d3b517823cb8144',
'2a8a69c01305563932b957de4b3a9ba6',
'bad7472f41a0e7d601ca294eb4607c3a',
'f31dfe97db782e8cec26de18dddf8965',
b'abe6ad743fc81bd1c55ece2e1307a178',
b'31535ddf214f5b239d3b517823cb8144',
b'2a8a69c01305563932b957de4b3a9ba6',
b'bad7472f41a0e7d601ca294eb4607c3a',
b'f31dfe97db782e8cec26de18dddf8965',
]
},
{
'incomming_set:af12f10f9ae2002a1607ba0b47ba8407': [
'c93e1e758c53912638438e2a7d7f7b7f',
'a45af31b43ee5ea271214338a5a5bd61',
'2c927fdc6c0f1272ee439ceb76a6d1a4',
'2a8a69c01305563932b957de4b3a9ba6',
'b5459e299a5c5e8662c427f7e01b3bf1',
'bad7472f41a0e7d601ca294eb4607c3a',
'16f7e407087bfa0b35b13d13a1aadcae',
b'c93e1e758c53912638438e2a7d7f7b7f',
b'a45af31b43ee5ea271214338a5a5bd61',
b'2c927fdc6c0f1272ee439ceb76a6d1a4',
b'2a8a69c01305563932b957de4b3a9ba6',
b'b5459e299a5c5e8662c427f7e01b3bf1',
b'bad7472f41a0e7d601ca294eb4607c3a',
b'16f7e407087bfa0b35b13d13a1aadcae',
]
},
{
Expand Down Expand Up @@ -2939,6 +2939,11 @@ def smembers(key: str):
if list(data.keys())[0] == key:
return list(data.values())[0]
return []
if 'incomming_set' in key:
for data in incomming_set_redis_mock_data:
if list(data.keys())[0] == key:
return list(data.values())[0]
return []
if 'patterns' in key:
value = patterns_redis_mock_data.get(key)
if value:
Expand Down Expand Up @@ -3516,3 +3521,33 @@ def test_add_node_and_link_with_reserved_parameters(self, database):
exc.value.details
== "['_id', 'composite_type_hash', 'is_toplevel', 'composite_type', 'named_type', 'named_type_hash', 'key_n']"
)

def test_get_incoming_links(self, database):
h = database.get_node_handle('Concept', 'human')
m = database.get_node_handle('Concept', 'monkey')
s = database.get_link_handle('Similarity', [h, m])

links = database.get_incoming_links(atom_handle=h, handles_only=False)
atom = database.get_atom(handle=s, targets_type=True, targets_document=True)
assert atom in links

links = database.get_incoming_links(atom_handle=h, handles_only=True)
answer = database.redis.smembers(f'incomming_set:{h}')
assert links == [h.decode() for h in answer]
assert s in links

links = database.get_incoming_links(atom_handle=m, handles_only=True)
answer = database.redis.smembers(f'incomming_set:{m}')
assert links == [h.decode() for h in answer]

links = database.get_incoming_links(atom_handle=s, handles_only=True)
assert links == []

def test_get_atom_type(self, database):
h = database.get_node_handle('Concept', 'human')
m = database.get_node_handle('Concept', 'mammal')
i = database.get_link_handle('Inheritance', [h, m])

assert 'Concept' == database.get_atom_type(h)
assert 'Concept' == database.get_atom_type(m)
assert 'Inheritance' == database.get_atom_type(i)

0 comments on commit 4ba2063

Please sign in to comment.