diff --git a/tests/unit/test_query/test_neo4j_user_collection_query.py b/tests/unit/test_query/test_neo4j_user_collection_query.py index bf23680c..bbcaeb9a 100644 --- a/tests/unit/test_query/test_neo4j_user_collection_query.py +++ b/tests/unit/test_query/test_neo4j_user_collection_query.py @@ -26,19 +26,21 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=Value(value="http://example.com/s", is_uri=True), p=Value(value="http://example.com/p", is_uri=True), - o=Value(value="test_object", is_uri=False) + o=Value(value="test_object", is_uri=False), + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify SPO query for literal includes user/collection expected_query = ( "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN $src as src" + "RETURN $src as src " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -65,21 +67,23 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=Value(value="http://example.com/s", is_uri=True), p=Value(value="http://example.com/p", is_uri=True), - o=None + o=None, + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify SP query for literals includes user/collection expected_literal_query = ( "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN dest.value as dest" + "RETURN dest.value as dest " + "LIMIT 10" ) - + mock_driver.execute_query.assert_any_call( expected_literal_query, src="http://example.com/s", @@ -88,13 +92,14 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", database_='neo4j' ) - + # Verify SP query for nodes includes user/collection expected_node_query = ( "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN dest.uri as dest" + "RETURN dest.uri as dest " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -120,19 +125,21 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=Value(value="http://example.com/s", is_uri=True), p=None, - o=Value(value="http://example.com/o", is_uri=True) + o=Value(value="http://example.com/o", is_uri=True), + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify SO query for nodes includes user/collection expected_query = ( "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {uri: $uri, user: $user, collection: $collection}) " - "RETURN rel.uri as rel" + "RETURN rel.uri as rel " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -158,21 +165,23 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=Value(value="http://example.com/s", is_uri=True), p=None, - o=None + o=None, + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify S query includes user/collection expected_query = ( "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN rel.uri as rel, dest.value as dest" + "RETURN rel.uri as rel, dest.value as dest " + "LIMIT 10" ) - + mock_driver.execute_query.assert_any_call( expected_query, src="http://example.com/s", @@ -195,19 +204,21 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=None, p=Value(value="http://example.com/p", is_uri=True), - o=Value(value="literal", is_uri=False) + o=Value(value="literal", is_uri=False), + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify PO query for literals includes user/collection expected_query = ( "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN src.uri as src" + "RETURN src.uri as src " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -233,19 +244,21 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=None, p=Value(value="http://example.com/p", is_uri=True), - o=None + o=None, + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify P query includes user/collection expected_query = ( "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN src.uri as src, dest.value as dest" + "RETURN src.uri as src, dest.value as dest " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -270,19 +283,21 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=None, p=None, - o=Value(value="test_value", is_uri=False) + o=Value(value="test_value", is_uri=False), + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify O query for literals includes user/collection expected_query = ( "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel" + "RETURN src.uri as src, rel.uri as rel " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -307,34 +322,37 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=None, p=None, - o=None + o=None, + limit=10 ) - + mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) - + await processor.query_triples(query) - + # Verify wildcard query for literals includes user/collection expected_literal_query = ( "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel, dest.value as dest" + "RETURN src.uri as src, rel.uri as rel, dest.value as dest " + "LIMIT 10" ) - + mock_driver.execute_query.assert_any_call( expected_literal_query, user="test_user", collection="test_collection", database_='neo4j' ) - + # Verify wildcard query for nodes includes user/collection expected_node_query = ( "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel, dest.uri as dest" + "RETURN src.uri as src, rel.uri as rel, dest.uri as dest " + "LIMIT 10" ) mock_driver.execute_query.assert_any_call( @@ -357,7 +375,8 @@ class TestNeo4jQueryUserCollectionIsolation: query = TriplesQueryRequest( s=Value(value="http://example.com/s", is_uri=True), p=None, - o=None + o=None, + limit=10 ) mock_driver.execute_query.return_value = ([], MagicMock(), MagicMock()) @@ -386,28 +405,29 @@ class TestNeo4jQueryUserCollectionIsolation: collection="test_collection", s=Value(value="http://example.com/s", is_uri=True), p=None, - o=None + o=None, + limit=10 ) - + # Mock some results mock_record1 = MagicMock() mock_record1.data.return_value = { "rel": "http://example.com/p1", "dest": "literal_value" } - + mock_record2 = MagicMock() mock_record2.data.return_value = { "rel": "http://example.com/p2", "dest": "http://example.com/o" } - + # Return results for literal query, empty for node query mock_driver.execute_query.side_effect = [ ([mock_record1], MagicMock(), MagicMock()), # Literal query ([mock_record2], MagicMock(), MagicMock()) # Node query ] - + result = await processor.query_triples(query) # Verify results are proper Triple objects diff --git a/trustgraph-flow/trustgraph/query/triples/neo4j/service.py b/trustgraph-flow/trustgraph/query/triples/neo4j/service.py index 0e84d733..8c513a67 100755 --- a/trustgraph-flow/trustgraph/query/triples/neo4j/service.py +++ b/trustgraph-flow/trustgraph/query/triples/neo4j/service.py @@ -71,7 +71,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN $src as src", + "RETURN $src as src " + "LIMIT " + str(query.limit), src=query.s.value, rel=query.p.value, value=query.o.value, user=user, collection=collection, database_=self.db, @@ -84,7 +85,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Node {uri: $uri, user: $user, collection: $collection}) " - "RETURN $src as src", + "RETURN $src as src " + "LIMIT " + str(query.limit), src=query.s.value, rel=query.p.value, uri=query.o.value, user=user, collection=collection, database_=self.db, @@ -101,7 +103,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN dest.value as dest", + "RETURN dest.value as dest " + "LIMIT " + str(query.limit), src=query.s.value, rel=query.p.value, user=user, collection=collection, database_=self.db, @@ -115,7 +118,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {uri: $rel, user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN dest.uri as dest", + "RETURN dest.uri as dest " + "LIMIT " + str(query.limit), src=query.s.value, rel=query.p.value, user=user, collection=collection, database_=self.db, @@ -135,7 +139,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN rel.uri as rel", + "RETURN rel.uri as rel " + "LIMIT " + str(query.limit), src=query.s.value, value=query.o.value, user=user, collection=collection, database_=self.db, @@ -149,7 +154,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {uri: $uri, user: $user, collection: $collection}) " - "RETURN rel.uri as rel", + "RETURN rel.uri as rel " + "LIMIT " + str(query.limit), src=query.s.value, uri=query.o.value, user=user, collection=collection, database_=self.db, @@ -167,7 +173,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN rel.uri as rel, dest.value as dest", + "RETURN rel.uri as rel, dest.value as dest " + "LIMIT " + str(query.limit), src=query.s.value, user=user, collection=collection, database_=self.db, @@ -181,7 +188,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {uri: $src, user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN rel.uri as rel, dest.uri as dest", + "RETURN rel.uri as rel, dest.uri as dest " + "LIMIT " + str(query.limit), src=query.s.value, user=user, collection=collection, database_=self.db, @@ -204,7 +212,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN src.uri as src", + "RETURN src.uri as src " + "LIMIT " + str(query.limit), uri=query.p.value, value=query.o.value, user=user, collection=collection, database_=self.db, @@ -218,7 +227,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Node {uri: $dest, user: $user, collection: $collection}) " - "RETURN src.uri as src", + "RETURN src.uri as src " + "LIMIT " + str(query.limit), uri=query.p.value, dest=query.o.value, user=user, collection=collection, database_=self.db, @@ -236,7 +246,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN src.uri as src, dest.value as dest", + "RETURN src.uri as src, dest.value as dest " + "LIMIT " + str(query.limit), uri=query.p.value, user=user, collection=collection, database_=self.db, @@ -250,7 +261,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {uri: $uri, user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN src.uri as src, dest.uri as dest", + "RETURN src.uri as src, dest.uri as dest " + "LIMIT " + str(query.limit), uri=query.p.value, user=user, collection=collection, database_=self.db, @@ -270,7 +282,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {value: $value, user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel", + "RETURN src.uri as src, rel.uri as rel " + "LIMIT " + str(query.limit), value=query.o.value, user=user, collection=collection, database_=self.db, @@ -284,7 +297,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {uri: $uri, user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel", + "RETURN src.uri as src, rel.uri as rel " + "LIMIT " + str(query.limit), uri=query.o.value, user=user, collection=collection, database_=self.db, @@ -302,7 +316,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Literal {user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel, dest.value as dest", + "RETURN src.uri as src, rel.uri as rel, dest.value as dest " + "LIMIT " + str(query.limit), user=user, collection=collection, database_=self.db, ) @@ -315,7 +330,8 @@ class Processor(TriplesQueryService): "MATCH (src:Node {user: $user, collection: $collection})-" "[rel:Rel {user: $user, collection: $collection}]->" "(dest:Node {user: $user, collection: $collection}) " - "RETURN src.uri as src, rel.uri as rel, dest.uri as dest", + "RETURN src.uri as src, rel.uri as rel, dest.uri as dest " + "LIMIT " + str(query.limit), user=user, collection=collection, database_=self.db, ) @@ -327,10 +343,10 @@ class Processor(TriplesQueryService): triples = [ Triple( s=self.create_value(t[0]), - p=self.create_value(t[1]), + p=self.create_value(t[1]), o=self.create_value(t[2]) ) - for t in triples + for t in triples[:query.limit] ] return triples