envio 3.0.2 → 3.1.0-rc.0

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.
Files changed (91) hide show
  1. package/README.md +0 -1
  2. package/evm.schema.json +15 -8
  3. package/fuel.schema.json +19 -12
  4. package/index.d.ts +0 -2
  5. package/package.json +6 -7
  6. package/rescript.json +1 -1
  7. package/src/Batch.res +4 -214
  8. package/src/Batch.res.mjs +6 -165
  9. package/src/ChainFetcher.res +4 -5
  10. package/src/ChainFetcher.res.mjs +6 -7
  11. package/src/ChainManager.res +10 -9
  12. package/src/ChainManager.res.mjs +6 -10
  13. package/src/Config.res +9 -25
  14. package/src/Config.res.mjs +17 -27
  15. package/src/Core.res +7 -0
  16. package/src/Ctx.res +1 -0
  17. package/src/Env.res +0 -1
  18. package/src/Env.res.mjs +0 -3
  19. package/src/EventConfigBuilder.res +13 -123
  20. package/src/EventConfigBuilder.res.mjs +6 -73
  21. package/src/EventProcessing.res +5 -29
  22. package/src/EventProcessing.res.mjs +11 -20
  23. package/src/EventUtils.res +0 -27
  24. package/src/EventUtils.res.mjs +0 -24
  25. package/src/FetchState.res +1 -11
  26. package/src/FetchState.res.mjs +2 -16
  27. package/src/GlobalState.res +23 -37
  28. package/src/GlobalState.res.mjs +10 -38
  29. package/src/HandlerLoader.res +6 -5
  30. package/src/HandlerLoader.res.mjs +27 -9
  31. package/src/HandlerRegister.res +1 -12
  32. package/src/HandlerRegister.res.mjs +1 -6
  33. package/src/HandlerRegister.resi +1 -1
  34. package/src/Hasura.res +96 -32
  35. package/src/Hasura.res.mjs +93 -38
  36. package/src/InMemoryStore.res +181 -45
  37. package/src/InMemoryStore.res.mjs +143 -40
  38. package/src/InMemoryTable.res +147 -247
  39. package/src/InMemoryTable.res.mjs +131 -230
  40. package/src/Internal.res +10 -34
  41. package/src/Internal.res.mjs +9 -3
  42. package/src/LoadLayer.res +5 -5
  43. package/src/LoadLayer.res.mjs +5 -5
  44. package/src/Main.res +4 -6
  45. package/src/Main.res.mjs +26 -15
  46. package/src/Persistence.res +7 -132
  47. package/src/Persistence.res.mjs +1 -102
  48. package/src/PgStorage.res +57 -40
  49. package/src/PgStorage.res.mjs +60 -34
  50. package/src/ReorgDetection.res +35 -58
  51. package/src/ReorgDetection.res.mjs +21 -29
  52. package/src/SimulateItems.res.mjs +21 -3
  53. package/src/Sink.res +2 -2
  54. package/src/Sink.res.mjs +1 -1
  55. package/src/TableIndices.res +9 -2
  56. package/src/TableIndices.res.mjs +7 -1
  57. package/src/TestIndexer.res +53 -60
  58. package/src/TestIndexer.res.mjs +77 -63
  59. package/src/TestIndexerProxyStorage.res +4 -14
  60. package/src/TestIndexerProxyStorage.res.mjs +1 -5
  61. package/src/UserContext.res +2 -4
  62. package/src/UserContext.res.mjs +4 -5
  63. package/src/Utils.res +0 -2
  64. package/src/Utils.res.mjs +0 -3
  65. package/src/bindings/ClickHouse.res +45 -38
  66. package/src/bindings/ClickHouse.res.mjs +16 -17
  67. package/src/bindings/Vitest.res +3 -0
  68. package/src/db/InternalTable.res +59 -18
  69. package/src/db/InternalTable.res.mjs +82 -51
  70. package/src/db/Table.res +9 -2
  71. package/src/db/Table.res.mjs +10 -7
  72. package/src/sources/EvmChain.res +32 -9
  73. package/src/sources/EvmChain.res.mjs +31 -4
  74. package/src/sources/HyperFuelSource.res +14 -57
  75. package/src/sources/HyperFuelSource.res.mjs +18 -38
  76. package/src/sources/HyperSync.res +36 -101
  77. package/src/sources/HyperSync.res.mjs +42 -96
  78. package/src/sources/HyperSync.resi +4 -22
  79. package/src/sources/HyperSyncClient.res +67 -245
  80. package/src/sources/HyperSyncClient.res.mjs +47 -46
  81. package/src/sources/HyperSyncSource.res +76 -147
  82. package/src/sources/HyperSyncSource.res.mjs +61 -114
  83. package/src/sources/RpcSource.res +43 -22
  84. package/src/sources/RpcSource.res.mjs +50 -35
  85. package/src/sources/SimulateSource.res +1 -7
  86. package/src/sources/SimulateSource.res.mjs +1 -7
  87. package/src/sources/Source.res +8 -1
  88. package/src/sources/SourceManager.res +9 -0
  89. package/src/sources/SourceManager.res.mjs +10 -0
  90. package/src/sources/SourceManager.resi +2 -0
  91. package/svm.schema.json +11 -4
