react-native-mosquito-transport 0.0.21 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -7
- package/TODO +18 -8
- package/ios/Mosquitodb.swift +0 -4
- package/package.json +7 -7
- package/src/helpers/engine_api.js +2 -7
- package/src/helpers/peripherals.js +7 -31
- package/src/helpers/purger.js +264 -0
- package/src/helpers/sqlite_manager.js +138 -0
- package/src/helpers/utils.js +63 -43
- package/src/helpers/values.js +5 -20
- package/src/helpers/variables.js +36 -10
- package/src/index.d.ts +97 -56
- package/src/index.js +42 -38
- package/src/products/auth/accessor.js +10 -11
- package/src/products/auth/index.js +13 -28
- package/src/products/database/accessor.js +429 -165
- package/src/products/database/counter.js +12 -10
- package/src/products/database/index.js +177 -162
- package/src/products/database/types.js +1 -0
- package/src/products/database/validator.js +1 -1
- package/src/products/http_callable/accessor.js +72 -0
- package/src/products/http_callable/counter.js +11 -0
- package/src/products/http_callable/index.js +57 -66
- package/src/products/storage/index.js +20 -13
|
@@ -2,16 +2,19 @@ import { niceHash, shuffleArray, sortArrayByObjectKey } from "../../helpers/peri
|
|
|
2
2
|
import { awaitStore, updateCacheStore } from "../../helpers/utils";
|
|
3
3
|
import { CacheStore, Scoped } from "../../helpers/variables";
|
|
4
4
|
import { assignExtractionFind, CompareBson, confirmFilterDoc, defaultBSON, downcastBSON, validateCollectionName, validateFilter } from "./validator";
|
|
5
|
-
import getLodash from 'lodash
|
|
6
|
-
import setLodash from 'lodash
|
|
7
|
-
import unsetLodash from 'lodash
|
|
5
|
+
import getLodash from 'lodash/get';
|
|
6
|
+
import setLodash from 'lodash/set';
|
|
7
|
+
import unsetLodash from 'lodash/unset';
|
|
8
8
|
import { DatabaseRecordsListener } from "../../helpers/listeners";
|
|
9
|
-
import cloneDeep from "lodash
|
|
9
|
+
import cloneDeep from "lodash/cloneDeep";
|
|
10
10
|
import { BSONRegExp, ObjectId, Timestamp } from "bson";
|
|
11
11
|
import { niceGuard, Validator } from "guard-object";
|
|
12
12
|
import { TIMESTAMP } from "../..";
|
|
13
|
-
import {
|
|
14
|
-
import { serializeToBase64 } from "./bson";
|
|
13
|
+
import { docSize, incrementDatabaseSize } from "./counter";
|
|
14
|
+
import { deserializeBSON, serializeToBase64 } from "./bson";
|
|
15
|
+
import { openDB, SQLITE_COMMANDS, SQLITE_PATH, useSqliteLinearAccessId } from "../../helpers/sqlite_manager";
|
|
16
|
+
|
|
17
|
+
const { LIMITER_DATA, LIMITER_RESULT, DB_COUNT_QUERY } = SQLITE_PATH;
|
|
15
18
|
|
|
16
19
|
export const listenQueryEntry = (callback, { accessId, builder, config, processId }) => {
|
|
17
20
|
const { projectUrl, dbName, dbUrl, path } = builder;
|
|
@@ -25,8 +28,8 @@ export const listenQueryEntry = (callback, { accessId, builder, config, processI
|
|
|
25
28
|
|
|
26
29
|
const listener = DatabaseRecordsListener.listenTo('d', async (dispatchId) => {
|
|
27
30
|
if (dispatchId !== processId) return;
|
|
28
|
-
const cache = await getRecord(builder,
|
|
29
|
-
if (cache) callback(cache[
|
|
31
|
+
const cache = await getRecord(builder, accessId, episode);
|
|
32
|
+
if (cache) callback(cache[0]);
|
|
30
33
|
});
|
|
31
34
|
|
|
32
35
|
return () => {
|
|
@@ -40,100 +43,244 @@ export const listenQueryEntry = (callback, { accessId, builder, config, processI
|
|
|
40
43
|
};
|
|
41
44
|
};
|
|
42
45
|
|
|
43
|
-
export const
|
|
46
|
+
export const insertCountQuery = async (builder, access_id, value) => {
|
|
47
|
+
const { projectUrl, dbUrl, dbName, path } = builder;
|
|
48
|
+
|
|
49
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
50
|
+
if (io) {
|
|
51
|
+
setLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id], { value, touched: Date.now() });
|
|
52
|
+
} else {
|
|
53
|
+
const initNode = `${projectUrl}_${dbUrl}_${dbName}_${path}`;
|
|
54
|
+
await useSqliteLinearAccessId(builder, access_id, 'dbQueryCount')(async sqlite => {
|
|
55
|
+
if (!Scoped.initedSqliteInstances.dbQueryCount[initNode]) {
|
|
56
|
+
Scoped.initedSqliteInstances.dbQueryCount[initNode] = (async () => {
|
|
57
|
+
await sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${DB_COUNT_QUERY(path)} ( access_id TEXT PRIMARY KEY, value TEXT, touched INTEGER )`).catch(() => null);
|
|
58
|
+
await Promise.allSettled([
|
|
59
|
+
sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(DB_COUNT_QUERY(path), ['access_id'])),
|
|
60
|
+
// sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(DB_COUNT_QUERY(path), ['touched']))
|
|
61
|
+
]);
|
|
62
|
+
})();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
await Scoped.initedSqliteInstances.dbQueryCount[initNode];
|
|
66
|
+
await sqlite.executeSql(
|
|
67
|
+
SQLITE_COMMANDS.MERGE(DB_COUNT_QUERY(path), ['access_id', 'value', 'touched']),
|
|
68
|
+
[access_id, JSON.stringify(value), Date.now()]
|
|
69
|
+
);
|
|
70
|
+
setLodash(CacheStore.DatabaseStats.counters, [projectUrl, dbUrl, dbName, path], true);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
updateCacheStore(undefined, ['DatabaseCountResult'])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const getCountQuery = async (builder, access_id) => {
|
|
77
|
+
const { projectUrl, dbUrl, dbName, path } = builder;
|
|
78
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
79
|
+
|
|
80
|
+
if (io) {
|
|
81
|
+
const data = getLodash(CacheStore.DatabaseCountResult, [projectUrl, dbUrl, dbName, path, access_id]);
|
|
82
|
+
if (data) data.touched = Date.now();
|
|
83
|
+
return data && data.value;
|
|
84
|
+
} else {
|
|
85
|
+
const result = await useSqliteLinearAccessId(builder, access_id, 'dbQueryCount')(sqlite =>
|
|
86
|
+
sqlite.executeSql(`SELECT * FROM ${DB_COUNT_QUERY(path)} WHERE access_id = ?`, [access_id]).then(async r => {
|
|
87
|
+
r = JSON.parse(r[0].rows.item(0).value);
|
|
88
|
+
await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(DB_COUNT_QUERY(path), ['touched'], 'access_id = ?'), [Date.now(), access_id]);
|
|
89
|
+
return r;
|
|
90
|
+
}).catch(() => undefined)
|
|
91
|
+
);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export const insertRecord = async (builder, config, accessIdWithoutLimit, value, episode = 0) => {
|
|
44
97
|
builder = builder && cloneDeep(builder);
|
|
45
98
|
config = config && cloneDeep(config);
|
|
46
99
|
value = value && cloneDeep(value);
|
|
47
100
|
|
|
48
101
|
await awaitStore();
|
|
102
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
49
103
|
const { projectUrl, dbUrl, dbName, path, command } = builder;
|
|
50
|
-
const
|
|
51
|
-
const
|
|
52
|
-
|
|
104
|
+
const { limit } = command;
|
|
105
|
+
const thisSize = docSize(value);
|
|
106
|
+
|
|
107
|
+
if (!io) {
|
|
108
|
+
await useSqliteLinearAccessId(builder, accessIdWithoutLimit, 'database')(async (sqlite) => {
|
|
109
|
+
const initNode = `${projectUrl}_${dbUrl}_${dbName}_${path}`;
|
|
110
|
+
|
|
111
|
+
if (!Scoped.initedSqliteInstances.database[initNode]) {
|
|
112
|
+
Scoped.initedSqliteInstances.database[initNode] = (async () => {
|
|
113
|
+
await Promise.allSettled([
|
|
114
|
+
sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${LIMITER_DATA(path)} ( access_id TEXT PRIMARY KEY, value TEXT, touched INTEGER, size INTEGER )`),
|
|
115
|
+
sqlite.executeSql(`CREATE TABLE IF NOT EXISTS ${LIMITER_RESULT(path)} ( access_id-limit TEXT PRIMARY KEY, access_id TEXT, value TEXT, touched INTEGER, size INTEGER )`)
|
|
116
|
+
]);
|
|
117
|
+
|
|
118
|
+
await Promise.allSettled([
|
|
119
|
+
sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_DATA(path), ['access_id'])),
|
|
120
|
+
// sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_DATA(path), ['touched'])),
|
|
121
|
+
sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_RESULT(path), ['access_id-limit'])),
|
|
122
|
+
// sqlite.executeSql(SQLITE_COMMANDS.CREATE_INDEX(LIMITER_RESULT(path), ['touched']))
|
|
123
|
+
]);
|
|
124
|
+
})();
|
|
125
|
+
}
|
|
53
126
|
|
|
54
|
-
|
|
127
|
+
await Scoped.initedSqliteInstances.database[initNode];
|
|
55
128
|
|
|
56
|
-
|
|
57
|
-
const trackedList = [...tracks || []];
|
|
58
|
-
const ignoreList = [...ignore || []];
|
|
129
|
+
const resultAccessId = `${accessIdWithoutLimit}-${limit}`;
|
|
59
130
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
131
|
+
const [instanceData, resultData] = await Promise.all([
|
|
132
|
+
sqlite.executeSql(`SELECT access_id, size FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [accessIdWithoutLimit]),
|
|
133
|
+
sqlite.executeSql(`SELECT access_id-limit, size FROM ${LIMITER_RESULT(path)} WHERE access_id-limit = ?`, [resultAccessId])
|
|
134
|
+
]).then(r =>
|
|
135
|
+
r.map(v => v[0].rows.item(0))
|
|
136
|
+
);
|
|
137
|
+
const isEpisode = episode === 1 || !!resultData;
|
|
138
|
+
|
|
139
|
+
const editionSizeOffset = thisSize - (instanceData?.size || 0);
|
|
140
|
+
const resultSizeOffset = isEpisode ? thisSize - (resultData?.size || 0) : 0;
|
|
141
|
+
|
|
142
|
+
const newData = serializeToBase64({
|
|
143
|
+
command,
|
|
144
|
+
config,
|
|
145
|
+
latest_limiter: limit,
|
|
146
|
+
size: thisSize,
|
|
147
|
+
data: value ? Array.isArray(value) ? value : [value] : []
|
|
148
|
+
});
|
|
149
|
+
const newResultData = isEpisode && serializeToBase64({
|
|
150
|
+
data: value,
|
|
151
|
+
size: thisSize
|
|
152
|
+
});
|
|
64
153
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
154
|
+
await Promise.all([
|
|
155
|
+
sqlite.executeSql(
|
|
156
|
+
SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
|
|
157
|
+
[accessIdWithoutLimit, newData, Date.now(), thisSize]
|
|
158
|
+
),
|
|
159
|
+
isEpisode ?
|
|
160
|
+
sqlite.executeSql(
|
|
161
|
+
SQLITE_COMMANDS.MERGE(LIMITER_RESULT(path), ['access_id-limit', 'access_id', 'value', 'touched', 'size']),
|
|
162
|
+
[resultAccessId, accessIdWithoutLimit, newResultData, Date.now(), thisSize]
|
|
163
|
+
) : Promise.resolve()
|
|
164
|
+
]);
|
|
165
|
+
incrementDatabaseSize(builder, path, editionSizeOffset + resultSizeOffset);
|
|
166
|
+
});
|
|
167
|
+
updateCacheStore(undefined, ['DatabaseStore', 'DatabaseStats']);
|
|
168
|
+
return;
|
|
68
169
|
}
|
|
69
170
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
colData.listing.push(e);
|
|
74
|
-
incrementDatabaseSize(projectUrl, e);
|
|
75
|
-
} else {
|
|
76
|
-
decrementDatabaseSize(projectUrl, colData.listing[b4DocIndex]);
|
|
77
|
-
incrementDatabaseSize(projectUrl, e);
|
|
78
|
-
colData.listing[b4DocIndex] = e;
|
|
79
|
-
}
|
|
80
|
-
addSet(trackedList, e._id);
|
|
81
|
-
});
|
|
171
|
+
const instanceData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit]);
|
|
172
|
+
const resultData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, limit]);
|
|
173
|
+
const isEpisode = episode === 1 || !!resultData;
|
|
82
174
|
|
|
83
|
-
(
|
|
84
|
-
|
|
85
|
-
if (colData.listing.findIndex(v => CompareBson.equal(v._id, e)) === -1) {
|
|
86
|
-
deleteSet(trackedList, e);
|
|
87
|
-
deleteSet(ignoreList, e);
|
|
88
|
-
} else addSet(ignoreList, e);
|
|
89
|
-
} else deleteSet(ignoreList, e);
|
|
90
|
-
});
|
|
175
|
+
const editionSizeOffset = thisSize - (instanceData?.size || 0);
|
|
176
|
+
const resultSizeOffset = isEpisode ? thisSize - (resultData?.size || 0) : 0;
|
|
91
177
|
|
|
92
|
-
|
|
93
|
-
setLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'record', accessId], {
|
|
178
|
+
const newData = {
|
|
94
179
|
command,
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
|
|
180
|
+
config,
|
|
181
|
+
latest_limiter: limit,
|
|
182
|
+
size: thisSize,
|
|
183
|
+
data: value ? Array.isArray(value) ? value : [value] : [],
|
|
184
|
+
touched: Date.now()
|
|
185
|
+
};
|
|
186
|
+
const newResultData = isEpisode && {
|
|
187
|
+
data: value,
|
|
188
|
+
size: thisSize,
|
|
189
|
+
touched: Date.now()
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
incrementDatabaseSize(builder, path, editionSizeOffset + resultSizeOffset);
|
|
193
|
+
|
|
194
|
+
setLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit], newData);
|
|
195
|
+
if (isEpisode) setLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, limit], cloneDeep(newResultData));
|
|
196
|
+
updateCacheStore(undefined, ['DatabaseStore', 'DatabaseStats']);
|
|
102
197
|
};
|
|
103
198
|
|
|
104
|
-
export const getRecord = async (builder,
|
|
199
|
+
export const getRecord = async (builder, accessIdWithoutLimit, episode = 0) => {
|
|
105
200
|
await awaitStore();
|
|
201
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
106
202
|
const { projectUrl, dbUrl, dbName, path, command } = builder;
|
|
107
|
-
const {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
203
|
+
const { limit, sort, direction, random, findOne } = command;
|
|
204
|
+
const isEpisode = episode === 1;
|
|
205
|
+
|
|
206
|
+
const transformData = (data) => {
|
|
207
|
+
data = cloneDeep(data);
|
|
208
|
+
if (random) {
|
|
209
|
+
data = shuffleArray(data);
|
|
210
|
+
} else if (sort) {
|
|
211
|
+
data = sortArrayByObjectKey(data.slice(0), sort);
|
|
212
|
+
if (
|
|
213
|
+
direction === -1 ||
|
|
214
|
+
direction === 'desc' ||
|
|
215
|
+
direction === 'descending'
|
|
216
|
+
) data = data.slice(0).reverse();
|
|
217
|
+
}
|
|
117
218
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
direction === -1 ||
|
|
124
|
-
direction === 'desc' ||
|
|
125
|
-
direction === 'descending'
|
|
126
|
-
) choosenColData.reverse();
|
|
219
|
+
if (findOne) {
|
|
220
|
+
data = data[0];
|
|
221
|
+
} else if (limit) data = data.slice(0, limit);
|
|
222
|
+
|
|
223
|
+
return data;
|
|
127
224
|
}
|
|
128
225
|
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
226
|
+
if (!io) {
|
|
227
|
+
const record = await useSqliteLinearAccessId(builder, accessIdWithoutLimit, 'database')(async sqlite => {
|
|
228
|
+
const resultAccessId = `${accessIdWithoutLimit}-${limit}`;
|
|
229
|
+
|
|
230
|
+
const thisData = await (
|
|
231
|
+
isEpisode ? sqlite.executeSql(`SELECT * FROM ${LIMITER_RESULT(path)} WHERE access_id-limit = ?`, [resultAccessId]) :
|
|
232
|
+
sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [accessIdWithoutLimit])
|
|
233
|
+
).then(v => {
|
|
234
|
+
const d = v[0].rows.item(0);
|
|
235
|
+
if (d) return deserializeBSON(d.value, true);
|
|
236
|
+
}).catch(() => null);
|
|
237
|
+
|
|
238
|
+
if (!thisData) return null;
|
|
132
239
|
|
|
133
|
-
|
|
240
|
+
if (isEpisode) {
|
|
241
|
+
await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(LIMITER_RESULT(path), ['touched'], 'access_id-limit = ?'), [Date.now(), resultAccessId]);
|
|
242
|
+
return [thisData.data];
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const { latest_limiter, data } = thisData;
|
|
246
|
+
|
|
247
|
+
if (
|
|
248
|
+
latest_limiter === undefined ||
|
|
249
|
+
(Validator.POSITIVE_NUMBER(limit) && latest_limiter >= limit)
|
|
250
|
+
) {
|
|
251
|
+
await sqlite.executeSql(SQLITE_COMMANDS.UPDATE_COLUMNS(LIMITER_DATA(path), ['touched'], 'access_id = ?'), [Date.now(), accessIdWithoutLimit]);
|
|
252
|
+
return [transformData(data)];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return record || null;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (isEpisode) {
|
|
260
|
+
const resultData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'episode', accessIdWithoutLimit, limit]);
|
|
261
|
+
if (resultData) {
|
|
262
|
+
resultData.touched = Date.now();
|
|
263
|
+
return [cloneDeep(resultData.data)];
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const instanceData = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', accessIdWithoutLimit]);
|
|
269
|
+
if (!instanceData) return null;
|
|
270
|
+
const { latest_limiter, data } = instanceData;
|
|
271
|
+
|
|
272
|
+
if (
|
|
273
|
+
latest_limiter === undefined ||
|
|
274
|
+
(Validator.POSITIVE_NUMBER(limit) && latest_limiter >= limit)
|
|
275
|
+
) {
|
|
276
|
+
instanceData.touched = Date.now();
|
|
277
|
+
return [transformData(data)];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return null;
|
|
134
281
|
};
|
|
135
282
|
|
|
136
|
-
export const generateRecordID = (builder, config) => {
|
|
283
|
+
export const generateRecordID = (builder, config, removeLimit) => {
|
|
137
284
|
builder = builder && cloneDeep(builder);
|
|
138
285
|
config = config && cloneDeep(config);
|
|
139
286
|
|
|
@@ -151,16 +298,16 @@ export const generateRecordID = (builder, config) => {
|
|
|
151
298
|
}).filter(([_, v]) => v !== undefined)
|
|
152
299
|
);
|
|
153
300
|
|
|
154
|
-
if (command) recordObj.command = arrangeCommands(command);
|
|
301
|
+
if (command) recordObj.command = arrangeCommands(command, removeLimit);
|
|
155
302
|
if (extraction) {
|
|
156
|
-
if (Array.isArray(extraction)) recordObj.extraction = extraction.map(arrangeCommands);
|
|
303
|
+
if (Array.isArray(extraction)) recordObj.extraction = extraction.map(v => arrangeCommands(v));
|
|
157
304
|
else recordObj.extraction = arrangeCommands(extraction);
|
|
158
305
|
}
|
|
159
306
|
|
|
160
307
|
return niceHash(serializeToBase64(recordObj));
|
|
161
308
|
};
|
|
162
309
|
|
|
163
|
-
const arrangeCommands = c => {
|
|
310
|
+
const arrangeCommands = (c, removeLimit) => {
|
|
164
311
|
c = cloneDeep(c);
|
|
165
312
|
const sortFind = f => {
|
|
166
313
|
['$and', '$or', '$nor'].forEach(n => {
|
|
@@ -174,11 +321,12 @@ const arrangeCommands = c => {
|
|
|
174
321
|
if (c.sort) c.direction = [-1, 'desc', 'descending'].includes(c.direction) ? 'desc' : 'asc';
|
|
175
322
|
if (c.find) c.find = sortFind(c.find);
|
|
176
323
|
if (c.findOne) c.findOne = sortFind(c.findOne);
|
|
324
|
+
if (removeLimit && 'limit' in c) delete c.limit;
|
|
177
325
|
return sortObject(c);
|
|
178
326
|
};
|
|
179
327
|
|
|
180
328
|
const sortObject = (o) => Object.fromEntries(
|
|
181
|
-
Object.entries(o).sort((a, b) => (a > b) ? 1 : (a < b) ? -1 : 0)
|
|
329
|
+
Object.entries(o).sort(([a], [b]) => (a > b) ? 1 : (a < b) ? -1 : 0)
|
|
182
330
|
);
|
|
183
331
|
|
|
184
332
|
const recursiveFlat = (a) => {
|
|
@@ -270,31 +418,88 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
270
418
|
result = result && cloneDeep(result);
|
|
271
419
|
await awaitStore();
|
|
272
420
|
|
|
421
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
273
422
|
const { projectUrl, dbUrl, dbName } = builder;
|
|
274
423
|
const editions = [];
|
|
275
424
|
const duplicateSets = {};
|
|
276
425
|
const pathChanges = new Set([]);
|
|
426
|
+
const pendingSnapshot = cloneDeep(result);
|
|
277
427
|
|
|
278
|
-
(
|
|
428
|
+
await Promise.all((
|
|
279
429
|
result.type === 'batchWrite' ?
|
|
280
430
|
result.value.map(({ scope, value, find, path }) =>
|
|
281
431
|
({ type: scope, value, find, path })
|
|
282
432
|
)
|
|
283
433
|
: [{ ...result, path: builder.path }]
|
|
284
|
-
).
|
|
434
|
+
).map(async ({ value: writeObj, find, type, path }) => {
|
|
285
435
|
WriteValidator[type]({ find, value: writeObj });
|
|
286
436
|
validateCollectionName(path);
|
|
287
437
|
pathChanges.add(path);
|
|
288
|
-
const colObj = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'data'], {});
|
|
289
438
|
|
|
290
|
-
|
|
439
|
+
if (io) {
|
|
440
|
+
const colObj = getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance']);
|
|
441
|
+
|
|
442
|
+
if (colObj)
|
|
443
|
+
await Promise.all(
|
|
444
|
+
Object.entries(colObj).map(e =>
|
|
445
|
+
MutateDataInstance(
|
|
446
|
+
e,
|
|
447
|
+
path =>
|
|
448
|
+
Object.values(
|
|
449
|
+
getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance'], {})
|
|
450
|
+
).map(({ data }) => data).flat()
|
|
451
|
+
)
|
|
452
|
+
)
|
|
453
|
+
);
|
|
454
|
+
} else {
|
|
455
|
+
const sqlite = await openDB(builder);
|
|
456
|
+
try {
|
|
457
|
+
const colListing = await sqlite.executeSql(`SELECT access_id FROM ${LIMITER_DATA(path)}`).then(v =>
|
|
458
|
+
v[0].rows.raw().map(d => d.access_id)
|
|
459
|
+
).catch(() => []);
|
|
460
|
+
const pathFinder = {};
|
|
461
|
+
|
|
462
|
+
await Promise.all(colListing.map(async access_id =>
|
|
463
|
+
useSqliteLinearAccessId(builder, access_id, 'database')(async sqlite => {
|
|
464
|
+
const data = await sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [access_id]).then(v =>
|
|
465
|
+
v[0].rows.raw().map(d => [d.access_id, deserializeBSON(d.value, true)])[0]
|
|
466
|
+
);
|
|
467
|
+
await MutateDataInstance(data, path =>
|
|
468
|
+
pathFinder[path] || (
|
|
469
|
+
pathFinder[path] = sqlite.executeSql(`SELECT value FROM ${LIMITER_DATA(path)}`).then(v =>
|
|
470
|
+
v[0].rows.raw().map(d => deserializeBSON(d.value, true).data).flat()
|
|
471
|
+
).catch(() => [])
|
|
472
|
+
)
|
|
473
|
+
);
|
|
474
|
+
await sqlite.executeSql(
|
|
475
|
+
SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
|
|
476
|
+
[access_id, serializeToBase64(data[1]), Date.now(), data[1].size]
|
|
477
|
+
);
|
|
478
|
+
})
|
|
479
|
+
));
|
|
480
|
+
} catch (error) {
|
|
481
|
+
throw error;
|
|
482
|
+
} finally {
|
|
483
|
+
sqlite.close();
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
async function MutateDataInstance([entityId, dataObj], pathGetter) {
|
|
488
|
+
const { data: instance_data, command, config } = dataObj;
|
|
489
|
+
const entityFind = command.findOne || command.find;
|
|
291
490
|
const { extraction } = config || {};
|
|
292
491
|
|
|
293
492
|
const logChanges = (d) => {
|
|
294
|
-
editions.push([entityId, d, path]);
|
|
493
|
+
editions.push(cloneDeep([entityId, d, path]));
|
|
494
|
+
const [b4, af] = d;
|
|
495
|
+
const offset = docSize(af) - docSize(b4);
|
|
496
|
+
dataObj.size += offset;
|
|
497
|
+
incrementDatabaseSize(builder, path, offset);
|
|
295
498
|
};
|
|
296
499
|
|
|
297
|
-
const
|
|
500
|
+
const snipUpdate = doc => snipDocument(doc, entityFind, config);
|
|
501
|
+
|
|
502
|
+
const accessExtraction = async obj => {
|
|
298
503
|
const buildAssignedExtraction = (data) => {
|
|
299
504
|
const d = (Array.isArray(extraction) ? extraction : [extraction]).map(thisExtraction => {
|
|
300
505
|
const query = cloneDeep(thisExtraction);
|
|
@@ -311,18 +516,18 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
311
516
|
const extractionResultant = buildAssignedExtraction(obj);
|
|
312
517
|
const extractionBinary = serializeToBase64({ _: extractionResultant });
|
|
313
518
|
|
|
314
|
-
const sameProjection =
|
|
519
|
+
const sameProjection = instance_data.find(({ _foreign_doc, ...restDoc }) =>
|
|
315
520
|
extractionBinary === serializeToBase64({ _: buildAssignedExtraction(restDoc) })
|
|
316
521
|
);
|
|
317
522
|
|
|
318
523
|
if (sameProjection) return sameProjection._foreign_doc;
|
|
319
524
|
|
|
320
|
-
// if no matching extraction was found, proceed to scrapping
|
|
321
|
-
const scrapedProjection = (Array.isArray(extractionResultant) ? extractionResultant : [extractionResultant]).map((query, i) => {
|
|
525
|
+
// if no matching extraction was found, proceed to scrapping each _foreign_doc segment
|
|
526
|
+
const scrapedProjection = await Promise.all((Array.isArray(extractionResultant) ? extractionResultant : [extractionResultant]).map(async (query, i) => {
|
|
322
527
|
const { sort, direction, limit, find, findOne, collection: path } = query;
|
|
323
|
-
|
|
528
|
+
let scrapDocs = [];
|
|
324
529
|
|
|
325
|
-
|
|
530
|
+
instance_data.forEach(({ _foreign_doc }) => {
|
|
326
531
|
_foreign_doc = (Array.isArray(_foreign_doc) ? _foreign_doc : [_foreign_doc])[i];
|
|
327
532
|
|
|
328
533
|
recursiveFlat([_foreign_doc]).forEach(e => {
|
|
@@ -331,83 +536,102 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
331
536
|
}
|
|
332
537
|
});
|
|
333
538
|
});
|
|
539
|
+
|
|
334
540
|
if (!scrapDocs.length) {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
});
|
|
541
|
+
// if no matching extraction was found, proceed to scrapping ancestor path
|
|
542
|
+
(await pathGetter(path)).forEach(({ _foreign_doc, ...doc }) => {
|
|
543
|
+
if (confirmFilterDoc(doc, find || findOne)) {
|
|
544
|
+
scrapDocs.push(doc);
|
|
545
|
+
}
|
|
342
546
|
});
|
|
343
547
|
}
|
|
548
|
+
scrapDocs = scrapDocs.filter((v, i, a) => a.findIndex(b => b._id === v._id) === i);
|
|
344
549
|
if (sort) sortArrayByObjectKey(scrapDocs, sort);
|
|
345
550
|
if ([-1, 'desc', 'descending'].includes(direction)) scrapDocs.reverse();
|
|
346
|
-
if (limit) scrapDocs.
|
|
551
|
+
if (limit) scrapDocs = scrapDocs.slice(0, limit);
|
|
552
|
+
scrapDocs = scrapDocs.map(v => snipDocument(v, find || findOne, query));
|
|
347
553
|
|
|
348
554
|
return findOne ? scrapDocs[0] : scrapDocs;
|
|
349
|
-
});
|
|
555
|
+
}));
|
|
350
556
|
|
|
351
|
-
return Array.isArray(extraction) ? scrapedProjection : scrapedProjection[0];
|
|
557
|
+
return cloneDeep(Array.isArray(extraction) ? scrapedProjection : scrapedProjection[0]);
|
|
352
558
|
}
|
|
353
559
|
|
|
354
560
|
if (['setOne', 'setMany'].includes(type)) {
|
|
355
|
-
(type === 'setOne' ? [writeObj] : writeObj).
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
561
|
+
await Promise.all((type === 'setOne' ? [writeObj] : writeObj).map(async e => {
|
|
562
|
+
const obj = deserializeNonAtomicWrite(e);
|
|
563
|
+
if (extraction) obj._foreign_doc = await accessExtraction(obj);
|
|
564
|
+
|
|
565
|
+
if (confirmFilterDoc(obj, entityFind)) {
|
|
566
|
+
|
|
567
|
+
if (instance_data.findIndex(v => CompareBson.equal(v._id, e._id)) === -1) {
|
|
568
|
+
const x = snipUpdate(obj);
|
|
569
|
+
instance_data.push(cloneDeep(x));
|
|
570
|
+
logChanges([undefined, x]);
|
|
571
|
+
} else if (!duplicateSets[e._id]) {
|
|
572
|
+
console.warn(`document with _id=${e._id} already exist locally with ${type}() operation, skipping to online commit`);
|
|
573
|
+
duplicateSets[e._id] = true;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
}));
|
|
366
577
|
return;
|
|
367
578
|
}
|
|
368
579
|
|
|
369
580
|
if (['putOne', 'replaceOne'].includes(type)) {
|
|
370
581
|
const extras = createWriteFromFind(find);
|
|
371
582
|
|
|
372
|
-
|
|
373
|
-
|
|
583
|
+
let deletions = 0;
|
|
584
|
+
const cdata = instance_data.slice(0);
|
|
585
|
+
|
|
586
|
+
for (let i = 0; i < cdata.length; i++) {
|
|
587
|
+
const doc = cdata[i];
|
|
588
|
+
|
|
374
589
|
if (confirmFilterDoc(doc, find)) {
|
|
375
590
|
const obj = deserializeNonAtomicWrite({
|
|
376
591
|
...extras,
|
|
377
592
|
...writeObj,
|
|
378
593
|
...'_id' in extras ? {} : { _id: doc._id }
|
|
379
594
|
});
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
595
|
+
if (extraction) obj._foreign_doc = await accessExtraction(obj);
|
|
596
|
+
|
|
597
|
+
if (confirmFilterDoc(obj, entityFind)) {
|
|
598
|
+
const x = snipUpdate(obj);
|
|
599
|
+
instance_data[i - deletions] = x;
|
|
600
|
+
logChanges([doc, x]);
|
|
601
|
+
} else {
|
|
602
|
+
instance_data.splice(i - deletions++, 1);
|
|
603
|
+
logChanges([doc, undefined]);
|
|
604
|
+
}
|
|
384
605
|
return;
|
|
385
606
|
}
|
|
386
607
|
}
|
|
608
|
+
|
|
387
609
|
if (type === 'putOne') {
|
|
388
610
|
const obj = deserializeNonAtomicWrite({
|
|
389
611
|
...extras,
|
|
390
612
|
...writeObj,
|
|
391
613
|
...'_id' in extras ? {} : { _id: new ObjectId() }
|
|
392
614
|
});
|
|
615
|
+
if (extraction) obj._foreign_doc = await accessExtraction(obj);
|
|
393
616
|
|
|
394
|
-
if (
|
|
395
|
-
|
|
396
|
-
|
|
617
|
+
if (confirmFilterDoc(obj, entityFind)) {
|
|
618
|
+
const x = snipUpdate(obj);
|
|
619
|
+
instance_data.push(x);
|
|
620
|
+
logChanges([undefined, x]);
|
|
621
|
+
}
|
|
397
622
|
}
|
|
398
623
|
return;
|
|
399
624
|
}
|
|
400
625
|
|
|
401
626
|
if (['deleteOne', 'deleteMany'].includes(type)) {
|
|
402
627
|
let deletions = 0;
|
|
628
|
+
const cdata = instance_data.slice(0);
|
|
403
629
|
|
|
404
|
-
for (let i = 0; i <
|
|
405
|
-
const
|
|
406
|
-
const doc = listing[dex];
|
|
630
|
+
for (let i = 0; i < cdata.length; i++) {
|
|
631
|
+
const doc = cdata[i];
|
|
407
632
|
if (confirmFilterDoc(doc, find)) {
|
|
408
|
-
|
|
409
|
-
logChanges([doc]);
|
|
410
|
-
--deletions;
|
|
633
|
+
instance_data.splice(i - deletions++, 1);
|
|
634
|
+
logChanges([doc, undefined]);
|
|
411
635
|
if (type === 'deleteOne') return;
|
|
412
636
|
}
|
|
413
637
|
}
|
|
@@ -415,14 +639,23 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
415
639
|
}
|
|
416
640
|
|
|
417
641
|
let founded;
|
|
418
|
-
|
|
419
|
-
|
|
642
|
+
let deletions = 0;
|
|
643
|
+
const cdata = instance_data.slice(0);
|
|
644
|
+
|
|
645
|
+
for (let i = 0; i < cdata.length; i++) {
|
|
646
|
+
const doc = cdata[i];
|
|
420
647
|
if (confirmFilterDoc(doc, find)) {
|
|
421
648
|
const obj = deserializeAtomicWrite(doc, deserializeWriteValue(writeObj), false, type);
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
649
|
+
if (extraction) obj._foreign_doc = await accessExtraction(obj);
|
|
650
|
+
|
|
651
|
+
if (confirmFilterDoc(obj, entityFind)) {
|
|
652
|
+
const x = snipUpdate(obj);
|
|
653
|
+
instance_data[i - deletions] = x;
|
|
654
|
+
logChanges([doc, x]);
|
|
655
|
+
} else {
|
|
656
|
+
instance_data.splice(i - deletions++, 1);
|
|
657
|
+
logChanges([doc, undefined]);
|
|
658
|
+
}
|
|
426
659
|
|
|
427
660
|
founded = true;
|
|
428
661
|
if (type.endsWith('One')) return;
|
|
@@ -440,22 +673,25 @@ export const addPendingWrites = async (builder, writeId, result) => {
|
|
|
440
673
|
type
|
|
441
674
|
)
|
|
442
675
|
};
|
|
676
|
+
if (extraction) obj._foreign_doc = await accessExtraction(obj);
|
|
443
677
|
|
|
444
|
-
if (
|
|
445
|
-
|
|
446
|
-
|
|
678
|
+
if (confirmFilterDoc(obj, entityFind)) {
|
|
679
|
+
const x = snipUpdate(obj);
|
|
680
|
+
instance_data.push(x);
|
|
681
|
+
logChanges([undefined, x]);
|
|
682
|
+
}
|
|
447
683
|
}
|
|
448
|
-
}
|
|
449
|
-
});
|
|
684
|
+
};
|
|
685
|
+
}));
|
|
450
686
|
|
|
451
687
|
setLodash(CacheStore.PendingWrites, [projectUrl, writeId], cloneDeep({
|
|
452
688
|
builder,
|
|
453
|
-
snapshot:
|
|
689
|
+
snapshot: pendingSnapshot,
|
|
454
690
|
editions,
|
|
455
691
|
addedOn: Date.now()
|
|
456
692
|
}));
|
|
457
693
|
|
|
458
|
-
updateCacheStore();
|
|
694
|
+
updateCacheStore(undefined, ['DatabaseStore', 'PendingWrites', 'DatabaseStats']);
|
|
459
695
|
notifyDatabaseNodeChanges(builder, [...pathChanges]);
|
|
460
696
|
};
|
|
461
697
|
|
|
@@ -463,40 +699,69 @@ export const removePendingWrite = async (builder, writeId, revert) => {
|
|
|
463
699
|
await awaitStore();
|
|
464
700
|
const { projectUrl, dbUrl, dbName } = builder;
|
|
465
701
|
const pendingData = getLodash(CacheStore.PendingWrites, [projectUrl, writeId]);
|
|
702
|
+
const { io } = Scoped.ReleaseCacheData;
|
|
466
703
|
|
|
467
704
|
if (!pendingData) return;
|
|
468
705
|
const pathChanges = new Set([]);
|
|
469
706
|
|
|
470
707
|
if (revert) {
|
|
471
|
-
pendingData.editions.
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
708
|
+
await Promise.all(pendingData.editions.map(async ([access_id, [b4Doc, afDoc], path]) => {
|
|
709
|
+
if (io) {
|
|
710
|
+
RevertMutation(getLodash(CacheStore.DatabaseStore, [projectUrl, dbUrl, dbName, path, 'instance', access_id]));
|
|
711
|
+
} else {
|
|
712
|
+
await useSqliteLinearAccessId(builder, access_id, 'database')(async sqlite => {
|
|
713
|
+
const colObj = await sqlite.executeSql(`SELECT * FROM ${LIMITER_DATA(path)} WHERE access_id = ?`, [access_id]).then(v =>
|
|
714
|
+
v[0].rows.raw().map(d => deserializeBSON(d.value))[0]
|
|
715
|
+
).catch(() => null);
|
|
716
|
+
if (!colObj) return;
|
|
717
|
+
RevertMutation(colObj);
|
|
718
|
+
await sqlite.executeSql(
|
|
719
|
+
SQLITE_COMMANDS.MERGE(LIMITER_DATA(path), ['access_id', 'value', 'touched', 'size']),
|
|
720
|
+
[access_id, serializeToBase64(colObj), Date.now(), colObj.size]
|
|
721
|
+
);
|
|
722
|
+
});
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
function RevertMutation(colObj) {
|
|
726
|
+
const colList = colObj?.data;
|
|
727
|
+
|
|
728
|
+
const updateSize = (b4, af) => {
|
|
729
|
+
const offset = docSize(af) - docSize(b4);
|
|
730
|
+
colObj.size += offset;
|
|
731
|
+
incrementDatabaseSize(builder, path, offset);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
if (colList) {
|
|
735
|
+
if (afDoc) {
|
|
736
|
+
const editedIndex = colList.findIndex(e => CompareBson.equal(e._id, afDoc._id));
|
|
737
|
+
if (editedIndex !== -1) {
|
|
738
|
+
if (
|
|
739
|
+
serializeToBase64(afDoc) === serializeToBase64(colList[editedIndex])
|
|
740
|
+
) {
|
|
741
|
+
if (b4Doc) {
|
|
742
|
+
colList[editedIndex] = b4Doc;
|
|
743
|
+
updateSize(afDoc, b4Doc);
|
|
744
|
+
} else {
|
|
745
|
+
colList.splice(editedIndex, 1);
|
|
746
|
+
updateSize(afDoc, undefined);
|
|
747
|
+
}
|
|
748
|
+
}
|
|
485
749
|
}
|
|
750
|
+
} else if (
|
|
751
|
+
b4Doc &&
|
|
752
|
+
colList.findIndex(e => CompareBson.equal(e._id, b4Doc._id)) === -1
|
|
753
|
+
) {
|
|
754
|
+
colList.push(b4Doc);
|
|
755
|
+
updateSize(undefined, b4Doc);
|
|
486
756
|
}
|
|
487
|
-
} else if (
|
|
488
|
-
b4Doc &&
|
|
489
|
-
colList.findIndex(e => CompareBson.equal(e._id, b4Doc._id)) === -1
|
|
490
|
-
) {
|
|
491
|
-
colList.push(b4Doc);
|
|
492
757
|
}
|
|
758
|
+
pathChanges.add(path);
|
|
493
759
|
}
|
|
494
|
-
|
|
495
|
-
});
|
|
760
|
+
}));
|
|
496
761
|
}
|
|
497
762
|
|
|
498
763
|
unsetLodash(CacheStore.PendingWrites, [projectUrl, writeId]);
|
|
499
|
-
updateCacheStore();
|
|
764
|
+
updateCacheStore(undefined, ['PendingWrites', 'DatabaseStore', 'DatabaseStats']);
|
|
500
765
|
notifyDatabaseNodeChanges(builder, [...pathChanges]);
|
|
501
766
|
};
|
|
502
767
|
|
|
@@ -541,7 +806,7 @@ const snipDocument = (data, find, config) => {
|
|
|
541
806
|
if (!data || !config) return data;
|
|
542
807
|
const { returnOnly, excludeFields } = config || {};
|
|
543
808
|
|
|
544
|
-
let output =
|
|
809
|
+
let output = cloneDeep(data);
|
|
545
810
|
|
|
546
811
|
if (returnOnly) {
|
|
547
812
|
output = {};
|
|
@@ -601,7 +866,6 @@ const deserializeWriteValue = (value) => {
|
|
|
601
866
|
|
|
602
867
|
const deserializeNonAtomicWrite = (writeObj) => deserializeWriteValue(writeObj);
|
|
603
868
|
|
|
604
|
-
|
|
605
869
|
const deserializeAtomicWrite = (b4Doc, writeObj, isNew, type) => {
|
|
606
870
|
const resultantDoc = { ...b4Doc };
|
|
607
871
|
|