flowquery 1.0.21 → 1.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flowquery.min.js +1 -1
- package/dist/graph/database.d.ts +1 -0
- package/dist/graph/database.d.ts.map +1 -1
- package/dist/graph/database.js +39 -0
- package/dist/graph/database.js.map +1 -1
- package/dist/parsing/functions/function_factory.d.ts +1 -0
- package/dist/parsing/functions/function_factory.d.ts.map +1 -1
- package/dist/parsing/functions/function_factory.js +1 -0
- package/dist/parsing/functions/function_factory.js.map +1 -1
- package/dist/parsing/functions/schema.d.ts +17 -0
- package/dist/parsing/functions/schema.d.ts.map +1 -0
- package/dist/parsing/functions/schema.js +62 -0
- package/dist/parsing/functions/schema.js.map +1 -0
- package/docs/flowquery.min.js +1 -1
- package/flowquery-py/pyproject.toml +1 -1
- package/flowquery-py/src/graph/database.py +25 -1
- package/flowquery-py/src/parsing/functions/__init__.py +2 -0
- package/flowquery-py/src/parsing/functions/schema.py +36 -0
- package/flowquery-py/tests/compute/test_runner.py +46 -1
- package/flowquery-vscode/flowQueryEngine/flowquery.min.js +1 -1
- package/package.json +1 -1
- package/src/graph/database.ts +30 -0
- package/src/parsing/functions/function_factory.ts +1 -0
- package/src/parsing/functions/schema.ts +36 -0
- package/tests/compute/runner.test.ts +40 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Dict, Optional, Union
|
|
5
|
+
from typing import Any, Dict, Optional, Union
|
|
6
6
|
|
|
7
7
|
from ..parsing.ast_node import ASTNode
|
|
8
8
|
from .node import Node
|
|
@@ -54,6 +54,30 @@ class Database:
|
|
|
54
54
|
"""Gets a relationship from the database."""
|
|
55
55
|
return Database._relationships.get(relationship.type) if relationship.type else None
|
|
56
56
|
|
|
57
|
+
async def schema(self) -> list[dict[str, Any]]:
|
|
58
|
+
"""Returns the graph schema with node/relationship labels and sample data."""
|
|
59
|
+
result: list[dict[str, Any]] = []
|
|
60
|
+
|
|
61
|
+
for label, physical_node in Database._nodes.items():
|
|
62
|
+
records = await physical_node.data()
|
|
63
|
+
entry: dict[str, Any] = {"kind": "node", "label": label}
|
|
64
|
+
if records:
|
|
65
|
+
sample = {k: v for k, v in records[0].items() if k != "id"}
|
|
66
|
+
if sample:
|
|
67
|
+
entry["sample"] = sample
|
|
68
|
+
result.append(entry)
|
|
69
|
+
|
|
70
|
+
for rel_type, physical_rel in Database._relationships.items():
|
|
71
|
+
records = await physical_rel.data()
|
|
72
|
+
entry_rel: dict[str, Any] = {"kind": "relationship", "type": rel_type}
|
|
73
|
+
if records:
|
|
74
|
+
sample = {k: v for k, v in records[0].items() if k not in ("left_id", "right_id")}
|
|
75
|
+
if sample:
|
|
76
|
+
entry_rel["sample"] = sample
|
|
77
|
+
result.append(entry_rel)
|
|
78
|
+
|
|
79
|
+
return result
|
|
80
|
+
|
|
57
81
|
async def get_data(self, element: Union['Node', 'Relationship']) -> Union['NodeData', 'RelationshipData']:
|
|
58
82
|
"""Gets data for a node or relationship."""
|
|
59
83
|
if isinstance(element, Node):
|
|
@@ -27,6 +27,7 @@ from .range_ import Range
|
|
|
27
27
|
from .reducer_element import ReducerElement
|
|
28
28
|
from .replace import Replace
|
|
29
29
|
from .round_ import Round
|
|
30
|
+
from .schema import Schema
|
|
30
31
|
from .size import Size
|
|
31
32
|
from .split import Split
|
|
32
33
|
from .stringify import Stringify
|
|
@@ -71,5 +72,6 @@ __all__ = [
|
|
|
71
72
|
"ToJson",
|
|
72
73
|
"Type",
|
|
73
74
|
"Functions",
|
|
75
|
+
"Schema",
|
|
74
76
|
"PredicateSum",
|
|
75
77
|
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Schema introspection function."""
|
|
2
|
+
|
|
3
|
+
from typing import Any, AsyncGenerator
|
|
4
|
+
|
|
5
|
+
from .async_function import AsyncFunction
|
|
6
|
+
from .function_metadata import FunctionDef
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@FunctionDef({
|
|
10
|
+
"description": (
|
|
11
|
+
"Returns the graph schema listing all nodes and relationships "
|
|
12
|
+
"with a sample of their data."
|
|
13
|
+
),
|
|
14
|
+
"category": "async",
|
|
15
|
+
"parameters": [],
|
|
16
|
+
"output": {
|
|
17
|
+
"description": "Schema entry with kind, label/type, and optional sample data",
|
|
18
|
+
"type": "object",
|
|
19
|
+
},
|
|
20
|
+
"examples": [
|
|
21
|
+
"CALL schema() YIELD kind, label, type, sample RETURN kind, label, type, sample",
|
|
22
|
+
],
|
|
23
|
+
})
|
|
24
|
+
class Schema(AsyncFunction):
|
|
25
|
+
"""Returns the graph schema of the database.
|
|
26
|
+
|
|
27
|
+
Lists all nodes and relationships with their labels/types and a sample
|
|
28
|
+
of their data (excluding id from nodes, left_id and right_id from relationships).
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
async def generate(self) -> AsyncGenerator[Any, None]:
|
|
32
|
+
# Import at runtime to avoid circular dependency
|
|
33
|
+
from ...graph.database import Database
|
|
34
|
+
entries = await Database.get_instance().schema()
|
|
35
|
+
for entry in entries:
|
|
36
|
+
yield entry
|
|
@@ -1539,4 +1539,49 @@ class TestRunner:
|
|
|
1539
1539
|
await match.run()
|
|
1540
1540
|
results = match.results
|
|
1541
1541
|
assert len(results) == 1
|
|
1542
|
-
assert results[0]["name"] == "Employee 1"
|
|
1542
|
+
assert results[0]["name"] == "Employee 1"
|
|
1543
|
+
|
|
1544
|
+
@pytest.mark.asyncio
|
|
1545
|
+
async def test_schema_returns_nodes_and_relationships_with_sample_data(self):
|
|
1546
|
+
"""Test schema() returns nodes and relationships with sample data."""
|
|
1547
|
+
await Runner(
|
|
1548
|
+
"""
|
|
1549
|
+
CREATE VIRTUAL (:Animal) AS {
|
|
1550
|
+
UNWIND [
|
|
1551
|
+
{id: 1, species: 'Cat', legs: 4},
|
|
1552
|
+
{id: 2, species: 'Dog', legs: 4}
|
|
1553
|
+
] AS record
|
|
1554
|
+
RETURN record.id AS id, record.species AS species, record.legs AS legs
|
|
1555
|
+
}
|
|
1556
|
+
"""
|
|
1557
|
+
).run()
|
|
1558
|
+
await Runner(
|
|
1559
|
+
"""
|
|
1560
|
+
CREATE VIRTUAL (:Animal)-[:CHASES]-(:Animal) AS {
|
|
1561
|
+
UNWIND [
|
|
1562
|
+
{left_id: 2, right_id: 1, speed: 'fast'}
|
|
1563
|
+
] AS record
|
|
1564
|
+
RETURN record.left_id AS left_id, record.right_id AS right_id, record.speed AS speed
|
|
1565
|
+
}
|
|
1566
|
+
"""
|
|
1567
|
+
).run()
|
|
1568
|
+
|
|
1569
|
+
runner = Runner(
|
|
1570
|
+
"CALL schema() YIELD kind, label, type, sample RETURN kind, label, type, sample"
|
|
1571
|
+
)
|
|
1572
|
+
await runner.run()
|
|
1573
|
+
results = runner.results
|
|
1574
|
+
|
|
1575
|
+
animal = next((r for r in results if r.get("kind") == "node" and r.get("label") == "Animal"), None)
|
|
1576
|
+
assert animal is not None
|
|
1577
|
+
assert animal["sample"] is not None
|
|
1578
|
+
assert "id" not in animal["sample"]
|
|
1579
|
+
assert "species" in animal["sample"]
|
|
1580
|
+
assert "legs" in animal["sample"]
|
|
1581
|
+
|
|
1582
|
+
chases = next((r for r in results if r.get("kind") == "relationship" and r.get("type") == "CHASES"), None)
|
|
1583
|
+
assert chases is not None
|
|
1584
|
+
assert chases["sample"] is not None
|
|
1585
|
+
assert "left_id" not in chases["sample"]
|
|
1586
|
+
assert "right_id" not in chases["sample"]
|
|
1587
|
+
assert "speed" in chases["sample"]
|