envio 3.0.2 → 3.1.0-rc.1
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 +0 -1
- package/evm.schema.json +15 -8
- package/fuel.schema.json +19 -12
- package/index.d.ts +0 -2
- package/package.json +6 -7
- package/rescript.json +1 -1
- package/src/Batch.res +4 -214
- package/src/Batch.res.mjs +6 -165
- package/src/ChainFetcher.res +12 -28
- package/src/ChainFetcher.res.mjs +8 -17
- package/src/ChainManager.res +10 -9
- package/src/ChainManager.res.mjs +6 -10
- package/src/Config.res +9 -25
- package/src/Config.res.mjs +17 -27
- package/src/Core.res +7 -0
- package/src/Ctx.res +1 -0
- package/src/Env.res +0 -8
- package/src/Env.res.mjs +0 -6
- package/src/EventConfigBuilder.res +13 -123
- package/src/EventConfigBuilder.res.mjs +6 -73
- package/src/EventProcessing.res +5 -29
- package/src/EventProcessing.res.mjs +11 -20
- package/src/EventUtils.res +0 -27
- package/src/EventUtils.res.mjs +0 -24
- package/src/FetchState.res +2 -15
- package/src/FetchState.res.mjs +3 -18
- package/src/GlobalState.res +26 -39
- package/src/GlobalState.res.mjs +12 -40
- package/src/HandlerLoader.res +6 -5
- package/src/HandlerLoader.res.mjs +27 -9
- package/src/HandlerRegister.res +1 -12
- package/src/HandlerRegister.res.mjs +1 -6
- package/src/HandlerRegister.resi +1 -1
- package/src/Hasura.res +96 -32
- package/src/Hasura.res.mjs +93 -38
- package/src/InMemoryStore.res +205 -45
- package/src/InMemoryStore.res.mjs +157 -40
- package/src/InMemoryTable.res +165 -249
- package/src/InMemoryTable.res.mjs +156 -227
- package/src/Internal.res +10 -34
- package/src/Internal.res.mjs +9 -3
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +5 -5
- package/src/LogSelection.res +15 -19
- package/src/LogSelection.res.mjs +5 -6
- package/src/Main.res +4 -6
- package/src/Main.res.mjs +26 -15
- package/src/Persistence.res +7 -132
- package/src/Persistence.res.mjs +1 -102
- package/src/PgStorage.res +57 -40
- package/src/PgStorage.res.mjs +60 -34
- package/src/ReorgDetection.res +35 -58
- package/src/ReorgDetection.res.mjs +21 -29
- package/src/SimulateItems.res.mjs +21 -3
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +1 -1
- package/src/TableIndices.res +9 -2
- package/src/TableIndices.res.mjs +7 -1
- package/src/TestIndexer.res +53 -60
- package/src/TestIndexer.res.mjs +77 -63
- package/src/TestIndexerProxyStorage.res +4 -14
- package/src/TestIndexerProxyStorage.res.mjs +1 -5
- package/src/UserContext.res +2 -4
- package/src/UserContext.res.mjs +4 -5
- package/src/Utils.res +0 -2
- package/src/Utils.res.mjs +0 -3
- package/src/bindings/ClickHouse.res +45 -38
- package/src/bindings/ClickHouse.res.mjs +16 -17
- package/src/bindings/Vitest.res +3 -0
- package/src/db/InternalTable.res +59 -18
- package/src/db/InternalTable.res.mjs +82 -51
- package/src/db/Table.res +9 -2
- package/src/db/Table.res.mjs +10 -7
- package/src/sources/EnvioApiClient.res +15 -0
- package/src/sources/EnvioApiClient.res.mjs +24 -0
- package/src/sources/EvmChain.res +32 -10
- package/src/sources/EvmChain.res.mjs +31 -5
- package/src/sources/HyperFuelSource.res +15 -58
- package/src/sources/HyperFuelSource.res.mjs +20 -39
- package/src/sources/HyperSync.res +54 -100
- package/src/sources/HyperSync.res.mjs +67 -96
- package/src/sources/HyperSync.resi +4 -22
- package/src/sources/HyperSyncClient.res +70 -247
- package/src/sources/HyperSyncClient.res.mjs +47 -46
- package/src/sources/HyperSyncSource.res +94 -166
- package/src/sources/HyperSyncSource.res.mjs +100 -127
- package/src/sources/RpcSource.res +43 -22
- package/src/sources/RpcSource.res.mjs +50 -35
- package/src/sources/SimulateSource.res +1 -7
- package/src/sources/SimulateSource.res.mjs +1 -7
- package/src/sources/Source.res +10 -1
- package/src/sources/Source.res.mjs +3 -0
- package/src/sources/SourceManager.res +177 -8
- package/src/sources/SourceManager.res.mjs +141 -3
- package/src/sources/SourceManager.resi +19 -0
- package/src/tui/Tui.res +44 -6
- package/src/tui/Tui.res.mjs +56 -8
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +11 -4
- package/src/sources/HyperSyncJsonApi.res +0 -390
- package/src/sources/HyperSyncJsonApi.res.mjs +0 -237
package/src/InMemoryTable.res
CHANGED
|
@@ -1,55 +1,27 @@
|
|
|
1
|
-
type t<'key, 'val> = {
|
|
2
|
-
dict: dict<'val>,
|
|
3
|
-
hash: 'key => string,
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
let make = (~hash): t<'key, 'val> => {
|
|
7
|
-
dict: Dict.make(),
|
|
8
|
-
hash,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let set = (self: t<'key, 'val>, key, value) => self.dict->Dict.set(key->self.hash, value)
|
|
12
|
-
|
|
13
|
-
let setByHash = (self: t<'key, 'val>, hash, value) => self.dict->Dict.set(hash, value)
|
|
14
|
-
|
|
15
|
-
let hasByHash = (self: t<'key, 'val>, hash) => {
|
|
16
|
-
self.dict->Utils.Dict.has(hash)
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
let getUnsafeByHash = (self: t<'key, 'val>, hash) => {
|
|
20
|
-
self.dict->Dict.getUnsafe(hash)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
let get = (self: t<'key, 'val>, key: 'key) =>
|
|
24
|
-
self.dict->Utils.Dict.dangerouslyGetNonOption(key->self.hash)
|
|
25
|
-
|
|
26
|
-
let values = (self: t<'key, 'val>) => self.dict->Dict.valuesToArray
|
|
27
|
-
|
|
28
|
-
let clone = (self: t<'key, 'val>) => {
|
|
29
|
-
...self,
|
|
30
|
-
dict: self.dict->Lodash.cloneDeep,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
1
|
module Entity = {
|
|
34
2
|
type relatedEntityId = string
|
|
35
3
|
type indexWithRelatedIds = (TableIndices.Index.t, Utils.Set.t<relatedEntityId>)
|
|
36
|
-
|
|
37
|
-
type
|
|
38
|
-
|
|
39
|
-
type
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
4
|
+
// Keyed by TableIndices.Index.toString
|
|
5
|
+
type indicesSerializedToValue = dict<indexWithRelatedIds>
|
|
6
|
+
// Keyed by TableIndices.Index.getFieldName
|
|
7
|
+
type indexFieldNameToIndices = dict<indicesSerializedToValue>
|
|
8
|
+
|
|
9
|
+
type entityIndices = Utils.Set.t<TableIndices.Index.t>
|
|
10
|
+
type t = {
|
|
11
|
+
latestEntityChangeById: dict<Change.t<Internal.entity>>,
|
|
12
|
+
// Counts every recorded change (new latest ids and pushes to
|
|
13
|
+
// prevEntityChanges), kept in sync manually so InMemoryStore can gauge the
|
|
14
|
+
// store size without scanning every dict.
|
|
15
|
+
mutable changesCount: float,
|
|
16
|
+
prevEntityChanges: array<Change.t<Internal.entity>>,
|
|
17
|
+
indicesByEntityId: dict<entityIndices>,
|
|
46
18
|
fieldNameIndices: indexFieldNameToIndices,
|
|
47
19
|
}
|
|
48
20
|
|
|
49
21
|
// Helper to extract entity ID from any entity
|
|
50
22
|
exception UnexpectedIdNotDefinedOnEntity
|
|
51
|
-
let getEntityIdUnsafe = (entity:
|
|
52
|
-
switch (entity->(Utils.magic:
|
|
23
|
+
let getEntityIdUnsafe = (entity: Internal.entity): string =>
|
|
24
|
+
switch (entity->(Utils.magic: Internal.entity => {"id": option<string>}))["id"] {
|
|
53
25
|
| Some(id) => id
|
|
54
26
|
| None =>
|
|
55
27
|
UnexpectedIdNotDefinedOnEntity->ErrorHandling.mkLogAndRaise(
|
|
@@ -57,207 +29,192 @@ module Entity = {
|
|
|
57
29
|
)
|
|
58
30
|
}
|
|
59
31
|
|
|
32
|
+
let getOrCreateEntityIndices = (self: t, ~entityId) =>
|
|
33
|
+
switch self.indicesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
|
|
34
|
+
| Some(s) => s
|
|
35
|
+
| None =>
|
|
36
|
+
let s = Utils.Set.make()
|
|
37
|
+
self.indicesByEntityId->Dict.set(entityId, s)
|
|
38
|
+
s
|
|
39
|
+
}
|
|
40
|
+
|
|
60
41
|
let makeIndicesSerializedToValue = (
|
|
61
42
|
~index,
|
|
62
43
|
~relatedEntityIds=Utils.Set.make(),
|
|
63
44
|
): indicesSerializedToValue => {
|
|
64
|
-
let empty = make(
|
|
65
|
-
empty->set(index, (index, relatedEntityIds))
|
|
45
|
+
let empty = Dict.make()
|
|
46
|
+
empty->Dict.set(index->TableIndices.Index.toString, (index, relatedEntityIds))
|
|
66
47
|
empty
|
|
67
48
|
}
|
|
68
49
|
|
|
69
|
-
let make = (): t
|
|
70
|
-
|
|
71
|
-
|
|
50
|
+
let make = (): t => {
|
|
51
|
+
latestEntityChangeById: Dict.make(),
|
|
52
|
+
changesCount: 0.,
|
|
53
|
+
prevEntityChanges: [],
|
|
54
|
+
indicesByEntityId: Dict.make(),
|
|
55
|
+
fieldNameIndices: Dict.make(),
|
|
72
56
|
}
|
|
73
57
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
58
|
+
// Drops the per-batch index state and rollback history, but keeps the
|
|
59
|
+
// already committed entities so the next batch can read them without
|
|
60
|
+
// hitting the database.
|
|
61
|
+
let resetButKeepLatestChanges = (self: t): t => {
|
|
62
|
+
...make(),
|
|
63
|
+
latestEntityChangeById: self.latestEntityChangeById,
|
|
64
|
+
// writeBatch already mutated this to subtract the dropped prevEntityChanges.
|
|
65
|
+
changesCount: self.changesCount,
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Like resetButKeepLatestChanges, but only keeps entities loaded from the db
|
|
69
|
+
// (changes carrying loadedFromDbCheckpointId), dropping everything written in
|
|
70
|
+
// a batch. The kept count is exposed through the table's changesCount.
|
|
71
|
+
let resetButKeepLoadedFromDbChanges = (self: t): t => {
|
|
72
|
+
let latestEntityChangeById = Dict.make()
|
|
73
|
+
let keptCount = ref(0.)
|
|
74
|
+
self.latestEntityChangeById->Utils.Dict.forEachWithKey((change, key) =>
|
|
75
|
+
if change->Change.getCheckpointId === Internal.loadedFromDbCheckpointId {
|
|
76
|
+
latestEntityChangeById->Dict.set(key, change)
|
|
77
|
+
keptCount := keptCount.contents +. 1.
|
|
88
78
|
}
|
|
89
|
-
|
|
79
|
+
)
|
|
80
|
+
{
|
|
81
|
+
...make(),
|
|
82
|
+
latestEntityChangeById,
|
|
83
|
+
changesCount: keptCount.contents,
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let updateIndices = (self: t, ~entity: Internal.entity) => {
|
|
88
|
+
let entityId = entity->getEntityIdUnsafe
|
|
89
|
+
//Remove any invalid indices on entity
|
|
90
|
+
switch self.indicesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
|
|
91
|
+
| None => ()
|
|
92
|
+
| Some(entityIndices) =>
|
|
93
|
+
entityIndices->Utils.Set.forEach(index => {
|
|
94
|
+
let fieldName = index->TableIndices.Index.getFieldName
|
|
95
|
+
let fieldValue =
|
|
96
|
+
entity
|
|
97
|
+
->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
|
|
98
|
+
->Dict.getUnsafe(fieldName)
|
|
99
|
+
if !(index->TableIndices.Index.evaluate(~fieldName, ~fieldValue)) {
|
|
100
|
+
entityIndices->Utils.Set.delete(index)->ignore
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
}
|
|
90
104
|
|
|
91
|
-
self.fieldNameIndices.
|
|
92
|
-
->Dict.keysToArray
|
|
93
|
-
->Array.forEach(fieldName => {
|
|
94
|
-
let indices = self.fieldNameIndices.dict->Dict.getUnsafe(fieldName)
|
|
105
|
+
self.fieldNameIndices->Utils.Dict.forEachWithKey((indices, fieldName) => {
|
|
95
106
|
// A missing key reads as `undefined`, which matches the `None` arm of
|
|
96
107
|
// `FieldValue.t` (`option<...>`). Mirror `addEmptyIndex` so nullable
|
|
97
108
|
// FK columns that were omitted on the set entity don't crash.
|
|
98
109
|
let fieldValue =
|
|
99
110
|
entity
|
|
100
|
-
->(Utils.magic:
|
|
111
|
+
->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
|
|
101
112
|
->Dict.getUnsafe(fieldName)
|
|
102
|
-
indices
|
|
103
|
-
->values
|
|
104
|
-
->Array.forEach(((index, relatedEntityIds)) => {
|
|
113
|
+
indices->Utils.Dict.forEach(((index, relatedEntityIds)) => {
|
|
105
114
|
if index->TableIndices.Index.evaluate(~fieldName, ~fieldValue) {
|
|
106
115
|
//Add entity id to indices and add index to entity indicies
|
|
107
|
-
relatedEntityIds->Utils.Set.add(
|
|
108
|
-
|
|
116
|
+
relatedEntityIds->Utils.Set.add(entityId)->ignore
|
|
117
|
+
self->getOrCreateEntityIndices(~entityId)->Utils.Set.add(index)->ignore
|
|
109
118
|
} else {
|
|
110
|
-
relatedEntityIds->Utils.Set.delete(
|
|
119
|
+
relatedEntityIds->Utils.Set.delete(entityId)->ignore
|
|
111
120
|
}
|
|
112
121
|
})
|
|
113
122
|
})
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
let deleteEntityFromIndices = (self: t
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
->
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
~allowOverWriteEntity=false,
|
|
134
|
-
) => {
|
|
135
|
-
let shouldWriteEntity =
|
|
136
|
-
allowOverWriteEntity ||
|
|
137
|
-
inMemTable.table.dict->Dict.get(key->inMemTable.table.hash)->Option.isNone
|
|
125
|
+
let deleteEntityFromIndices = (self: t, ~entityId: string) =>
|
|
126
|
+
switch self.indicesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
|
|
127
|
+
| None => ()
|
|
128
|
+
| Some(entityIndices) =>
|
|
129
|
+
entityIndices->Utils.Set.forEach(index => {
|
|
130
|
+
switch self.fieldNameIndices
|
|
131
|
+
->Utils.Dict.dangerouslyGetNonOption(index->TableIndices.Index.getFieldName)
|
|
132
|
+
->Option.flatMap(indices =>
|
|
133
|
+
indices->Utils.Dict.dangerouslyGetNonOption(index->TableIndices.Index.toString)
|
|
134
|
+
) {
|
|
135
|
+
| Some((_index, relatedEntityIds)) =>
|
|
136
|
+
let _wasRemoved = relatedEntityIds->Utils.Set.delete(entityId)
|
|
137
|
+
| None => () //Unexpected index should exist if it is entityIndices
|
|
138
|
+
}
|
|
139
|
+
let _wasRemoved = entityIndices->Utils.Set.delete(index)
|
|
140
|
+
})
|
|
141
|
+
}
|
|
138
142
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
let set = (inMemTable: t, ~committedCheckpointId, change: Change.t<Internal.entity>) => {
|
|
144
|
+
let entityId = change->Change.getEntityId
|
|
145
|
+
switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityId) {
|
|
146
|
+
| Some(prev) =>
|
|
147
|
+
let prevCheckpointId = prev->Change.getCheckpointId
|
|
148
|
+
if (
|
|
149
|
+
prevCheckpointId > committedCheckpointId &&
|
|
150
|
+
prevCheckpointId < change->Change.getCheckpointId
|
|
151
|
+
) {
|
|
152
|
+
inMemTable.prevEntityChanges->Array.push(prev)
|
|
153
|
+
inMemTable.changesCount = inMemTable.changesCount +. 1.
|
|
149
154
|
}
|
|
150
|
-
|
|
151
|
-
key->inMemTable.table.hash,
|
|
152
|
-
{
|
|
153
|
-
latest: entity,
|
|
154
|
-
status: Loaded,
|
|
155
|
-
entityIndices,
|
|
156
|
-
},
|
|
157
|
-
)
|
|
155
|
+
| None => inMemTable.changesCount = inMemTable.changesCount +. 1.
|
|
158
156
|
}
|
|
159
|
-
}
|
|
160
157
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
inMemTable
|
|
164
|
-
change: Change.t<'entity>,
|
|
165
|
-
~shouldSaveHistory,
|
|
166
|
-
~containsRollbackDiffChange=false,
|
|
167
|
-
) => {
|
|
168
|
-
//New entity row with only the latest update
|
|
169
|
-
@inline
|
|
170
|
-
let newStatus = () => Internal.Updated({
|
|
171
|
-
latestChange: change,
|
|
172
|
-
history: shouldSaveHistory
|
|
173
|
-
? [change]
|
|
174
|
-
: Utils.Array.immutableEmpty->(Utils.magic: array<unknown> => array<Change.t<'entity>>),
|
|
175
|
-
containsRollbackDiffChange,
|
|
176
|
-
})
|
|
177
|
-
let latest = switch change {
|
|
178
|
-
| Set({entity}) => Some(entity)
|
|
179
|
-
| Delete(_) => None
|
|
158
|
+
switch change {
|
|
159
|
+
| Set({entity}) => inMemTable->updateIndices(~entity)
|
|
160
|
+
| Delete({entityId}) => inMemTable->deleteEntityFromIndices(~entityId)
|
|
180
161
|
}
|
|
162
|
+
inMemTable.latestEntityChangeById->Dict.set(entityId, change)
|
|
163
|
+
}
|
|
181
164
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
165
|
+
// Only writes when the id isn't already present, so set always takes its
|
|
166
|
+
// None branch here (committedCheckpointId is never read).
|
|
167
|
+
let initValue = (
|
|
168
|
+
inMemTable: t,
|
|
169
|
+
~committedCheckpointId,
|
|
170
|
+
~key: string,
|
|
171
|
+
~entity: option<Internal.entity>,
|
|
172
|
+
) =>
|
|
173
|
+
if inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(key)->Option.isNone {
|
|
174
|
+
let change: Change.t<Internal.entity> = switch entity {
|
|
175
|
+
| Some(entity) =>
|
|
176
|
+
Set({entityId: key, entity, checkpointId: Internal.loadedFromDbCheckpointId})
|
|
177
|
+
| None => Delete({entityId: key, checkpointId: Internal.loadedFromDbCheckpointId})
|
|
188
178
|
}
|
|
189
|
-
|
|
190
|
-
let newStatus = Internal.Updated({
|
|
191
|
-
latestChange: change,
|
|
192
|
-
history: switch shouldSaveHistory {
|
|
193
|
-
// This prevents two db actions in the same event on the same entity from being recorded to the history table.
|
|
194
|
-
| true
|
|
195
|
-
if previous_values.latestChange->Change.getCheckpointId ===
|
|
196
|
-
change->Change.getCheckpointId =>
|
|
197
|
-
previous_values.history->Utils.Array.setIndexImmutable(
|
|
198
|
-
previous_values.history->Array.length - 1,
|
|
199
|
-
change,
|
|
200
|
-
)
|
|
201
|
-
| true => [...previous_values.history, change]
|
|
202
|
-
| false => previous_values.history
|
|
203
|
-
},
|
|
204
|
-
containsRollbackDiffChange: previous_values.containsRollbackDiffChange,
|
|
205
|
-
})
|
|
206
|
-
{latest, status: newStatus, entityIndices}
|
|
179
|
+
inMemTable->set(~committedCheckpointId, change)
|
|
207
180
|
}
|
|
208
181
|
|
|
182
|
+
let mapChangeToEntity = (change: Change.t<Internal.entity>) =>
|
|
209
183
|
switch change {
|
|
210
|
-
| Set({entity}) =>
|
|
211
|
-
|
|
212
|
-
| Delete({entityId}) =>
|
|
213
|
-
inMemTable->deleteEntityFromIndices(
|
|
214
|
-
~entityId,
|
|
215
|
-
~entityIndices=updatedEntityRecord.entityIndices,
|
|
216
|
-
)
|
|
184
|
+
| Set({entity}) => Some(entity)
|
|
185
|
+
| Delete(_) => None
|
|
217
186
|
}
|
|
218
|
-
inMemTable.table->setRow(change->Change.getEntityId, updatedEntityRecord)
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
let rowToEntity = row => row.latest
|
|
222
|
-
|
|
223
|
-
let getRow = get
|
|
224
187
|
|
|
225
188
|
/** It returns option<option<'entity>> where the first option means
|
|
226
189
|
that the entity is not set to the in memory store,
|
|
227
190
|
and the second option means that the entity doesn't esist/deleted.
|
|
228
191
|
It's needed to prevent an additional round trips to the database for deleted entities. */
|
|
229
|
-
let getUnsafe = (inMemTable: t
|
|
192
|
+
let getUnsafe = (inMemTable: t) =>
|
|
230
193
|
(key: string) =>
|
|
231
|
-
inMemTable.
|
|
194
|
+
inMemTable.latestEntityChangeById
|
|
232
195
|
->Dict.getUnsafe(key)
|
|
233
|
-
->
|
|
196
|
+
->mapChangeToEntity
|
|
234
197
|
|
|
235
|
-
let hasIndex = (inMemTable: t
|
|
198
|
+
let hasIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) =>
|
|
236
199
|
fieldValueHash => {
|
|
237
|
-
switch inMemTable.fieldNameIndices
|
|
200
|
+
switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
|
|
238
201
|
| None => false
|
|
239
202
|
| Some(indicesSerializedToValue) => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
indicesSerializedToValue.dict->Utils.Dict.dangerouslyGetNonOption(key) !== None
|
|
203
|
+
let key = TableIndices.Index.toStringByParts(~fieldName, ~operator, ~fieldValueHash)
|
|
204
|
+
indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) !== None
|
|
243
205
|
}
|
|
244
206
|
}
|
|
245
207
|
}
|
|
246
208
|
|
|
247
|
-
let getUnsafeOnIndex = (
|
|
248
|
-
inMemTable: t<'entity>,
|
|
249
|
-
~fieldName,
|
|
250
|
-
~operator: TableIndices.Operator.t,
|
|
251
|
-
) => {
|
|
209
|
+
let getUnsafeOnIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) => {
|
|
252
210
|
let getEntity = inMemTable->getUnsafe
|
|
253
211
|
fieldValueHash => {
|
|
254
|
-
switch inMemTable.fieldNameIndices
|
|
212
|
+
switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
|
|
255
213
|
| None =>
|
|
256
214
|
JsError.throwWithMessage(`Unexpected error. Must have an index on field ${fieldName}`)
|
|
257
215
|
| Some(indicesSerializedToValue) => {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
switch indicesSerializedToValue.dict->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
216
|
+
let key = TableIndices.Index.toStringByParts(~fieldName, ~operator, ~fieldValueHash)
|
|
217
|
+
switch indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) {
|
|
261
218
|
| None =>
|
|
262
219
|
JsError.throwWithMessage(
|
|
263
220
|
`Unexpected error. Must have an index for the value ${fieldValueHash} on field ${fieldName}`,
|
|
@@ -267,7 +224,7 @@ module Entity = {
|
|
|
267
224
|
relatedEntityIds
|
|
268
225
|
->Utils.Set.toArray
|
|
269
226
|
->Array.filterMap(entityId => {
|
|
270
|
-
switch
|
|
227
|
+
switch inMemTable.latestEntityChangeById->Dict.has(entityId) {
|
|
271
228
|
| true => getEntity(entityId)
|
|
272
229
|
| false => None
|
|
273
230
|
}
|
|
@@ -280,83 +237,42 @@ module Entity = {
|
|
|
280
237
|
}
|
|
281
238
|
}
|
|
282
239
|
|
|
283
|
-
let addEmptyIndex = (inMemTable: t
|
|
240
|
+
let addEmptyIndex = (inMemTable: t, ~index) => {
|
|
284
241
|
let fieldName = index->TableIndices.Index.getFieldName
|
|
285
242
|
let relatedEntityIds = Utils.Set.make()
|
|
286
243
|
|
|
287
|
-
inMemTable.
|
|
288
|
-
|
|
289
|
-
->Array.forEach(row => {
|
|
290
|
-
switch row->rowToEntity {
|
|
244
|
+
inMemTable.latestEntityChangeById->Utils.Dict.forEach(change => {
|
|
245
|
+
switch change->mapChangeToEntity {
|
|
291
246
|
| Some(entity) =>
|
|
292
247
|
let fieldValue =
|
|
293
248
|
entity
|
|
294
|
-
->(Utils.magic:
|
|
249
|
+
->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
|
|
295
250
|
->Dict.getUnsafe(fieldName)
|
|
296
251
|
if index->TableIndices.Index.evaluate(~fieldName, ~fieldValue) {
|
|
297
|
-
let
|
|
298
|
-
let _ =
|
|
252
|
+
let entityId = entity->getEntityIdUnsafe
|
|
253
|
+
let _ = inMemTable->getOrCreateEntityIndices(~entityId)->Utils.Set.add(index)
|
|
254
|
+
let _ = relatedEntityIds->Utils.Set.add(entityId)
|
|
299
255
|
}
|
|
300
256
|
| None => ()
|
|
301
257
|
}
|
|
302
258
|
})
|
|
303
|
-
switch inMemTable.fieldNameIndices->
|
|
259
|
+
switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
|
|
304
260
|
| None =>
|
|
305
|
-
inMemTable.fieldNameIndices->
|
|
306
|
-
|
|
261
|
+
inMemTable.fieldNameIndices->Dict.set(
|
|
262
|
+
fieldName,
|
|
307
263
|
makeIndicesSerializedToValue(~index, ~relatedEntityIds),
|
|
308
264
|
)
|
|
309
265
|
| Some(indicesSerializedToValue) =>
|
|
310
|
-
switch indicesSerializedToValue->
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
let addIdToIndex = (inMemTable: t<'entity>, ~index, ~entityId) =>
|
|
318
|
-
switch inMemTable.fieldNameIndices->getRow(index) {
|
|
319
|
-
| None =>
|
|
320
|
-
inMemTable.fieldNameIndices->setRow(
|
|
321
|
-
index,
|
|
322
|
-
makeIndicesSerializedToValue(
|
|
323
|
-
~index,
|
|
324
|
-
~relatedEntityIds=Utils.Set.make()->Utils.Set.add(entityId),
|
|
325
|
-
),
|
|
326
|
-
)
|
|
327
|
-
| Some(indicesSerializedToValue) =>
|
|
328
|
-
switch indicesSerializedToValue->getRow(index) {
|
|
266
|
+
switch indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(
|
|
267
|
+
index->TableIndices.Index.toString,
|
|
268
|
+
) {
|
|
329
269
|
| None =>
|
|
330
|
-
indicesSerializedToValue->
|
|
331
|
-
|
|
270
|
+
indicesSerializedToValue->Dict.set(
|
|
271
|
+
index->TableIndices.Index.toString,
|
|
272
|
+
(index, relatedEntityIds),
|
|
273
|
+
)
|
|
274
|
+
| Some(_) => () //Should not happen, this means the index already exists
|
|
332
275
|
}
|
|
333
276
|
}
|
|
334
|
-
|
|
335
|
-
let updates = (inMemTable: t<'entity>) => {
|
|
336
|
-
inMemTable.table
|
|
337
|
-
->values
|
|
338
|
-
->Array.filterMap(v =>
|
|
339
|
-
switch v.status {
|
|
340
|
-
| Updated(update) => Some(update)
|
|
341
|
-
| Loaded => None
|
|
342
|
-
}
|
|
343
|
-
)
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
let values = (inMemTable: t<'entity>) => {
|
|
347
|
-
inMemTable.table
|
|
348
|
-
->values
|
|
349
|
-
->Array.filterMap(rowToEntity)
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
let clone = ({table, fieldNameIndices}: t<'entity>) => {
|
|
353
|
-
table: table->clone,
|
|
354
|
-
fieldNameIndices: {
|
|
355
|
-
...fieldNameIndices,
|
|
356
|
-
dict: fieldNameIndices.dict
|
|
357
|
-
->Dict.toArray
|
|
358
|
-
->Array.map(((k, v)) => (k, v->clone))
|
|
359
|
-
->Dict.fromArray,
|
|
360
|
-
},
|
|
361
277
|
}
|
|
362
278
|
}
|