fauna.query.query_builder

  1import abc
  2from typing import Any, Optional, List
  3
  4from .template import FaunaTemplate
  5
  6
  7class Fragment(abc.ABC):
  8  """An abstract class representing a Fragment of a query.
  9    """
 10
 11  @abc.abstractmethod
 12  def get(self) -> Any:
 13    """An abstract method for returning a stored value.
 14        """
 15    pass
 16
 17
 18class ValueFragment(Fragment):
 19  """A concrete :class:`Fragment` representing a part of a query that can represent a template variable.
 20    For example, if a template contains a variable ``${foo}``, and an object ``{ "prop": 1 }`` is provided for foo,
 21    then ``{ "prop": 1 }`` should be wrapped as a :class:`ValueFragment`.
 22
 23    :param Any val: The value to be used as a fragment.
 24    """
 25
 26  def __init__(self, val: Any):
 27    self._val = val
 28
 29  def get(self) -> Any:
 30    """Gets the stored value.
 31
 32        :returns: The stored value.
 33        """
 34    return self._val
 35
 36
 37class LiteralFragment(Fragment):
 38  """A concrete :class:`Fragment` representing a query literal For example, in the template ```let x = ${foo}```,
 39    the portion ```let x = ``` is a query literal and should be wrapped as a :class:`LiteralFragment`.
 40
 41    :param str val: The query literal to be used as a fragment.
 42    """
 43
 44  def __init__(self, val: str):
 45    self._val = val
 46
 47  def get(self) -> str:
 48    """Returns the stored value.
 49
 50        :returns: The stored value.
 51        """
 52    return self._val
 53
 54
 55class Query:
 56  """A class for representing a query.
 57
 58       e.g. { "fql": [...] }
 59    """
 60  _fragments: List[Fragment]
 61
 62  def __init__(self, fragments: Optional[List[Fragment]] = None):
 63    self._fragments = fragments or []
 64
 65  @property
 66  def fragments(self) -> List[Fragment]:
 67    """The list of stored Fragments"""
 68    return self._fragments
 69
 70  def __str__(self) -> str:
 71    res = ""
 72    for f in self._fragments:
 73      res += str(f.get())
 74
 75    return res
 76
 77
 78def fql(query: str, **kwargs: Any) -> Query:
 79  """Creates a Query - capable of performing query composition and simple querying. It can accept a
 80    simple string query, or can perform composition using ``${}`` sigil string template with ``**kwargs`` as
 81    substitutions.
 82
 83    The ``**kwargs`` can be Fauna data types - such as strings, document references, or modules - and embedded
 84    Query - allowing you to compose arbitrarily complex queries.
 85
 86    When providing ``**kwargs``, following types are accepted:
 87        - :class:`str`, :class:`int`, :class:`float`, :class:`bool`, :class:`datetime.datetime`, :class:`datetime.date`,
 88          :class:`dict`, :class:`list`, :class:`Query`, :class:`DocumentReference`, :class:`Module`
 89
 90    :raises ValueError: If there is an invalid template placeholder or a value that cannot be encoded.
 91    :returns: A :class:`Query` that can be passed to the client for evaluation against Fauna.
 92
 93    Examples:
 94
 95    .. code-block:: python
 96        :name: Simple-FQL-Example
 97        :caption: Simple query declaration using this function.
 98
 99        fql('Dogs.byName("Fido")')
100
101    .. code-block:: python
102        :name: Composition-FQL-Example
103        :caption: Query composition using this function.
104
105        def get_dog(id):
106            return fql('Dogs.byId(${id})', id=id)
107
108        def get_vet_phone(id):
109            return fql('${dog} { .vet_phone_number }', dog=get_dog(id))
110
111        get_vet_phone('d123')
112
113    """
114
115  fragments: List[Any] = []
116  template = FaunaTemplate(query)
117  for text, field_name in template.iter():
118    if text is not None and len(text) > 0:
119      fragments.append(LiteralFragment(text))
120
121    if field_name is not None:
122      if field_name not in kwargs:
123        raise ValueError(
124            f"template variable `{field_name}` not found in provided kwargs")
125
126      # TODO: Reject if it's already a fragment, or accept *Fragment? Decide on API here
127      fragments.append(ValueFragment(kwargs[field_name]))
128  return Query(fragments)
class Fragment(abc.ABC):
 8class Fragment(abc.ABC):
 9  """An abstract class representing a Fragment of a query.
10    """
11
12  @abc.abstractmethod
13  def get(self) -> Any:
14    """An abstract method for returning a stored value.
15        """
16    pass

An abstract class representing a Fragment of a query.

@abc.abstractmethod
def get(self) -> Any:
12  @abc.abstractmethod
13  def get(self) -> Any:
14    """An abstract method for returning a stored value.
15        """
16    pass

An abstract method for returning a stored value.

class ValueFragment(Fragment):
19class ValueFragment(Fragment):
20  """A concrete :class:`Fragment` representing a part of a query that can represent a template variable.
21    For example, if a template contains a variable ``${foo}``, and an object ``{ "prop": 1 }`` is provided for foo,
22    then ``{ "prop": 1 }`` should be wrapped as a :class:`ValueFragment`.
23
24    :param Any val: The value to be used as a fragment.
25    """
26
27  def __init__(self, val: Any):
28    self._val = val
29
30  def get(self) -> Any:
31    """Gets the stored value.
32
33        :returns: The stored value.
34        """
35    return self._val

