Coverage for src/fluree_py/types/query/select.py: 90%
39 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-02 03:03 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-02 03:03 +0000
1"""
2FlureeQL Select Clause Grammar
4The select clause in FlureeQL determines the structure and content of query results.
5It can be either a select object or a select array.
7This grammar defines the syntax for FlureeQL select clauses. The types defined below
8in this file implement this grammar in Python.
10Grammar (EBNF):
11``` ebnf
12 (* Main select clause structure *)
13 SelectClause = SelectObject | SelectArray
15 (* Select object maps logic variables to expressions *)
16 SelectObject = "{" LogicVariable ":" SelectExpressionList "}"
17 SelectExpressionList = [SelectExpression {"," SelectExpression}]
19 (* Select array contains variables or objects *)
20 SelectArray = "[" SelectArrayElement {"," SelectArrayElement} "]"
21 SelectArrayElement = LogicVariable | SelectObject
23 (* Expression types *)
24 SelectExpression = Wildcard | Predicate | NodeObjectTemplate
25 NodeObjectTemplate = "{" Predicate ":" SelectExpressionList "}"
27 (* Basic elements *)
28 LogicVariable = "?" (letter | digit | "-" | "_") {letter | digit | "-" | "_"}
29 Predicate = string
30 Wildcard = "*"
32 (* Character sets *)
33 letter = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z"
34 digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
35 string = '"' {character} '"'
36 character = letter | digit | "-" | "_" | ":" | "."
37```
39Example Queries:
40 # Select object - get name and all predicates of best friend
41 - { "?s": [ "name", { "bestFriend": ["*"] } ] }
43 # Select array - get multiple variables and objects
44 - ["?s", "?name", "?friend"]
45 - [ { "?s": ["*"] }, { "?friend": ["*"] } ]
47 # Node object template - nested data structures
48 - { "schema:address": ["*"] } # Get all address predicates
49 - { "bestFriend": ["*"] } # Get all best friend predicates
50 - { "bestFriend": [ { "address": ["*"] } ] } # Get address of best friend
52 # Logic variable examples
53 - "?firstname"
54 - "?first-name"
55 - "?first_name"
56 - "?address-1"
57"""
59import re
60from typing import Any, Dict, List, TypeAlias, TypeGuard, Union
62from fluree_py.types.common import Predicate, Wildcard
64# Regex pattern to match valid logic variables.
65# Starts with ? and is followed by alphanumeric characters, hyphens, or underscores.
66LOGIC_VARIABLE_PATTERN = re.compile(r"^\?[a-zA-Z0-9_-]+$")
68LogicVariable: TypeAlias = str
69"""
70A logic variable name in a FlureeQL query.
71Logic variables are strings that begin with a question mark, ?, followed by
72alphanumeric characters, hyphens, and underscores. They are used to bind
73subjects to variables in the query.
75Example Queries:
76 "?firstname"
77 "?first-name"
78 "?first_name"
79 "?address-1"
80"""
83def is_logic_variable(var: str) -> TypeGuard[LogicVariable]:
84 """
85 Type guard to check if a string is a valid logic variable.
86 """
87 if not all(c.isprintable() for c in var):
88 return False
89 return LOGIC_VARIABLE_PATTERN.search(var) is not None
92SelectExpression: TypeAlias = Union[Wildcard, Predicate, "NodeObjectTemplate"]
93"""
94A select expression in a FlureeQL query.
95Select expressions define what data to include in the query results.
96They can be:
971. A predicate (e.g., "schema:name") - includes the value of that predicate
982. The wildcard "*" - includes all predicates of the subject
993. A node object template - traverses nested predicate values
101Example Queries:
102 ["name", { "bestFriend": ["*"] }]
103"""
105SelectExpressionList: TypeAlias = List[SelectExpression]
106"""
107A list of select expressions in a FlureeQL query.
108Used in both select objects and node templates to specify multiple expressions.
110Example Queries:
111 ["name", "*", { "bestFriend": ["*"] }]
112"""
114NodeObjectTemplate: TypeAlias = Dict[Predicate, "SelectExpressionList"]
115"""
116A node object template in a FlureeQL query.
117Node object templates define how to traverse nested predicate values.
118They are objects where the keys are predicates, and the values are arrays of
119select expressions. This allows for recursive querying of nested data structures.
121Example Queries:
122 { "schema:address": ["*"] }
124 # Return an object that has all predicates for the node that "bestFriend" refers to
125 { "bestFriend": ["*"] }
127 # Multi-level nested object
128 { "bestFriend": [ { "address": ["*"] } ] }
129"""
132def is_node_object_template(var: Any) -> TypeGuard[NodeObjectTemplate]:
133 """
134 Type guard to check if a value is a valid node object template.
135 """
136 if not isinstance(var, dict):
137 return False
138 return all(isinstance(k, str) and isinstance(v, list) for (k, v) in var.items()) # type: ignore
141SelectObject: TypeAlias = Dict[LogicVariable, SelectExpressionList]
142"""
143A select object in a FlureeQL query.
144A select object maps logic variables to arrays of select expressions.
145Each logic variable corresponds to a set of subjects, and for each subject,
146a JSON object is constructed based on the select expressions.
148Example Queries:
149 { "?s": [ "name", { "bestFriend": ["*"] } ] }
150"""
153def is_select_object(var: Any) -> TypeGuard[SelectObject]:
154 """
155 Type guard to check if a value is a valid select object.
156 """
157 if not isinstance(var, dict):
158 return False
159 return all(is_logic_variable(k) and isinstance(v, list) for k, v in var.items()) # type: ignore
162SelectArrayElement: TypeAlias = Union[LogicVariable, SelectObject]
163"""
164An element in a select array in a FlureeQL query.
165An element in a select array can be either a logic variable or a select object.
167Example Queries:
168 "?s"
169 { "?s": ["*"] }
170"""
173def is_select_array_element(var: Any) -> TypeGuard[SelectArrayElement]:
174 """
175 Type guard to check if a value is a valid select array element.
176 """
177 return is_logic_variable(var) if isinstance(var, str) else is_select_object(var)
180SelectArray: TypeAlias = List[SelectArrayElement]
181"""
182A select array in a FlureeQL query.
183A select array is a list containing logic variables or select objects.
184When using a select array, each element of the query results will be an array
185containing the values for each element in the select array.
187Example Queries:
188 ["?s", "?name", "?friend"]
189 [ { "?s": ["*"] }, { "?friend": ["*"] } ]
190"""
193def is_select_array(var: Any) -> TypeGuard[SelectArray]:
194 """
195 Type guard to check if a value is a valid select array.
196 """
197 if not isinstance(var, list):
198 return False
200 return all(is_select_array_element(v) for v in var) # type: ignore
203SelectClause: TypeAlias = Union[SelectObject, SelectArray]
204"""
205A select clause in a FlureeQL query.
206A select clause can be either a select object or a select array.
208Example Queries:
209 - { "?s": [ "name", { "bestFriend": ["*"] } ] }
210 - ["?s", "?name", "?friend"]
211"""
213__all__ = [
214 "LogicVariable",
215 "Predicate",
216 "Wildcard",
217 "SelectExpression",
218 "SelectExpressionList",
219 "NodeObjectTemplate",
220 "SelectObject",
221 "SelectArrayElement",
222 "SelectArray",
223 "is_logic_variable",
224 "is_node_object_template",
225 "is_select_object",
226 "is_select_array_element",
227 "is_select_array",
228]