1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
use crateSorter;
use crate::;
use Serialize;
use Value as JsonValue;
use Debug;
/// A window into a result set: how many items to step over, and how many to return.
///
/// # A window is only meaningful over a defined order
///
/// There is no such thing as a database default sort. MongoDB does not guarantee
/// natural order across two reads, and Postgres guarantees nothing at all without an
/// `ORDER BY`. So when `skip > 0` and no sort is in effect, the storage layer is free
/// to hand the same document to two pages and another to none — while still reporting a
/// correct `total`, `skip` and `limit` on every response, which is what makes the
/// symptom so hard to see.
///
/// The library does **not** invent an order to cover this: an imposed `ORDER BY id` on a
/// filtered query that does not use the id index forces a sort of the whole set, and
/// that cost is the caller's to choose, not the library's to impose. A view that wants
/// stable pages declares [`Query::default_sort`], **ending in a unique field** — a sort
/// on a non-unique key still orders its ties arbitrarily between two requests.
/// Paginating without any sort logs a warning at `warn` rather than failing.
/// Query abstraction for read-side storage.
///
/// All methods have default implementations so minimal structs need zero
/// boilerplate:
///
/// ```rust,ignore
/// #[derive(Debug, Serialize, Deserialize)]
/// struct GameQuery { category: Option<String>, available: Option<bool> }
/// impl Query for GameQuery {}
/// ```
///
/// The default `filter()` converts every non-`None` field to an equality
/// constraint (`field == value`) ANDed together. Override only when you need
/// non-equality operators (`Gte`, `Like`, …) or field-name remapping.
///
/// # What `_q` may name
///
/// Under `CqrsHttpQuery`, this struct's fields and the RSQL `_q` parameter are **one set
/// in two syntaxes**: RSQL exists because a flat `?field=value` cannot express `>=`,
/// `=in=`, `or` or a range. So `_q` may only name fields of the implementing struct,
/// derived from its `Deserialize` impl — a field not reachable as a query param has no
/// reason to be reachable from `_q`. A struct with no fields offers no filter at all.
/// Sorting is a different question — see [`Query::sortable_fields`].
///
/// **The derivation cannot read every shape.** It reads names off
/// `Deserializer::deserialize_struct`, which serde does not emit for a
/// `#[serde(flatten)]` field, for a unit/newtype/tuple struct, or for a hand-written
/// `Deserialize` that goes through `deserialize_map`. A query type of any of those shapes
/// derives *no* field, and every `_q` against it is rejected. Keep the fields plain and
/// on the struct itself.
///
/// **`#[serde(alias)]` is unsupported here.** serde expands aliases into a struct's
/// `FIELDS`, so the alias is admitted — but `_q` does not pass through serde, and the
/// name reaches the storage as written. `?label=x` would filter on the real field while
/// `_q=label==x` filters on `label`. `#[serde(rename)]` is fine: it moves both sides.
///
/// A field named after a param the extractor owns — `_q`, `skip`, `limit`, `page`,
/// `page_size`, `pageSize`, `sort` — is dropped from the set too, and from the published
/// params: the extractor eats the value, so the field is unreachable either way.
/// Converts every non-`null` scalar field of a serializable struct into an
/// equality constraint and ANDs them together.
///
/// Useful in `Query::filter()` overrides that need to combine the auto-derived
/// filter with custom logic.
Sized>