|
10 | 10 |
|
11 | 11 | from pprint import pprint |
12 | 12 |
|
13 | | -from collections import Counter |
| 13 | +from collections import Counter, defaultdict |
14 | 14 | from itertools import permutations |
15 | 15 | from typing import Any, Dict, List, Optional, Tuple, cast |
16 | 16 | from neomodel.exceptions import ( |
@@ -1449,7 +1449,6 @@ def get_CREs( |
1449 | 1449 | include_only: Optional[List[str]] = None, |
1450 | 1450 | internal_id: Optional[str] = None, |
1451 | 1451 | ) -> List[cre_defs.CRE]: |
1452 | | - cres: List[cre_defs.CRE] = [] |
1453 | 1452 | query = CRE.query |
1454 | 1453 | if not external_id and not name and not description and not internal_id: |
1455 | 1454 | logger.error( |
@@ -1484,65 +1483,128 @@ def get_CREs( |
1484 | 1483 | ) |
1485 | 1484 | return [] |
1486 | 1485 |
|
1487 | | - for matching_cre in dbcres: |
1488 | | - cre = CREfromDB(matching_cre) |
1489 | | - cre.links = self.__make_cre_links( |
1490 | | - cre=matching_cre, include_only_nodes=include_only |
1491 | | - ) |
1492 | | - cre.links.extend(self.__make_cre_internal_links(cre=matching_cre)) |
1493 | | - cres.append(cre) |
1494 | | - return cres |
| 1486 | + return self._hydrate_cres_batch(dbcres, include_only_nodes=include_only) |
1495 | 1487 |
|
1496 | | - def __make_cre_internal_links(self, cre: CRE) -> List[cre_defs.Link]: |
1497 | | - links = [] |
1498 | | - internal_links = ( |
| 1488 | + def _hydrate_cres_batch( |
| 1489 | + self, |
| 1490 | + dbcres: List[CRE], |
| 1491 | + include_only_nodes: Optional[List[str]] = None, |
| 1492 | + ) -> List[cre_defs.CRE]: |
| 1493 | + if not dbcres: |
| 1494 | + return [] |
| 1495 | + |
| 1496 | + cre_ids = [cre.id for cre in dbcres] |
| 1497 | + node_links_by_cre: Dict[str, List[Links]] = defaultdict(list) |
| 1498 | + node_ids: set = set() |
| 1499 | + for link in self.session.query(Links).filter(Links.cre.in_(cre_ids)).all(): |
| 1500 | + node_links_by_cre[link.cre].append(link) |
| 1501 | + node_ids.add(link.node) |
| 1502 | + |
| 1503 | + nodes_by_id: Dict[str, Node] = {} |
| 1504 | + if node_ids: |
| 1505 | + nodes_by_id = { |
| 1506 | + node.id: node |
| 1507 | + for node in self.session.query(Node).filter(Node.id.in_(node_ids)).all() |
| 1508 | + } |
| 1509 | + |
| 1510 | + internal_links_by_cre: Dict[str, List[InternalLinks]] = defaultdict(list) |
| 1511 | + linked_cre_ids: set = set() |
| 1512 | + for internal_link in ( |
1499 | 1513 | self.session.query(InternalLinks) |
1500 | 1514 | .filter( |
1501 | | - sqla.or_(InternalLinks.cre == cre.id, InternalLinks.group == cre.id) |
| 1515 | + sqla.or_( |
| 1516 | + InternalLinks.cre.in_(cre_ids), |
| 1517 | + InternalLinks.group.in_(cre_ids), |
| 1518 | + ) |
1502 | 1519 | ) |
1503 | 1520 | .all() |
1504 | | - ) |
| 1521 | + ): |
| 1522 | + linked_cre_ids.add(internal_link.cre) |
| 1523 | + linked_cre_ids.add(internal_link.group) |
| 1524 | + if internal_link.cre in cre_ids: |
| 1525 | + internal_links_by_cre[internal_link.cre].append(internal_link) |
| 1526 | + if internal_link.group in cre_ids: |
| 1527 | + internal_links_by_cre[internal_link.group].append(internal_link) |
| 1528 | + |
| 1529 | + cres_by_id: Dict[str, CRE] = {cre.id: cre for cre in dbcres} |
| 1530 | + extra_cre_ids = linked_cre_ids - set(cre_ids) |
| 1531 | + if extra_cre_ids: |
| 1532 | + for linked in ( |
| 1533 | + self.session.query(CRE).filter(CRE.id.in_(extra_cre_ids)).all() |
| 1534 | + ): |
| 1535 | + cres_by_id[linked.id] = linked |
1505 | 1536 |
|
1506 | | - if len(internal_links) == 0: |
| 1537 | + result: List[cre_defs.CRE] = [] |
| 1538 | + for matching_cre in dbcres: |
| 1539 | + cre = CREfromDB(matching_cre) |
| 1540 | + cre.links = self._assemble_cre_node_links( |
| 1541 | + node_links_by_cre.get(matching_cre.id, []), |
| 1542 | + nodes_by_id, |
| 1543 | + include_only_nodes, |
| 1544 | + ) |
| 1545 | + seen_internal: set = set() |
| 1546 | + internal_rows = [] |
| 1547 | + for row in internal_links_by_cre.get(matching_cre.id, []): |
| 1548 | + key = (row.cre, row.group, row.type) |
| 1549 | + if key not in seen_internal: |
| 1550 | + seen_internal.add(key) |
| 1551 | + internal_rows.append(row) |
| 1552 | + cre.links.extend( |
| 1553 | + self._assemble_cre_internal_links( |
| 1554 | + matching_cre, internal_rows, cres_by_id |
| 1555 | + ) |
| 1556 | + ) |
| 1557 | + result.append(cre) |
| 1558 | + return result |
| 1559 | + |
| 1560 | + def _assemble_cre_internal_links( |
| 1561 | + self, |
| 1562 | + cre: CRE, |
| 1563 | + internal_links: List[InternalLinks], |
| 1564 | + cres_by_id: Dict[str, CRE], |
| 1565 | + ) -> List[cre_defs.Link]: |
| 1566 | + links: List[cre_defs.Link] = [] |
| 1567 | + if not internal_links: |
1507 | 1568 | logger.debug( |
1508 | 1569 | f"CRE {cre.name}:{cre.external_id}:{cre.id} has no internal links" |
1509 | 1570 | ) |
1510 | 1571 |
|
1511 | 1572 | for internal_link in internal_links: |
1512 | | - |
1513 | | - linked_cre_query = self.session.query(CRE) |
1514 | 1573 | link_type = cre_defs.LinkTypes.from_str(internal_link.type) |
1515 | 1574 |
|
1516 | 1575 | if internal_link.cre == cre.id: |
1517 | | - # if we are the lower cre in this relationship, we need to flip the "Contains" linktypes |
1518 | | - linked_cre = linked_cre_query.filter( |
1519 | | - CRE.id == internal_link.group |
1520 | | - ).first() # get the higher cre so we can add the link |
| 1576 | + linked_cre = cres_by_id.get(internal_link.group) |
| 1577 | + if not linked_cre: |
| 1578 | + continue |
1521 | 1579 | if link_type == cre_defs.LinkTypes.Contains: |
1522 | 1580 | links.append( |
1523 | 1581 | cre_defs.Link( |
1524 | 1582 | ltype=cre_defs.LinkTypes.PartOf, |
1525 | 1583 | document=CREfromDB(linked_cre), |
1526 | 1584 | ) |
1527 | 1585 | ) |
1528 | | - elif ( |
1529 | | - link_type == cre_defs.LinkTypes.Related |
1530 | | - ): # if it's not a "Contains" link, it's a "Related" link |
| 1586 | + elif link_type == cre_defs.LinkTypes.Related: |
1531 | 1587 | links.append( |
1532 | 1588 | cre_defs.Link(ltype=link_type, document=CREfromDB(linked_cre)) |
1533 | 1589 | ) |
1534 | 1590 | continue |
1535 | | - # if we are are the higher cre then we don't need to do anything, relationship types are always "higher"->"lower" |
1536 | | - linked_cre = linked_cre_query.filter(CRE.id == internal_link.cre).first() |
1537 | | - links.append(cre_defs.Link(ltype=link_type, document=CREfromDB(linked_cre))) |
| 1591 | + |
| 1592 | + linked_cre = cres_by_id.get(internal_link.cre) |
| 1593 | + if linked_cre: |
| 1594 | + links.append( |
| 1595 | + cre_defs.Link(ltype=link_type, document=CREfromDB(linked_cre)) |
| 1596 | + ) |
1538 | 1597 | return links |
1539 | 1598 |
|
1540 | | - def __make_cre_links( |
1541 | | - self, cre: CRE, include_only_nodes: List[str] |
| 1599 | + def _assemble_cre_node_links( |
| 1600 | + self, |
| 1601 | + node_links: List[Links], |
| 1602 | + nodes_by_id: Dict[str, Node], |
| 1603 | + include_only_nodes: Optional[List[str]], |
1542 | 1604 | ) -> List[cre_defs.Link]: |
1543 | | - links = [] |
1544 | | - for link in self.session.query(Links).filter(Links.cre == cre.id).all(): |
1545 | | - node = self.session.query(Node).filter(Node.id == link.node).first() |
| 1605 | + links: List[cre_defs.Link] = [] |
| 1606 | + for link in node_links: |
| 1607 | + node = nodes_by_id.get(link.node) |
1546 | 1608 | if node and (not include_only_nodes or node.name in include_only_nodes): |
1547 | 1609 | links.append( |
1548 | 1610 | cre_defs.Link( |
@@ -1647,13 +1709,11 @@ def export(self, dir: str = None, dry_run: bool = False) -> List[cre_defs.Docume |
1647 | 1709 | def all_cres_with_pagination( |
1648 | 1710 | self, page: int = 1, per_page: int = 10 |
1649 | 1711 | ) -> List[cre_defs.CRE]: |
1650 | | - result: List[cre_defs.CRE] = [] |
1651 | 1712 | cres = self.session.query(CRE).paginate( |
1652 | 1713 | page=int(page), per_page=per_page, error_out=False |
1653 | 1714 | ) |
1654 | 1715 | total_pages = cres.pages |
1655 | | - for cre in cres.items: |
1656 | | - result.extend(self.get_CREs(external_id=cre.external_id)) |
| 1716 | + result = self._hydrate_cres_batch(list(cres.items)) |
1657 | 1717 | return result, page, total_pages |
1658 | 1718 |
|
1659 | 1719 | def get_cre_path(self, fromID: str, toID: str) -> List[cre_defs.Document]: |
@@ -2202,10 +2262,7 @@ def get_root_cres(self): |
2202 | 2262 | ) |
2203 | 2263 | .all() |
2204 | 2264 | ) |
2205 | | - result = [] |
2206 | | - for c in cres: |
2207 | | - result.extend(self.get_CREs(external_id=c.external_id)) |
2208 | | - return result |
| 2265 | + return self._hydrate_cres_batch(list(cres)) |
2209 | 2266 |
|
2210 | 2267 | def get_embeddings_by_doc_type(self, doc_type: str) -> Dict[str, List[float]]: |
2211 | 2268 | res = {} |
|
0 commit comments