A concrete Fragment representing a part of a query that can represent a template variable. For example, if a template contains a variable ${foo}, and an object { "prop": 1 } is provided for foo, then { "prop": 1 } should be wrapped as a ValueFragment.

Parameters
  • Any val: The value to be used as a fragment.
ValueFragment(val: Any)
27  def __init__(self, val: Any):
28    self._val = val
def get(self) -> Any:
30  def get(self) -> Any:
31    """Gets the stored value.
32
33        :returns: The stored value.
34        """
35    return self._val

Gets the stored value.

:returns: The stored value.

class LiteralFragment(Fragment):
38class LiteralFragment(Fragment):
39  """A concrete :class:`Fragment` representing a query literal For example, in the template ```let x = ${foo}```,
40    the portion ```let x = ``` is a query literal and should be wrapped as a :class:`LiteralFragment`.
41
42    :param str val: The query literal to be used as a fragment.
43    """
44
45  def __init__(self, val: str):
46    self._val = val
47
48  def get(self) -> str:
49    """Returns the stored value.
50
51        :returns: The stored value.
52        """
53    return self._val

A concrete Fragment representing a query literal For example, in the template let x = ${foo}, the portion let x = is a query literal and should be wrapped as a LiteralFragment.

Parameters
  • str val: The query literal to be used as a fragment.
LiteralFragment(val: str)
45  def __init__(self, val: str):
46    self._val = val
def get(self) -> str:
48  def get(self) -> str:
49    """Returns the stored value.
50
51        :returns: The stored value.
52        """
53    return self._val

Returns the stored value.

:returns: The stored value.

class Query:
56class Query:
57  """A class for representing a query.
58
59       e.g. { "fql": [...] }
60    """
61  _fragments: List[Fragment]
62
63  def __init__(self, fragments: Optional[List[Fragment]] = None):
64    self._fragments = fragments or []
65
66  @property
67  def fragments(self) -> List[Fragment]:
68    """The list of stored Fragments"""
69    return self._fragments
70
71  def __str__(self) -> str:
72    res = ""
73    for f in self._fragments:
74      res += str(f.get())
75
76    return res

A class for representing a query.

e.g. { "fql": [...] }

Query(fragments: Optional[List[Fragment]] = None)
63  def __init__(self, fragments: Optional[List[Fragment]] = None):
64    self._fragments = fragments or []
fragments: List[Fragment]
66  @property
67  def fragments(self) -> List[Fragment]:
68    """The list of stored Fragments"""
69    return self._fragments

The list of stored Fragments

def fql(query: str, **kwargs: Any) -> Query:
 79def fql(query: str, **kwargs: Any) -> Query:
 80  """Creates a Query - capable of performing query composition and simple querying. It can accept a
 81    simple string query, or can perform composition using ``${}`` sigil string template with ``**kwargs`` as
 82    substitutions.
 83
 84    The ``**kwargs`` can be Fauna data types - such as strings, document references, or modules - and embedded
 85    Query - allowing you to compose arbitrarily complex queries.
 86
 87    When providing ``**kwargs``, following types are accepted:
 88        - :class:`str`, :class:`int`, :class:`float`, :class:`bool`, :class:`datetime.datetime`, :class:`datetime.date`,
 89          :class:`dict`, :class:`list`, :class:`Query`, :class:`DocumentReference`, :class:`Module`
 90
 91    :raises ValueError: If there is an invalid template placeholder or a value that cannot be encoded.
 92    :returns: A :class:`Query` that can be passed to the client for evaluation against Fauna.
 93
 94    Examples:
 95
 96    .. code-block:: python
 97        :name: Simple-FQL-Example
 98        :caption: Simple query declaration using this function.
 99
100        fql('Dogs.byName("Fido")')
101
102    .. code-block:: python
103        :name: Composition-FQL-Example
104        :caption: Query composition using this function.
105
106        def get_dog(id):
107            return fql('Dogs.byId(${id})', id=id)
108
109        def get_vet_phone(id):
110            return fql('${dog} { .vet_phone_number }', dog=get_dog(id))
111
112        get_vet_phone('d123')
113
114    """
115
116  fragments: List[Any] = []
117  template = FaunaTemplate(query)
118  for text, field_name in template.iter():
119    if text is not None and len(text) > 0:
120      fragments.append(LiteralFragment(text))
121
122    if field_name is not None:
123      if field_name not in kwargs:
124        raise ValueError(
125            f"template variable `{field_name}` not found in provided kwargs")
126
127      # TODO: Reject if it's already a fragment, or accept *Fragment? Decide on API here
128      fragments.append(ValueFragment(kwargs[field_name]))
129  return Query(fragments)

Creates a Query - capable of performing query composition and simple querying. It can accept a simple string query, or can perform composition using ${} sigil string template with **kwargs as substitutions.

The **kwargs can be Fauna data types - such as strings, document references, or modules - and embedded Query - allowing you to compose arbitrarily complex queries.

When providing **kwargs, following types are accepted: - str, int, float, bool, datetime.datetime, datetime.date, dict, list, Query, DocumentReference, Module

Raises
  • ValueError: If there is an invalid template placeholder or a value that cannot be encoded. :returns: A Query that can be passed to the client for evaluation against Fauna.

Examples:

fql('Dogs.byName("Fido")')
def get_dog(id):
    return fql('Dogs.byId(${id})', id=id)

def get_vet_phone(id):
    return fql('${dog} { .vet_phone_number }', dog=get_dog(id))

get_vet_phone('d123')