Line data Source code
1 : /**
2 : Copyright (c) 2019-2022 Roman Katuntsev <sbkarr@stappler.org>
3 : Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
4 :
5 : Permission is hereby granted, free of charge, to any person obtaining a copy
6 : of this software and associated documentation files (the "Software"), to deal
7 : in the Software without restriction, including without limitation the rights
8 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 : copies of the Software, and to permit persons to whom the Software is
10 : furnished to do so, subject to the following conditions:
11 :
12 : The above copyright notice and this permission notice shall be included in
13 : all copies or substantial portions of the Software.
14 :
15 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 : THE SOFTWARE.
22 : **/
23 :
24 : #include "SPDbContinueToken.h"
25 : #include "SPDbScheme.h"
26 :
27 : namespace STAPPLER_VERSIONIZED stappler::db {
28 :
29 150 : ContinueToken::ContinueToken(const StringView &field, size_t count, bool reverse)
30 150 : : field(field.str<Interface>()), count(count), flags(Initial) {
31 150 : if (reverse) {
32 75 : flags |= Reverse | Inverted;
33 : }
34 150 : }
35 :
36 300 : ContinueToken::ContinueToken(const StringView &str) {
37 300 : auto bytes = stappler::base64::decode<Interface>(str);
38 300 : auto d = data::read<Interface, typeof(bytes)>(bytes);
39 300 : if (d.isArray() && d.size() == 6) {
40 300 : field = d.getString(0);
41 300 : initVec = d.getValue(1);
42 300 : count = (size_t)d.getInteger(2);
43 300 : fetched = (size_t)d.getInteger(3);
44 300 : total = (size_t)d.getInteger(4);
45 300 : flags |= Flags(d.getInteger(5));
46 : }
47 300 : }
48 :
49 425 : bool ContinueToken::hasPrev() const {
50 425 : return hasFlag(Flags::Inverted) ? hasNextImpl() : hasPrevImpl();
51 : }
52 425 : bool ContinueToken::hasNext() const {
53 425 : return hasFlag(Flags::Inverted) ? hasPrevImpl() : hasNextImpl();
54 : }
55 :
56 375 : bool ContinueToken::isInit() const {
57 375 : return _init;
58 : }
59 :
60 75 : String ContinueToken::encode() const {
61 750 : return stappler::base64url::encode<Interface>(stappler::data::write<Interface>(Value({
62 75 : Value(field),
63 75 : Value(initVec),
64 75 : Value(count),
65 75 : Value(fetched),
66 75 : Value(total),
67 75 : Value(stappler::toInt(flags)),
68 675 : }), EncodeFormat::Cbor));
69 : }
70 :
71 250 : String ContinueToken::encodeNext() const {
72 250 : return hasFlag(Flags::Inverted) ? encodePrevImpl() : encodeNextImpl();
73 : }
74 :
75 250 : String ContinueToken::encodePrev() const {
76 250 : return hasFlag(Flags::Inverted) ? encodeNextImpl() : encodePrevImpl();
77 : }
78 :
79 425 : size_t ContinueToken::getStart() const {
80 425 : return hasFlag(Flags::Inverted) ? (getTotal() + 1 - fetched - getNumResults()) : (fetched + 1);
81 : }
82 425 : size_t ContinueToken::getEnd() const {
83 425 : return hasFlag(Flags::Inverted) ? std::min(total, getTotal() - fetched) : std::min(total, fetched + getNumResults());
84 : }
85 875 : size_t ContinueToken::getTotal() const {
86 875 : return total;
87 : }
88 375 : size_t ContinueToken::getCount() const {
89 375 : return count;
90 : }
91 0 : size_t ContinueToken::getFetched() const {
92 0 : return fetched;
93 : }
94 375 : StringView ContinueToken::getField() const {
95 375 : return field;
96 : }
97 :
98 425 : size_t ContinueToken::getNumResults() const {
99 425 : return _numResults;
100 : }
101 :
102 4450 : bool ContinueToken::hasFlag(Flags fl) const {
103 4450 : return (flags & fl) != Flags::None;
104 : }
105 :
106 0 : void ContinueToken::setFlag(Flags fl) {
107 0 : flags |= fl;
108 0 : }
109 0 : void ContinueToken::unsetFlag(Flags fl) {
110 0 : flags &= (~fl);
111 0 : }
112 :
113 0 : const Value &ContinueToken::getFirstVec() const {
114 0 : return firstVec;
115 : }
116 0 : const Value &ContinueToken::getLastVec() const {
117 0 : return lastVec;
118 : }
119 :
120 425 : bool ContinueToken::hasPrevImpl() const {
121 425 : return _init && fetched != 0;
122 : }
123 :
124 425 : bool ContinueToken::hasNextImpl() const {
125 425 : return _init && fetched + _numResults < total;
126 : }
127 :
128 250 : String ContinueToken::encodeNextImpl() const {
129 250 : Flags f = Flags::None;
130 250 : if (hasFlag(Flags::Inverted)) { f |= Flags::Inverted; }
131 2500 : return stappler::base64url::encode<Interface>(stappler::data::write<Interface>(Value({
132 250 : Value(field),
133 250 : Value(lastVec),
134 250 : Value(count),
135 250 : Value(fetched + _numResults),
136 250 : Value(total),
137 : Value(stappler::toInt(f))
138 2250 : }), EncodeFormat::Cbor));
139 : }
140 :
141 250 : String ContinueToken::encodePrevImpl() const {
142 250 : Flags f = Flags::Reverse;
143 250 : if (hasFlag(Flags::Inverted)) { f |= Flags::Inverted; }
144 2500 : return stappler::base64url::encode<Interface>(stappler::data::write<Interface>(Value({
145 250 : Value(field),
146 250 : Value(firstVec),
147 250 : Value(count),
148 250 : Value(fetched),
149 250 : Value(total),
150 : Value(stappler::toInt(f))
151 2250 : }), EncodeFormat::Cbor));
152 : }
153 :
154 425 : Value ContinueToken::perform(const Scheme &scheme, const Transaction &t, Query &q) {
155 425 : if (field != "__oid" && !scheme.getField(field)) {
156 0 : return Value();
157 : }
158 :
159 425 : q.select(field, db::Comparation::IsNotNull, Value(true));
160 425 : if (total == 0) {
161 150 : total = scheme.count(t, q);
162 150 : if (hasFlag(Flags::Reverse) && !fetched) {
163 75 : fetched = total;
164 : }
165 : }
166 :
167 425 : if (total == 0) {
168 0 : _init = true;
169 0 : return Value();
170 : }
171 :
172 425 : q.softLimit(field, hasFlag(Flags::Reverse) ? Ordering::Descending : Ordering::Ascending, count, Value(initVec));
173 :
174 425 : auto d = scheme.select(t, q);
175 425 : if (d.isArray()) {
176 425 : _numResults = d.size();
177 425 : if (hasFlag(Flags::Reverse)) {
178 225 : fetched -= (std::min(fetched, _numResults));
179 225 : firstVec = d.asArray().back().getValue(field);
180 225 : lastVec = d.asArray().front().getValue(field);
181 : } else {
182 200 : firstVec = d.asArray().front().getValue(field);
183 200 : lastVec = d.asArray().back().getValue(field);
184 : }
185 425 : _init = true;
186 : }
187 :
188 425 : return d;
189 425 : }
190 :
191 375 : Value ContinueToken::perform(const Scheme &scheme, const Transaction &t, Query &q, Ordering ord) {
192 375 : auto rev = hasFlag(Flags::Reverse);
193 375 : if (auto d = perform(scheme, t, q)) {
194 375 : if ((ord == Ordering::Ascending && rev) || (ord == Ordering::Descending && !rev)) {
195 0 : std::reverse(d.asArray().begin(), d.asArray().end());
196 : }
197 375 : return d;
198 375 : }
199 0 : return Value();
200 : }
201 :
202 0 : Value ContinueToken::performOrdered(const Scheme &scheme, const Transaction &t, Query &q) {
203 0 : if (!q.getOrderField().empty()) {
204 0 : field = q.getOrderField();
205 : }
206 :
207 0 : if (q.getOrdering() == db::Ordering::Descending) {
208 0 : setFlag(Flags::Inverted);
209 : } else {
210 0 : unsetFlag(Flags::Inverted);
211 : }
212 :
213 0 : return perform(scheme, t, q, q.getOrdering());
214 : }
215 :
216 25 : void ContinueToken::refresh(const Scheme &scheme, const Transaction &t, Query &q) {
217 25 : q.select(field, db::Comparation::IsNotNull, Value(true));
218 25 : auto newTotal = scheme.count(t, q);
219 :
220 25 : q.select(field, db::Comparation::LessThen, initVec);
221 25 : auto newFetched = scheme.count(t, q);
222 :
223 25 : total = newTotal;
224 25 : fetched = newFetched;
225 25 : }
226 :
227 : }
|