Coverage for src/fluree_py/types/query/where.py: 65%

23 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-02 03:03 +0000

1# You can think of a FlureeQL query executing in two phases: the where phase and the select phase. The where phase filters and returns sets of bound values that the select phase can use to construct JSON objects. The where clause may return node subject ?bindings that we then use with select expressions like "*" to perform graph crawls from those subject nodes. 

2# The where clause may also retrieve all the bindings we need, and we simply instruct the select clause that we want those bindings returned directly as query results. 

3# In any case, in addition to establishing logic variables for bound values, we use the where clause to establish various data constraints for the data we are interested in querying (e.g. filtering by a particular predicate value, or expressing optional and outer-join data conditions). 

4# When a where clause is an array, it can combine a series of where conditions and where operations. 

5 

6# Define Condition as a dictionary with flexible key-value pairs 

7from typing import Dict, List, Literal, TypeAlias, TypeGuard, Union 

8 

9from fluree_py.types.query.select import LogicVariable 

10 

11WhereResultSet: TypeAlias = Dict[str, str] 

12WhereRelationship: TypeAlias = Dict[str, str] 

13 

14# A where condition describes relationships between nodes that must be satisfied for the nodes to be included in result sets, and it names those sets. 

15# 

16# Examples: 

17# { "@id": "?s", "schema:name": "Sheil" } 

18# { "@id": "?s", "schema:name": "?name" } 

19# { "@id": "?s", "schema:name": "Freddie", "schema:familyName": "Mercury" } 

20# { "@id": "http://example.org/jack", "?p": "?o" } 

21WhereCondition = Union[WhereResultSet, WhereRelationship] 

22 

23# Examples: 

24# [ 

25# { 

26# "@id": "?s", 

27# "bestFriend": "?friend" 

28# }, 

29# { 

30# "@id": "?friend", 

31# "schema:name": "?name" 

32# } 

33# ] 

34SuccessiveWhereCondition: TypeAlias = List[WhereCondition] 

35 

36 

37# Examples: 

38# [ 

39# "optional", 

40# { "@id": "?s", "schema:name": "?name" }, 

41# { "@id": "?s", "schema:age": "?age" } 

42# ] 

43WhereOperationOptional: TypeAlias = List[Union[Literal["optional"], WhereCondition]] 

44 

45 

46# Examples: 

47# "(> ?age 45)" 

48# "(< ?age 50)" 

49# (! (strStarts ?url \"http\")) 

50WhereFilterExpression: TypeAlias = str 

51 

52 

53# A filter expression is a string that starts and ends with a parenthesis. 

54# It starts with a function, followed by a space 

55# It then contains a list of arguments. 

56# Each argument may be a logic variable, a predicate, or a nested filter expression. 

57# Examples: 

58# "(> ?age 45)" 

59# "(< ?age 50)" 

60# (! (strStarts ?url \"http\")) 

61def is_filter_expression(var: str) -> TypeGuard[WhereFilterExpression]: 

62 """ 

63 Type guard to check if a string is a valid filter expression. 

64 """ 

65 if not all(c.isprintable() for c in var): 

66 return False 

67 

68 # Check if the string starts and ends with a parenthesis 

69 if not (var.startswith("(") and var.endswith(")")): 

70 return False 

71 

72 # Get internal string 

73 internal = var[1:-1] 

74 

75 # Check if the internal string is balanced 

76 if internal.count("(") != internal.count(")") or internal.count('"') % 2 != 0: 

77 return False 

78 

79 # The internal string may have multiple arguments 

80 # Each argument can be a nested filter expression, a logic variable, or a predicate 

81 # There must be at least one logic variable or nested filter expression 

82 

83 return True 

84 

85 

86# Examples: 

87# ["filter", "(> ?age 45)", "(< ?age 50)"] 

88WhereOperationFilter: TypeAlias = List[ 

89 Union[Literal["optional"], WhereFilterExpression] 

90] 

91 

92 

93# Examples: 

94# [ 

95# "union", 

96# { "@id": "?s", "schema:email": "?email" }, 

97# { "@id": "?s", "ex:email": "?email" } 

98# ] 

99WhereOperationUnion: TypeAlias = List[Union[Literal["union"], WhereCondition]] 

100 

101# Examples: 

102# ["bind", "?canVote", "(>= ?age 18)"] 

103WhereOperationBind: TypeAlias = List[ 

104 Union[Literal["bind"], LogicVariable, WhereFilterExpression] 

105] 

106 

107WhereOperation = Union[ 

108 WhereOperationOptional, 

109 WhereOperationFilter, 

110 WhereOperationUnion, 

111 WhereOperationBind, 

112] 

113 

114 

115WhereClauseEntry: TypeAlias = Union[WhereCondition, WhereOperation] 

116 

117WhereClause: TypeAlias = WhereClauseEntry | List[WhereClauseEntry]