@@ -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
- type indicesSerializedToValue = t<TableIndices.Index.t, indexWithRelatedIds>
37
- type indexFieldNameToIndices = t<TableIndices.Index.t, indicesSerializedToValue>
38
-
39
- type entityWithIndices<'entity> = {
40
- latest: option<'entity>,
41
- status: Internal.inMemoryStoreEntityStatus<'entity>,
42
- entityIndices: Utils.Set.t<TableIndices.Index.t>,
43
- }
44
- type t<'entity> = {
45
- table: t<string, entityWithIndices<'entity>>,
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: 'entity): string =>
52
- switch (entity->(Utils.magic: 'entity => {"id": option<string>}))["id"] {
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,176 @@ 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(~hash=TableIndices.Index.toString)
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<'entity> => {
70
- table: make(~hash=str => str),
71
- fieldNameIndices: make(~hash=TableIndices.Index.getFieldName),
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
- let updateIndices = (
75
- self: t<'entity>,
76
- ~entity: 'entity,
77
- ~entityIndices: Utils.Set.t<TableIndices.Index.t>,
78
- ) => {
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
+ let updateIndices = (self: t, ~entity: Internal.entity) => {
69
+ let entityId = entity->getEntityIdUnsafe
79
70
  //Remove any invalid indices on entity
80
- entityIndices->Utils.Set.forEach(index => {
81
- let fieldName = index->TableIndices.Index.getFieldName
82
- let fieldValue =
83
- entity
84
- ->(Utils.magic: 'entity => dict<TableIndices.FieldValue.t>)
85
- ->Dict.getUnsafe(fieldName)
86
- if !(index->TableIndices.Index.evaluate(~fieldName, ~fieldValue)) {
87
- entityIndices->Utils.Set.delete(index)->ignore
88
- }
89
- })
71
+ switch self.indicesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
72
+ | None => ()
73
+ | Some(entityIndices) =>
74
+ entityIndices->Utils.Set.forEach(index => {
75
+ let fieldName = index->TableIndices.Index.getFieldName
76
+ let fieldValue =
77
+ entity
78
+ ->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
79
+ ->Dict.getUnsafe(fieldName)
80
+ if !(index->TableIndices.Index.evaluate(~fieldName, ~fieldValue)) {
81
+ entityIndices->Utils.Set.delete(index)->ignore
82
+ }
83
+ })
84
+ }
90
85
 
91
- self.fieldNameIndices.dict
86
+ self.fieldNameIndices
92
87
  ->Dict.keysToArray
93
88
  ->Array.forEach(fieldName => {
94
- let indices = self.fieldNameIndices.dict->Dict.getUnsafe(fieldName)
89
+ let indices = self.fieldNameIndices->Dict.getUnsafe(fieldName)
95
90
  // A missing key reads as `undefined`, which matches the `None` arm of
96
91
  // `FieldValue.t` (`option<...>`). Mirror `addEmptyIndex` so nullable
97
92
  // FK columns that were omitted on the set entity don't crash.
98
93
  let fieldValue =
99
94
  entity
100
- ->(Utils.magic: 'entity => dict<TableIndices.FieldValue.t>)
95
+ ->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
101
96
  ->Dict.getUnsafe(fieldName)
102
- indices
103
- ->values
104
- ->Array.forEach(((index, relatedEntityIds)) => {
97
+ indices->Utils.Dict.forEach(((index, relatedEntityIds)) => {
105
98
  if index->TableIndices.Index.evaluate(~fieldName, ~fieldValue) {
106
99
  //Add entity id to indices and add index to entity indicies
107
- relatedEntityIds->Utils.Set.add(getEntityIdUnsafe(entity))->ignore
108
- entityIndices->Utils.Set.add(index)->ignore
100
+ relatedEntityIds->Utils.Set.add(entityId)->ignore
101
+ self->getOrCreateEntityIndices(~entityId)->Utils.Set.add(index)->ignore
109
102
  } else {
110
- relatedEntityIds->Utils.Set.delete(getEntityIdUnsafe(entity))->ignore
103
+ relatedEntityIds->Utils.Set.delete(entityId)->ignore
111
104
  }
112
105
  })
113
106
  })
114
107
  }
115
108
 
116
- let deleteEntityFromIndices = (self: t<'entity>, ~entityId: string, ~entityIndices) =>
117
- entityIndices->Utils.Set.forEach(index => {
118
- switch self.fieldNameIndices
119
- ->get(index)
120
- ->Option.flatMap(get(_, index)) {
121
- | Some((_index, relatedEntityIds)) =>
122
- let _wasRemoved = relatedEntityIds->Utils.Set.delete(entityId)
123
- | None => () //Unexpected index should exist if it is entityIndices
124
- }
125
- let _wasRemoved = entityIndices->Utils.Set.delete(index)
126
- })
127
-
128
- let initValue = (
129
- inMemTable: t<'entity>,
130
- ~key: string,
131
- ~entity: option<'entity>,
132
- // NOTE: This value is only set to true in the internals of the test framework to create the mockDb.
133
- ~allowOverWriteEntity=false,
134
- ) => {
135
- let shouldWriteEntity =
136
- allowOverWriteEntity ||
137
- inMemTable.table.dict->Dict.get(key->inMemTable.table.hash)->Option.isNone
109
+ let deleteEntityFromIndices = (self: t, ~entityId: string) =>
110
+ switch self.indicesByEntityId->Utils.Dict.dangerouslyGetNonOption(entityId) {
111
+ | None => ()
112
+ | Some(entityIndices) =>
113
+ entityIndices->Utils.Set.forEach(index => {
114
+ switch self.fieldNameIndices
115
+ ->Utils.Dict.dangerouslyGetNonOption(index->TableIndices.Index.getFieldName)
116
+ ->Option.flatMap(indices =>
117
+ indices->Utils.Dict.dangerouslyGetNonOption(index->TableIndices.Index.toString)
118
+ ) {
119
+ | Some((_index, relatedEntityIds)) =>
120
+ let _wasRemoved = relatedEntityIds->Utils.Set.delete(entityId)
121
+ | None => () //Unexpected index should exist if it is entityIndices
122
+ }
123
+ let _wasRemoved = entityIndices->Utils.Set.delete(index)
124
+ })
125
+ }
138
126
 
139
- //Only initialize a row in the case where it is none
140
- //or if allowOverWriteEntity is true (used for mockDb in test helpers)
141
- if shouldWriteEntity {
142
- let entityIndices = Utils.Set.make()
143
- switch entity {
144
- | Some(entity) =>
145
- //update table indices in the case where there
146
- //is an already set entity
147
- inMemTable->updateIndices(~entity, ~entityIndices)
148
- | None => ()
127
+ let set = (inMemTable: t, ~committedCheckpointId, change: Change.t<Internal.entity>) => {
128
+ let entityId = change->Change.getEntityId
129
+ switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityId) {
130
+ | Some(prev) =>
131
+ let prevCheckpointId = prev->Change.getCheckpointId
132
+ if (
133
+ prevCheckpointId > committedCheckpointId &&
134
+ prevCheckpointId < change->Change.getCheckpointId
135
+ ) {
136
+ inMemTable.prevEntityChanges->Array.push(prev)
137
+ inMemTable.changesCount = inMemTable.changesCount +. 1.
149
138
  }
150
- inMemTable.table.dict->Dict.set(
151
- key->inMemTable.table.hash,
152
- {
153
- latest: entity,
154
- status: Loaded,
155
- entityIndices,
156
- },
157
- )
139
+ | None => inMemTable.changesCount = inMemTable.changesCount +. 1.
158
140
  }
159
- }
160
141
 
161
- let setRow = set
162
- let set = (
163
- inMemTable: t<'entity>,
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
142
+ switch change {
143
+ | Set({entity}) => inMemTable->updateIndices(~entity)
144
+ | Delete({entityId}) => inMemTable->deleteEntityFromIndices(~entityId)
180
145
  }
146
+ inMemTable.latestEntityChangeById->Dict.set(entityId, change)
147
+ }
181
148
 
182
- let updatedEntityRecord = switch inMemTable.table->get(change->Change.getEntityId) {
183
- | None => {latest, status: newStatus(), entityIndices: Utils.Set.make()}
184
- | Some({status: Loaded, entityIndices}) => {
185
- latest,
186
- status: newStatus(),
187
- entityIndices,
149
+ // Only writes when the id isn't already present, so set always takes its
150
+ // None branch here (committedCheckpointId is never read).
151
+ let initValue = (
152
+ inMemTable: t,
153
+ ~committedCheckpointId,
154
+ ~key: string,
155
+ ~entity: option<Internal.entity>,
156
+ ) =>
157
+ if inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(key)->Option.isNone {
158
+ let change: Change.t<Internal.entity> = switch entity {
159
+ | Some(entity) =>
160
+ Set({entityId: key, entity, checkpointId: Internal.loadedFromDbCheckpointId})
161
+ | None => Delete({entityId: key, checkpointId: Internal.loadedFromDbCheckpointId})
188
162
  }
189
- | Some({status: Updated(previous_values), entityIndices}) =>
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}
163
+ inMemTable->set(~committedCheckpointId, change)
207
164
  }
208
165
 
166
+ let mapChangeToEntity = (change: Change.t<Internal.entity>) =>
209
167
  switch change {
210
- | Set({entity}) =>
211
- inMemTable->updateIndices(~entity, ~entityIndices=updatedEntityRecord.entityIndices)
212
- | Delete({entityId}) =>
213
- inMemTable->deleteEntityFromIndices(
214
- ~entityId,
215
- ~entityIndices=updatedEntityRecord.entityIndices,
216
- )
168
+ | Set({entity}) => Some(entity)
169
+ | Delete(_) => None
217
170
  }
218
- inMemTable.table->setRow(change->Change.getEntityId, updatedEntityRecord)
219
- }
220
-
221
- let rowToEntity = row => row.latest
222
-
223
- let getRow = get
224
171
 
225
172
  /** It returns option<option<'entity>> where the first option means
226
173
  that the entity is not set to the in memory store,
227
174
  and the second option means that the entity doesn't esist/deleted.
228
175
  It's needed to prevent an additional round trips to the database for deleted entities. */
229
- let getUnsafe = (inMemTable: t<'entity>) =>
176
+ let getUnsafe = (inMemTable: t) =>
230
177
  (key: string) =>
231
- inMemTable.table.dict
178
+ inMemTable.latestEntityChangeById
232
179
  ->Dict.getUnsafe(key)
233
- ->rowToEntity
180
+ ->mapChangeToEntity
234
181
 
235
- let hasIndex = (inMemTable: t<'entity>, ~fieldName, ~operator: TableIndices.Operator.t) =>
182
+ let hasIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) =>
236
183
  fieldValueHash => {
237
- switch inMemTable.fieldNameIndices.dict->Utils.Dict.dangerouslyGetNonOption(fieldName) {
184
+ switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
238
185
  | None => false
239
186
  | Some(indicesSerializedToValue) => {
240
- // Should match TableIndices.toString logic
241
- let key = `${fieldName}:${(operator :> string)}:${fieldValueHash}`
242
- indicesSerializedToValue.dict->Utils.Dict.dangerouslyGetNonOption(key) !== None
187
+ let key = TableIndices.Index.toStringByParts(~fieldName, ~operator, ~fieldValueHash)
188
+ indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) !== None
243
189
  }
244
190
  }
245
191
  }
246
192
 
247
- let getUnsafeOnIndex = (
248
- inMemTable: t<'entity>,
249
- ~fieldName,
250
- ~operator: TableIndices.Operator.t,
251
- ) => {
193
+ let getUnsafeOnIndex = (inMemTable: t, ~fieldName, ~operator: TableIndices.Operator.t) => {
252
194
  let getEntity = inMemTable->getUnsafe
253
195
  fieldValueHash => {
254
- switch inMemTable.fieldNameIndices.dict->Utils.Dict.dangerouslyGetNonOption(fieldName) {
196
+ switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
255
197
  | None =>
256
198
  JsError.throwWithMessage(`Unexpected error. Must have an index on field ${fieldName}`)
257
199
  | Some(indicesSerializedToValue) => {
258
- // Should match TableIndices.toString logic
259
- let key = `${fieldName}:${(operator :> string)}:${fieldValueHash}`
260
- switch indicesSerializedToValue.dict->Utils.Dict.dangerouslyGetNonOption(key) {
200
+ let key = TableIndices.Index.toStringByParts(~fieldName, ~operator, ~fieldValueHash)
201
+ switch indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(key) {
261
202
  | None =>
262
203
  JsError.throwWithMessage(
263
204
  `Unexpected error. Must have an index for the value ${fieldValueHash} on field ${fieldName}`,
@@ -267,7 +208,7 @@ module Entity = {
267
208
  relatedEntityIds
268
209
  ->Utils.Set.toArray
269
210
  ->Array.filterMap(entityId => {
270
- switch hasByHash(inMemTable.table, entityId) {
211
+ switch inMemTable.latestEntityChangeById->Dict.has(entityId) {
271
212
  | true => getEntity(entityId)
272
213
  | false => None
273
214
  }
@@ -280,83 +221,42 @@ module Entity = {
280
221
  }
281
222
  }
282
223
 
283
- let addEmptyIndex = (inMemTable: t<'entity>, ~index) => {
224
+ let addEmptyIndex = (inMemTable: t, ~index) => {
284
225
  let fieldName = index->TableIndices.Index.getFieldName
285
226
  let relatedEntityIds = Utils.Set.make()
286
227
 
287
- inMemTable.table
288
- ->values
289
- ->Array.forEach(row => {
290
- switch row->rowToEntity {
228
+ inMemTable.latestEntityChangeById->Utils.Dict.forEach(change => {
229
+ switch change->mapChangeToEntity {
291
230
  | Some(entity) =>
292
231
  let fieldValue =
293
232
  entity
294
- ->(Utils.magic: 'entity => dict<TableIndices.FieldValue.t>)
233
+ ->(Utils.magic: Internal.entity => dict<TableIndices.FieldValue.t>)
295
234
  ->Dict.getUnsafe(fieldName)
296
235
  if index->TableIndices.Index.evaluate(~fieldName, ~fieldValue) {
297
- let _ = row.entityIndices->Utils.Set.add(index)
298
- let _ = relatedEntityIds->Utils.Set.add(entity->getEntityIdUnsafe)
236
+ let entityId = entity->getEntityIdUnsafe
237
+ let _ = inMemTable->getOrCreateEntityIndices(~entityId)->Utils.Set.add(index)
238
+ let _ = relatedEntityIds->Utils.Set.add(entityId)
299
239
  }
300
240
  | None => ()
301
241
  }
302
242
  })
303
- switch inMemTable.fieldNameIndices->getRow(index) {
243
+ switch inMemTable.fieldNameIndices->Utils.Dict.dangerouslyGetNonOption(fieldName) {
304
244
  | None =>
305
- inMemTable.fieldNameIndices->setRow(
306
- index,
245
+ inMemTable.fieldNameIndices->Dict.set(
246
+ fieldName,
307
247
  makeIndicesSerializedToValue(~index, ~relatedEntityIds),
308
248
  )
309
249
  | Some(indicesSerializedToValue) =>
310
- switch indicesSerializedToValue->getRow(index) {
311
- | None => indicesSerializedToValue->setRow(index, (index, relatedEntityIds))
312
- | Some(_) => () //Should not happen, this means the index already exists
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) {
250
+ switch indicesSerializedToValue->Utils.Dict.dangerouslyGetNonOption(
251
+ index->TableIndices.Index.toString,
252
+ ) {
329
253
  | None =>
330
- indicesSerializedToValue->setRow(index, (index, Utils.Set.make()->Utils.Set.add(entityId)))
331
- | Some((_index, relatedEntityIds)) => relatedEntityIds->Utils.Set.add(entityId)->ignore
254
+ indicesSerializedToValue->Dict.set(
255
+ index->TableIndices.Index.toString,
256
+ (index, relatedEntityIds),
257
+ )
258
+ | Some(_) => () //Should not happen, this means the index already exists
332
259
  }
333
260
  }
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
261
  }
362
262
  }