graflo.architecture.query.models¶
Read-request models for the DB-agnostic graph query surface.
Four questions an agent asks of a live graph — which nodes, what is adjacent to this one, what is reachable from these, how many — expressed once, in logical schema names, and answered identically by every backend.
Filters are :class:FilterExpression, which already renders to AQL, Cypher,
nGQL, GSQL, SQL and Python. There is no new query language here, and there is
deliberately no way to pass a backend query through: Connection.execute must
never be reachable from an agent-facing path, so nothing in this module carries
a raw query string.
Validation raises rather than clamps. Silently reducing hops=99 to 3
hands the caller a partial answer it believes is complete — the failure mode
this codebase already rejected for TigerGraph edge direction.
Attributes¶
Classes¶
AggregateQuery
¶
Bases: GraphQuery
Count or summarise one vertex type.
Source code in graflo/architecture/query/models.py
Attributes¶
aggregated_field = PydanticField(default=None, description='Property to aggregate. Required for everything but COUNT.')
class-attribute
instance-attribute
¶
aggregation
property
¶
function as an enum. See NeighborQuery.edge_direction.
filters = PydanticField(default=None)
class-attribute
instance-attribute
¶
function = PydanticField(default=AggregationType.COUNT, description='Aggregation to apply.')
class-attribute
instance-attribute
¶
group_by = PydanticField(default=None, description='Property to group by. COUNT only.')
class-attribute
instance-attribute
¶
vertex_type = PydanticField(...)
class-attribute
instance-attribute
¶
Methods:¶
GraphQuery
¶
Bases: ConfigBaseModel
Shared envelope for every read request.
Note
There is intentionally no caps field. Caps come from
:data:HARD_CAPS and are lowered only through :meth:narrowed, so a
request body cannot raise its own ceiling.
Source code in graflo/architecture/query/models.py
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | |
Attributes¶
limit = PydanticField(default=100, ge=1, description='Rows to return, within `max_rows`.')
class-attribute
instance-attribute
¶
projection = PydanticField(default=None, description="Property names to return. None returns whatever the backend stores, subject to the cap's allow-list.")
class-attribute
instance-attribute
¶
timeout_s = PydanticField(default=10.0, gt=0, description='Seconds to allow, within `timeout_s`.')
class-attribute
instance-attribute
¶
Methods:¶
finish_init(caps=None)
¶
Validate against caps, defaulting to the core ceiling.
Returns:
| Type | Description |
|---|---|
Self
|
Self, so the call composes: |
Raises:
| Type | Description |
|---|---|
CapExceededError
|
Naming the cap that was exceeded. |
Source code in graflo/architecture/query/models.py
named_fields()
¶
narrowed(caps)
¶
Return a copy fitted to caps, raising on anything explicitly asked for.
The split is what makes caps both enforceable and usable:
- A value the caller explicitly set above a cap raises
:class:
CapExceededError. Silently clamping it would hand back a partial answer the caller believes is complete — the failure mode this codebase already rejected for TigerGraph edge direction. - A value the caller left at its default is clamped. A policy of
max_rows=5must not 422 every request that simply did not mention a limit; that would make strict policies unusable rather than safe.
model_fields_set is what distinguishes the two, which is precisely
why it must be consulted rather than comparing against the default.
Projection is always intersected rather than raising, since an allow-list exists to hide properties: refusing the request would tell the caller which forbidden property they guessed correctly.
Source code in graflo/architecture/query/models.py
refuse_reserved(reserved)
¶
Refuse a query that names any of reserved.
Raises:
| Type | Description |
|---|---|
ReservedFieldError
|
Naming none of them. |
Source code in graflo/architecture/query/models.py
NeighborQuery
¶
Bases: GraphQuery
What is adjacent to one anchor vertex.
The instance-plane counterpart of SchemaGraph.schema_neighbors. They are
different questions and must never share a name: this one asks which rows
are adjacent, the other which types can be.
Source code in graflo/architecture/query/models.py
Attributes¶
direction = PydanticField(default=EdgeDirection.OUT, description='Orientation followed from the anchor. Defaults to OUT, matching `Connection.fetch_edges`; an edge declared `directed: false` is followed both ways regardless.')
class-attribute
instance-attribute
¶
edge_direction
property
¶
direction as an enum, for handing to a driver.
ConfigBaseModel sets use_enum_values=True, so the stored value is
the bare string. Backends compare against EdgeDirection members, and a
string silently matches none of them — which each backend then resolves
differently, so the same query returns different neighbourhoods
depending on where it runs. Always cross the boundary through this.
edge_relations = PydanticField(default=None, description='Restrict to these relations. None means all.')
class-attribute
instance-attribute
¶
filters = PydanticField(default=None)
class-attribute
instance-attribute
¶
hops = PydanticField(default=1, ge=1, description='Hop distance, within `max_hops`.')
class-attribute
instance-attribute
¶
key = PydanticField(..., description="A complete key of the anchor: the value of a one-field primary identity, or a mapping naming exactly the primary identity, a derived key's source fields, or one secondary identity. Any other mapping is refused: matching other fields is a lookup.")
class-attribute
instance-attribute
¶
vertex_type = PydanticField(..., description='Logical type of the anchor.')
class-attribute
instance-attribute
¶
Methods:¶
NodeQuery
¶
Bases: GraphQuery
List vertices of one type, optionally filtered.
Source code in graflo/architecture/query/models.py
ReservedFieldError
¶
Bases: Refusal
A query names a field only the writer may set.
The message names no field, so a refusal tells a caller nothing about which reserved field exists.
Source code in graflo/architecture/query/models.py
TraverseQuery
¶
Bases: GraphQuery
What is reachable from a set of anchors.
Source code in graflo/architecture/query/models.py
Attributes¶
direction = PydanticField(default=EdgeDirection.ANY)
class-attribute
instance-attribute
¶
edge_direction
property
¶
direction as an enum. See NeighborQuery.edge_direction.