Repository: hmatsu47/llama_index
Pull request #13: pgvector サポートのプロパティグラフ実装不足分を追加する
Author: login: amazon-q-developer[bot]
Created: 2025-05-19T03:21:39Z | Merged: 2025-05-19T03:31:37Z

Pull request for pgvector サポートのプロパティグラフ実装不足分を追加する


Diff:
diff --git a/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py b/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
index 7af060202..a110a50e5 100644
--- a/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
+++ b/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
@@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
+        self,
+        entity_names: Optional[List[str]] = None,
+        relation_names: Optional[List[str]] = None,
+        properties: Optional[dict] = None,
+        ids: Optional[List[str]] = None,
+    ) -> None:
+        """Delete matching data."""
+        with Session(self._engine) as session:
+            # 1. Delete relations
+            relation_stmt = delete(self._relation_model)
+            if ids:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source_id.in_(ids)
+                    | self._relation_model.target_id.in_(ids)
+                )
+            if entity_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source.has(name=entity_names)
+                    | self._relation_model.target.has(name=entity_names)
+                )
+            if relation_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.label.in_(relation_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    relation_stmt = relation_stmt.filter(
+                        self._relation_model.source.has(
+                            self._node_model.properties[key] == value
+                        )
+                        | self._relation_model.target.has(
+                            self._node_model.properties[key] == value
+                        )
+                    )
+            session.execute(relation_stmt)
+
+            # 2. Delete nodes
+            entity_stmt = delete(self._node_model)
+            if ids:
+                entity_stmt = entity_stmt.filter(self._node_model.id.in_(ids))
+            if entity_names:
+                entity_stmt = entity_stmt.filter(
+                    self._node_model.name.in_(entity_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    entity_stmt = entity_stmt.filter(
+                        self._node_model.properties[key] == value
+                    )
+            session.execute(entity_stmt)
+            session.commit()
+
+    def structured_query(
+        self, query: str, param_map: Optional[Dict[str, Any]] = None
+    ) -> Any:
+        """Query the graph store with statement and parameters."""
+        raise NotImplementedError("PostgreSQL does not support cypher queries.")
+
+    def vector_query(
+        self, query: VectorStoreQuery, **kwargs: Any
+    ) -> Tuple[List[LabelledNode], List[float]]:
+        """Query the graph store with a vector store query."""
+        with Session(self._engine) as session:
+            result = (
+                session.query(
+                    self._node_model,
+                    self._node_model.embedding.cosine_distance(
+                        query.query_embedding
+                    ).label("embedding_distance"),
+                )
+                .filter(self._node_model.name.is_not(None))
+                .order_by(sql.asc("embedding_distance"))
+                .limit(query.similarity_top_k)
+                .all()
+            )
+
+            nodes = []
+            scores = []
+            for node, score in result:
+                nodes.append(
+                    EntityNode(
+                        name=node.name,
+                        label=node.label,
+                        properties=remove_empty_values(node.properties),
+                    )
+                )
+                scores.append(score)
+            return nodes, scores
\ No newline at end of file
diff --git a/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests b/llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests
deleted file mode 100644
index e69de29bb..000000000


Reviews:
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:18Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:19Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:20Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:20Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:21Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:22Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:23Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:23Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:24Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:25Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:50Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:51Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:51Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:52Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:53Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:54Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:55Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:55Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:56Z
user: login: amazon-q-developer[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-19T03:30:57Z

Review comments:
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: > [!WARNING]
> ****Description:**** The method lacks error handling for database operations, which could lead to unhandled exceptions. Add try-except blocks to catch and handle potential database errors, ensuring graceful error handling.

****Severity:**** High


created_at: 2025-05-19T03:30:18Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: > [!WARNING]
> ****Description:**** The method lacks error handling for database operations. Consider adding try-except blocks to handle potential database errors.

****Severity:**** High


created_at: 2025-05-19T03:30:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
+        self,
+        entity_names: Optional[List[str]] = None,
+        relation_names: Optional[List[str]] = None,
+        properties: Optional[dict] = None,
+        ids: Optional[List[str]] = None,
+    ) -> None:
+        """Delete matching data."""
+        with Session(self._engine) as session:
+            # 1. Delete relations
+            relation_stmt = delete(self._relation_model)
+            if ids:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source_id.in_(ids)
+                    | self._relation_model.target_id.in_(ids)
+                )
+            if entity_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source.has(name=entity_names)
+                    | self._relation_model.target.has(name=entity_names)
+                )
+            if relation_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.label.in_(relation_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    relation_stmt = relation_stmt.filter(
+                        self._relation_model.source.has(
+                            self._node_model.properties[key] == value
+                        )
+                        | self._relation_model.target.has(
+                            self._node_model.properties[key] == value
+                        )
+                    )
+            session.execute(relation_stmt)
+
+            # 2. Delete nodes
+            entity_stmt = delete(self._node_model)
+            if ids:
+                entity_stmt = entity_stmt.filter(self._node_model.id.in_(ids))
+            if entity_names:
+                entity_stmt = entity_stmt.filter(
+                    self._node_model.name.in_(entity_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    entity_stmt = entity_stmt.filter(
+                        self._node_model.properties[key] == value
+                    )
+            session.execute(entity_stmt)
+            session.commit()
+
+    def structured_query(
+        self, query: str, param_map: Optional[Dict[str, Any]] = None
+    ) -> Any:
+        """Query the graph store with statement and parameters."""
+        raise NotImplementedError("PostgreSQL does not support cypher queries.")
+
+    def vector_query(
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: > [!WARNING]
> ****Description:**** The vector_query method lacks error handling for database-related exceptions. Add try-except blocks to catch and handle potential SQLAlchemy or database-related exceptions.

****Severity:**** High


created_at: 2025-05-19T03:30:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
+        self,
+        entity_names: Optional[List[str]] = None,
+        relation_names: Optional[List[str]] = None,
+        properties: Optional[dict] = None,
+        ids: Optional[List[str]] = None,
+    ) -> None:
+        """Delete matching data."""
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: > [!WARNING]
> ****Description:**** The delete method lacks error handling for database operations. Implement try-except blocks to catch and handle potential database errors during delete operations.

****Severity:**** High


created_at: 2025-05-19T03:30:20Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: > [!WARNING]
> ****Description:**** The function lacks error handling for database operations. Implement try-except blocks to catch and handle potential database exceptions.

****Severity:**** High


created_at: 2025-05-19T03:30:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: ****Description:**** The upsert_nodes method uses get_or_create for each node, which can be inefficient for bulk operations. Consider using bulk insert or upsert operations to improve performance for large datasets.

****Severity:**** Medium


created_at: 2025-05-19T03:30:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import os
+from unittest import TestCase, SkipTest
+
+from llama_index.core.graph_stores.types import (
+    EntityNode,
+    Relation,
+    VectorStoreQuery,
+)
+
+from llama_index.graph_stores.postgres import PostgresPropertyGraphStore
+
+
+def get_store():
+    return PostgresPropertyGraphStore(
+        db_connection_string=os.environ.get("POSTGRES_TEST_CONNECTION_STRING"),
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests/test_property_graph_stores_postgres.py
user: login: amazon-q-developer[bot]
body: ****Description:**** The get_store() function doesn't handle the case where the environment variable is not set. Add a check for the environment variable and raise a meaningful exception if it's not set.

****Severity:**** Medium


created_at: 2025-05-19T03:30:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import os
+from unittest import TestCase, SkipTest
+
+from llama_index.core.graph_stores.types import (
+    EntityNode,
+    Relation,
+    VectorStoreQuery,
+)
+
+from llama_index.graph_stores.postgres import PostgresPropertyGraphStore
+
+
+def get_store():
+    return PostgresPropertyGraphStore(
+        db_connection_string=os.environ.get("POSTGRES_TEST_CONNECTION_STRING"),
+        drop_existing_table=True,
+        relation_table_name="test_relations",
+        node_table_name="test_nodes",
+    )
+
+
+class TestPostgresPropertyGraphStore(TestCase):
+    @classmethod
+    def setUp(self) -> None:
+        try:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests/test_property_graph_stores_postgres.py
user: login: amazon-q-developer[bot]
body: ****Description:**** The setUp method catches all exceptions and raises a SkipTest, which may hide specific database connection issues. Consider catching specific exceptions related to database connection failures instead of a generic Exception.

****Severity:**** Medium


created_at: 2025-05-19T03:30:23Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: ****Description:**** The function fetches all results at once, which may be inefficient for large datasets. Consider using server-side cursors or pagination to fetch results in batches.

****Severity:**** Medium


created_at: 2025-05-19T03:30:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: ****Description:**** The delete method lacks logging for tracking delete operations and their results. Add logging statements to record the number of relations and nodes deleted, and any potential errors encountered.

****Severity:**** Medium


created_at: 2025-05-19T03:30:25Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: Sorry, I'm not able to suggest a fix for this review finding.
created_at: 2025-05-19T03:30:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix addresses the lack of error handling in the database operations by adding a try-except block around the database operations in the `upsert_relations` method. This ensures that if any exception occurs during the database operations, the session is rolled back to maintain data consistency, and the exception is re-raised for proper error handling at a higher level.
```suggestion
def upsert_relations(self, relations: List[Relation]) -> None:
        """Upsert relations."""
        with Session(self._engine) as session:
            try:
                for r in relations:
                    get_or_create(
                        session,
                        self._node_model,
                        id=r.source_id,
                    )
                    get_or_create(
                        session,
                        self._node_model,
                        id=r.target_id,
                    )
                    relation_instance, _ = get_or_create(
                        session,
                        self._relation_model,
                        label=r.label,
                        source_id=r.source_id,
                        target_id=r.target_id,
                    )
                    relation_instance.properties = r.properties
                    session.add(relation_instance)
                session.commit()
            except Exception as e:
                session.rollback()
                raise e
```

created_at: 2025-05-19T03:30:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
+        self,
+        entity_names: Optional[List[str]] = None,
+        relation_names: Optional[List[str]] = None,
+        properties: Optional[dict] = None,
+        ids: Optional[List[str]] = None,
+    ) -> None:
+        """Delete matching data."""
+        with Session(self._engine) as session:
+            # 1. Delete relations
+            relation_stmt = delete(self._relation_model)
+            if ids:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source_id.in_(ids)
+                    | self._relation_model.target_id.in_(ids)
+                )
+            if entity_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.source.has(name=entity_names)
+                    | self._relation_model.target.has(name=entity_names)
+                )
+            if relation_names:
+                relation_stmt = relation_stmt.filter(
+                    self._relation_model.label.in_(relation_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    relation_stmt = relation_stmt.filter(
+                        self._relation_model.source.has(
+                            self._node_model.properties[key] == value
+                        )
+                        | self._relation_model.target.has(
+                            self._node_model.properties[key] == value
+                        )
+                    )
+            session.execute(relation_stmt)
+
+            # 2. Delete nodes
+            entity_stmt = delete(self._node_model)
+            if ids:
+                entity_stmt = entity_stmt.filter(self._node_model.id.in_(ids))
+            if entity_names:
+                entity_stmt = entity_stmt.filter(
+                    self._node_model.name.in_(entity_names)
+                )
+            if properties:
+                for key, value in properties.items():
+                    entity_stmt = entity_stmt.filter(
+                        self._node_model.properties[key] == value
+                    )
+            session.execute(entity_stmt)
+            session.commit()
+
+    def structured_query(
+        self, query: str, param_map: Optional[Dict[str, Any]] = None
+    ) -> Any:
+        """Query the graph store with statement and parameters."""
+        raise NotImplementedError("PostgreSQL does not support cypher queries.")
+
+    def vector_query(
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: Sorry, I'm not able to suggest a fix for this review finding.
created_at: 2025-05-19T03:30:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
+        self,
+        entity_names: Optional[List[str]] = None,
+        relation_names: Optional[List[str]] = None,
+        properties: Optional[dict] = None,
+        ids: Optional[List[str]] = None,
+    ) -> None:
+        """Delete matching data."""
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix addresses the lack of error handling in the delete method by implementing a try-except block. It catches potential database errors during delete operations, rolls back the session if an error occurs, and raises an exception with a descriptive error message. This improvement enhances the robustness of the delete method by providing better error handling and preventing partial deletions in case of failures.
```suggestion
) -> None:
        """Delete matching data."""
        with Session(self._engine) as session:
            try:
                # 1. Delete relations
                relation_stmt = delete(self._relation_model)
                if ids:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.source_id.in_(ids)
                        | self._relation_model.target_id.in_(ids)
                    )
                if entity_names:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.source.has(name=entity_names)
                        | self._relation_model.target.has(name=entity_names)
                    )
                if relation_names:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.label.in_(relation_names)
                    )
                if properties:
                    for key, value in properties.items():
                        relation_stmt = relation_stmt.filter(
                            self._relation_model.source.has(
                                self._node_model.properties[key] == value
                            )
                            | self._relation_model.target.has(
                                self._node_model.properties[key] == value
                            )
                        )
                session.execute(relation_stmt)

                # 2. Delete nodes
                entity_stmt = delete(self._node_model)
                if ids:
                    entity_stmt = entity_stmt.filter(self._node_model.id.in_(ids))
                if entity_names:
                    entity_stmt = entity_stmt.filter(
                        self._node_model.name.in_(entity_names)
                    )
                if properties:
                    for key, value in properties.items():
                        entity_stmt = entity_stmt.filter(
                            self._node_model.properties[key] == value
                        )
                session.execute(entity_stmt)
                session.commit()
            except Exception as e:
                session.rollback()
                raise Exception(f"Error during delete operation: {str(e)}")

    def structured_query(
        self, query: str, param_map: Optional[Dict[str, Any]] = None
```

created_at: 2025-05-19T03:30:52Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix addresses the lack of error handling for database operations by implementing a try-except block around the database query execution and result processing. This change allows the function to catch and handle potential database exceptions, improving the robustness of the code. However, the fix is incomplete as it only prints the error message to the console. A TODO comment is added to remind developers to implement proper error logging in the future.
```suggestion
return []

        with Session(self._engine) as session:
            try:
                result = session.execute(
                    sql.text(
                        rel_depth_query.format(
                            relation_table=self._relation_table_name,
                            node_table=self._node_table_name,
                        )
                    ),
                    {
                        "ids": ids,
                        "depth": depth,
                        "limit": limit,
                    },
                )

                keys = result.keys()
                raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]

                ignore_rels = ignore_rels or []
                for row in raw_rels:
                    if row["rel_label"] in ignore_rels:
                        continue

                    source = EntityNode(
                        id=row["e1_id"],
                        name=row["e1_name"],
                        label=row["e1_label"],
                        properties=json.loads(row["e1_properties"]),
                    )
                    target = EntityNode(
                        id=row["e2_id"],
                        name=row["e2_name"],
                        label=row["e2_label"],
                        properties=json.loads(row["e2_properties"]),
                    )
                    relation = Relation(
                        label=row["rel_label"],
                        source_id=source.id,
                        target_id=target.id,
                        properties=json.loads(row["rel_properties"]),
                    )
                    triplets.append([source, relation, target])
            except Exception as e:
                # TODO: Implement proper error logging
                print(f"Database error occurred: {str(e)}")
        return triplets

    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
```

created_at: 2025-05-19T03:30:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix addresses the inefficiency of using get_or_create for each node by implementing a bulk upsert operation using SQLAlchemy's bulk_insert_mappings method. This approach significantly improves performance for large datasets by reducing the number of database queries and leveraging the database's native upsert capabilities. The fix separates the bulk upsert operations for entity nodes and chunk nodes, maintaining the original functionality while enhancing efficiency.
```suggestion
other_list.append(item)

        with Session(self._engine) as session:
            # Bulk upsert for entities
            session.bulk_insert_mappings(
                self._node_model,
                [
                    {
                        "id": entity.id,
                        "name": entity.name,
                        "label": entity.label,
                        "properties": entity.properties,
                        "embedding": entity.embedding,
                    }
                    for entity in entity_list
                ],
                update_changed_only=True,
            )

            # Bulk upsert for chunks
            session.bulk_insert_mappings(
                self._node_model,
                [
                    {
                        "id": chunk.id,
                        "text": chunk.text,
                        "label": chunk.label,
                        "properties": chunk.properties,
                        "embedding": chunk.embedding,
                    }
                    for chunk in chunk_list
                ],
                update_changed_only=True,
            )
            session.commit()

    def upsert_relations(self, relations: List[Relation]) -> None:
```

created_at: 2025-05-19T03:30:54Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import os
+from unittest import TestCase, SkipTest
+
+from llama_index.core.graph_stores.types import (
+    EntityNode,
+    Relation,
+    VectorStoreQuery,
+)
+
+from llama_index.graph_stores.postgres import PostgresPropertyGraphStore
+
+
+def get_store():
+    return PostgresPropertyGraphStore(
+        db_connection_string=os.environ.get("POSTGRES_TEST_CONNECTION_STRING"),
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests/test_property_graph_stores_postgres.py
user: login: amazon-q-developer[bot]
body: The fix addresses the comment by adding a check for the POSTGRES_TEST_CONNECTION_STRING environment variable in the get_store() function. If the variable is not set, it raises a ValueError with a meaningful message. This ensures that the function handles the case where the environment variable is missing, providing better error handling and debugging information.
```suggestion
def get_store():
    connection_string = os.environ.get("POSTGRES_TEST_CONNECTION_STRING")
    if not connection_string:
        raise ValueError("POSTGRES_TEST_CONNECTION_STRING environment variable is not set")
    return PostgresPropertyGraphStore(
        db_connection_string=connection_string,
        drop_existing_table=True,
        relation_table_name="test_relations",
        node_table_name="test_nodes",
```

created_at: 2025-05-19T03:30:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,104 @@
+import os
+from unittest import TestCase, SkipTest
+
+from llama_index.core.graph_stores.types import (
+    EntityNode,
+    Relation,
+    VectorStoreQuery,
+)
+
+from llama_index.graph_stores.postgres import PostgresPropertyGraphStore
+
+
+def get_store():
+    return PostgresPropertyGraphStore(
+        db_connection_string=os.environ.get("POSTGRES_TEST_CONNECTION_STRING"),
+        drop_existing_table=True,
+        relation_table_name="test_relations",
+        node_table_name="test_nodes",
+    )
+
+
+class TestPostgresPropertyGraphStore(TestCase):
+    @classmethod
+    def setUp(self) -> None:
+        try:
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/tests/test_property_graph_stores_postgres.py
user: login: amazon-q-developer[bot]
body: The fix addresses the comment by catching specific PostgreSQL-related exceptions (psycopg2.OperationalError and psycopg2.Error) instead of a generic Exception. This change allows for more precise error handling and provides more informative error messages when the database connection fails. The fix also includes the actual error message in the SkipTest exception, which can help in diagnosing the specific reason for the database unavailability.
```suggestion
def setUp(self) -> None:
        try:
            get_store()
        except (psycopg2.OperationalError, psycopg2.Error) as e:  # import psycopg2
            raise SkipTest(f"PostgreSQL database is not available: {str(e)}")

        self.e1 = EntityNode(name="e1", properties={"p1": "v1"})
        self.e2 = EntityNode(name="e2")
```

created_at: 2025-05-19T03:30:55Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix addresses the inefficiency of fetching all results at once by implementing batch processing. Instead of using `result.fetchall()`, which retrieves all rows into memory, the code now uses `result.fetchmany(batch_size)` in a loop to fetch results in smaller batches. This approach is more memory-efficient for large datasets and allows for processing data in chunks, potentially improving performance for large result sets.
```suggestion
)

            keys = result.keys()
            # Fetch results in batches
            batch_size = 1000  # Adjust this value based on your needs
            raw_rels = []
            while True:
                batch = result.fetchmany(batch_size)
                if not batch:
                    break
                raw_rels.extend([dict(zip(keys, row)) for row in batch])

            ignore_rels = ignore_rels or []
            for row in raw_rels:
```

created_at: 2025-05-19T03:30:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -234,4 +234,214 @@ def get_triplets(
                     properties=remove_empty_values(r.properties),
                 )
                 triplets.append([source, relation, target])
-            return triplets
\ No newline at end of file
+            return triplets
+            
+    def get_rel_map(
+        self,
+        graph_nodes: List[LabelledNode],
+        depth: int = 2,
+        limit: int = 30,
+        ignore_rels: Optional[List[str]] = None,
+    ) -> List[Triplet]:
+        """Get depth-aware rel map."""
+        triplets = []
+        ids = [node.id for node in graph_nodes]
+
+        if not ids:
+            return []
+
+        with Session(self._engine) as session:
+            result = session.execute(
+                sql.text(
+                    rel_depth_query.format(
+                        relation_table=self._relation_table_name,
+                        node_table=self._node_table_name,
+                    )
+                ),
+                {
+                    "ids": ids,
+                    "depth": depth,
+                    "limit": limit,
+                },
+            )
+
+            keys = result.keys()
+            raw_rels = [dict(zip(keys, row)) for row in result.fetchall()]
+
+            ignore_rels = ignore_rels or []
+            for row in raw_rels:
+                if row["rel_label"] in ignore_rels:
+                    continue
+
+                source = EntityNode(
+                    id=row["e1_id"],
+                    name=row["e1_name"],
+                    label=row["e1_label"],
+                    properties=json.loads(row["e1_properties"]),
+                )
+                target = EntityNode(
+                    id=row["e2_id"],
+                    name=row["e2_name"],
+                    label=row["e2_label"],
+                    properties=json.loads(row["e2_properties"]),
+                )
+                relation = Relation(
+                    label=row["rel_label"],
+                    source_id=source.id,
+                    target_id=target.id,
+                    properties=json.loads(row["rel_properties"]),
+                )
+                triplets.append([source, relation, target])
+        return triplets
+
+    def upsert_nodes(self, nodes: List[LabelledNode]) -> None:
+        """Upsert nodes."""
+        entity_list: List[EntityNode] = []
+        chunk_list: List[ChunkNode] = []
+        other_list: List[LabelledNode] = []
+
+        for item in nodes:
+            if isinstance(item, EntityNode):
+                entity_list.append(item)
+            elif isinstance(item, ChunkNode):
+                chunk_list.append(item)
+            else:
+                other_list.append(item)
+
+        with Session(self._engine) as session:
+            # TODO: use upsert instead of get_or_create
+            for entity in entity_list:
+                entity_instance, _ = get_or_create(
+                    session, self._node_model, id=entity.id
+                )
+                entity_instance.name = entity.name
+                entity_instance.label = entity.label
+                entity_instance.properties = entity.properties
+                entity_instance.embedding = entity.embedding
+                session.add(entity_instance)
+
+            for chunk in chunk_list:
+                chunk_instance, _ = get_or_create(
+                    session, self._node_model, id=chunk.id
+                )
+                chunk_instance.text = chunk.text
+                chunk_instance.label = chunk.label
+                chunk_instance.properties = chunk.properties
+                chunk_instance.embedding = chunk.embedding
+                session.add(chunk_instance)
+            session.commit()
+
+    def upsert_relations(self, relations: List[Relation]) -> None:
+        """Upsert relations."""
+        with Session(self._engine) as session:
+            for r in relations:
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.source_id,
+                )
+                get_or_create(
+                    session,
+                    self._node_model,
+                    id=r.target_id,
+                )
+                relation_instance, _ = get_or_create(
+                    session,
+                    self._relation_model,
+                    label=r.label,
+                    source_id=r.source_id,
+                    target_id=r.target_id,
+                )
+                relation_instance.properties = r.properties
+                session.add(relation_instance)
+                session.commit()
+
+    def delete(
path: llama-index-integrations/graph_stores/llama-index-graph-stores-postgres/llama_index/graph_stores/postgres/property_graph.py
user: login: amazon-q-developer[bot]
body: The fix adds logging functionality to the delete method as suggested in the comment. It imports the logging module and creates a logger object. The delete operation is now wrapped in a try-except block, which will allow for logging of any errors encountered during the deletion process. The actual logging of the number of relations and nodes deleted would need to be implemented after executing the delete statements, which is not shown in this code snippet.
```suggestion
import logging

                    self._node_model,
                    id=r.source_id,
                )
                get_or_create(
                    session,
                    self._node_model,
                    id=r.target_id,
                )
                relation_instance, _ = get_or_create(
                    session,
                    self._relation_model,
                    label=r.label,
                    source_id=r.source_id,
                    target_id=r.target_id,
                )
                relation_instance.properties = r.properties
                session.add(relation_instance)
                session.commit()

    def delete(
        self,
        entity_names: Optional[List[str]] = None,
        relation_names: Optional[List[str]] = None,
        properties: Optional[dict] = None,
        ids: Optional[List[str]] = None,
    ) -> None:
        """Delete matching data."""
        logger = logging.getLogger(__name__)
        with Session(self._engine) as session:
            try:
                # 1. Delete relations
                relation_stmt = delete(self._relation_model)
                if ids:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.source_id.in_(ids)
                        | self._relation_model.target_id.in_(ids)
                    )
                if entity_names:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.source.has(name=entity_names)
                        | self._relation_model.target.has(name=entity_names)
                    )
                if relation_names:
                    relation_stmt = relation_stmt.filter(
                        self._relation_model.label.in_(relation_names)
                    )
                if properties:
                    for key, value in properties.items():
                        relation_stmt = relation_stmt.filter(
                            self._relation_model.source.has(
```

created_at: 2025-05-19T03:30:57Z
author_association: CONTRIBUTOR

PR comments:
user: login: amazon-q-developer[bot]
created_at: 2025-05-19T03:21:40Z
body: Resolves #12
author_association: CONTRIBUTOR
user: login: amazon-q-developer[bot]
created_at: 2025-05-19T03:21:41Z
body: To provide feedback, navigate to the **Files changed** tab and leave comments on the proposed code changes. Choose **Start review** for each comment, and then choose **Request changes**, and I'll propose revised changes.
author_association: CONTRIBUTOR
user: login: amazon-q-developer[bot]
created_at: 2025-05-19T03:21:42Z
body: :hourglass_flowing_sand: I'm reviewing this pull request for security vulnerabilities and code quality issues. I'll provide an update when I'm done
author_association: CONTRIBUTOR
user: login: amazon-q-developer[bot]
created_at: 2025-05-19T03:30:25Z
body: :white_check_mark: I finished the code review, and left comments with the issues I found. I will now generate code fix suggestions.
author_association: CONTRIBUTOR