@live-change/db 0.9.61 → 0.9.63

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.
@@ -4,21 +4,47 @@ class ChangeStream {
4
4
  onChange() {
5
5
  throw new Error("abstract method - not implemented")
6
6
  }
7
- to(output) {
8
- return this.onChange((obj, oldObj, id, timestamp) => output.change(obj, oldObj, id, timestamp))
7
+ async rangeGet(range) {
8
+ throw new Error("abstract method - not implemented")
9
+ }
10
+ range(range) {
11
+ throw new Error("abstract method - not implemented")
12
+ }
13
+ async objectGet(id) {
14
+ throw new Error("abstract method - not implemented")
15
+ }
16
+ object(id) {
17
+ throw new Error("abstract method - not implemented")
9
18
  }
19
+ async count(range = {}) {
20
+ throw new Error("abstract method - not implemented")
21
+ }
22
+ async to(output) {
23
+ return this.onChange(async (obj, oldObj, id, timestamp) => {
24
+ if(obj || oldObj) await output.change(obj, oldObj, id, timestamp)
25
+ })
26
+ await this.observerPromise
27
+ }
10
28
  filter(func) {
11
29
  const pipe = new ChangeStreamPipe()
12
- const observerPromise = this.onChange((obj, oldObj, id, timestamp) =>
13
- pipe.change(obj && func(obj) ? obj : null, oldObj && func(oldObj) ? oldObj : null, id, timestamp))
30
+ const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
31
+ const newObj = obj && await func(obj)
32
+ const newOldObj = oldObj && await func(oldObj)
33
+ if(!(newObj || newOldObj)) return
34
+ pipe.change(newObj, newOldObj, id, timestamp)
35
+ })
14
36
  pipe.master = this
15
37
  pipe.observerPromise = observerPromise
16
38
  return pipe
17
39
  }
18
40
  map(func) {
19
41
  const pipe = new ChangeStreamPipe()
20
- const observerPromise = this.onChange((obj, oldObj, id, timestamp) =>
21
- pipe.change(obj && func(obj), oldObj && func(oldObj), id, timestamp))
42
+ const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
43
+ const newObj = obj && await func(obj)
44
+ const newOldObj = oldObj && await func(oldObj)
45
+ if(!(newObj || newOldObj)) return
46
+ pipe.change(newObj, newOldObj, id, timestamp)
47
+ })
22
48
  pipe.master = this
23
49
  pipe.observerPromise = observerPromise
24
50
  return pipe
@@ -42,6 +68,74 @@ class ChangeStream {
42
68
  pipe.observerPromise = observerPromise
43
69
  return pipe
44
70
  }
71
+ async readInBuckets(bucketCallback, bucketSize = 128) {
72
+ let position = ''
73
+ let readed = 0
74
+ do {
75
+ const bucket = await this.rangeGet({ gt: position, limit: bucketSize })
76
+ readed = bucket.length
77
+ if(!bucket.length) break
78
+ position = bucket[bucket.length - 1].id
79
+ await bucketCallback(bucket)
80
+ } while(readed === bucketSize)
81
+ }
82
+ cross(other, selfToRange, otherToRange, bucketSize = 128) {
83
+ const pipe = new ChangeStreamPipe()
84
+ const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
85
+ const otherRange = await selfToRange(obj || oldObj)
86
+ if(!otherRange) return // ignore
87
+ if(typeof otherRange === 'string') { // single id
88
+ const otherObj = await other.objectGet(otherRange)
89
+ await pipe.change([obj, otherObj], [oldObj, otherObj], [id, otherRange], timestamp)
90
+ return
91
+ }
92
+ await other.range(otherRange).readInBuckets(async bucket => {
93
+ for(const otherObj of bucket) {
94
+ const otherId = otherObj.id
95
+ await pipe.change([obj, otherObj], [oldObj, otherObj], [id, otherId], timestamp)
96
+ }
97
+ }, bucketSize)
98
+ })
99
+ const otherObserverPromise = other.onChange(async (otherObj, oldOtherObj, id, timestamp) => {
100
+ const selfRange = await otherToRange(otherObj || oldOtherObj)
101
+ if(!selfRange) return // ignore
102
+ const otherId = id
103
+ if(typeof selfRange === 'string') { // single id
104
+ const obj = await this.objectGet(selfRange)
105
+ await pipe.change([obj, otherObj], [obj, oldOtherObj], [selfRange, otherId], timestamp)
106
+ return
107
+ }
108
+ await this.range(selfRange).readInBuckets(async bucket => {
109
+ for(const obj of bucket) {
110
+ const id = obj.id
111
+ await pipe.change([obj, otherObj], [obj, oldOtherObj], [id, otherId], timestamp)
112
+ }
113
+ }, bucketSize)
114
+ })
115
+ pipe.master = this
116
+ pipe.observerPromise = Promise.all([observerPromise, otherObserverPromise])
117
+ return pipe
118
+ }
119
+ groupExisting(objectToRange) {
120
+ const pipe = new ChangeStreamPipe()
121
+ const observerPromise = this.onChange(async (obj, oldObj, id, timestamp) => {
122
+ const existingObj = obj || oldObj
123
+ let range = await objectToRange(existingObj)
124
+ if(!range) return
125
+ if(typeof range === 'string') {
126
+ range = { gte: range, lte: range + '\xFF\xFF\xFF\xFF' }
127
+ }
128
+ const count = await this.count(range)
129
+ if(count) {
130
+ await pipe.change(existingObj, null, id, timestamp)
131
+ } else {
132
+ await pipe.change(null, existingObj, id, timestamp)
133
+ }
134
+ })
135
+ pipe.master = this
136
+ pipe.observerPromise = observerPromise
137
+ return pipe
138
+ }
45
139
  }
46
140
 
47
141
  class ChangeStreamPipe extends ChangeStream {
package/lib/Index.js CHANGED
@@ -6,6 +6,7 @@ import queryGet from './queryGet.js'
6
6
  import profileLog from './profileLog.js'
7
7
  import nextTick from 'next-tick'
8
8
  import { ChangeStream } from './ChangeStream.js'
9
+ import { rangeIntersection } from './utils.js'
9
10
 
10
11
 
11
12
  import Debug from 'debug'
@@ -37,6 +38,21 @@ class ObjectReader extends ChangeStream {
37
38
  async get() {
38
39
  return await (await this.tableReader.table).objectGet(this.id)
39
40
  }
41
+ async rangeGet(range) {
42
+ return await (await this.tableReader.table).rangeGet(rangeIntersection(unitRange(this.id), range))
43
+ }
44
+ range(range) {
45
+ return new RangeReader(this.tableReader, unitRange(this.id))
46
+ }
47
+ object(id) {
48
+ return new ObjectReader(this.tableReader, id)
49
+ }
50
+ async objectGet(id) {
51
+ return await (await this.tableReader.table).objectGet(id)
52
+ }
53
+ async count(range = {}) {
54
+ return await (await this.tableReader.table).countGet(rangeIntersection(unitRange(this.id), range))
55
+ }
40
56
  dispose() {}
41
57
  }
42
58
 
@@ -63,9 +79,24 @@ class RangeReader extends ChangeStream {
63
79
  async change(obj, oldObj, id, timestamp) {
64
80
  for(const callback of this.callbacks) await callback(obj, oldObj, id, timestamp)
65
81
  }
82
+ async rangeGet(range) {
83
+ return await (await this.tableReader.table).rangeGet(rangeIntersection(this.range, range))
84
+ }
85
+ range(range) {
86
+ return new RangeReader(this.tableReader, rangeIntersection(this.range, range))
87
+ }
66
88
  async get() {
67
89
  return await (await this.tableReader.table).rangeGet(this.range)
68
90
  }
91
+ async objectGet(id) {
92
+ return await (await this.tableReader.table).objectGet(id)
93
+ }
94
+ object(id) {
95
+ return new ObjectReader(this.tableReader, id)
96
+ }
97
+ async count(range = {}) {
98
+ return await (await this.tableReader.table).countGet(rangeIntersection(this.range, range))
99
+ }
69
100
  }
70
101
 
71
102
  class TableReader extends ChangeStream {
@@ -138,6 +169,9 @@ class TableReader extends ChangeStream {
138
169
  for(const callback of this.callbacks) await callback(obj, oldObj, id, timestamp)
139
170
  if(profileOp) await profileLog.end(profileOp)
140
171
  }
172
+ async rangeGet(range) {
173
+ return await this.table.rangeGet(range)
174
+ }
141
175
  range(range) {
142
176
  const key = JSON.stringify(range)
143
177
  let reader = this.rangeReaders.get(key)
@@ -149,6 +183,12 @@ class TableReader extends ChangeStream {
149
183
  return reader
150
184
  return new RangeReader(this, range)
151
185
  }
186
+ async objectGet(id) {
187
+ return await (await this.table).objectGet(id)
188
+ }
189
+ async count(range = {}) {
190
+ return await (await this.table).count(rangeIntersection(this.range, range))
191
+ }
152
192
  object(id) {
153
193
  let reader = this.objectReaders.get(id)
154
194
  if(!reader) {
package/lib/queryGet.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { TableWriter, LogWriter } from './queryUpdate.js'
2
2
  import { ChangeStream } from './ChangeStream.js'
3
-
3
+ import { unitRange, rangeIntersection } from './utils.js'
4
4
  const maxGetLimit = 256
5
5
 
6
6
  class ObjectReader extends ChangeStream {
@@ -22,6 +22,15 @@ class ObjectReader extends ChangeStream {
22
22
  async get() {
23
23
  return await (await this.#table).objectGet(this.#id)
24
24
  }
25
+ async rangeGet(range) {
26
+ return await (await this.#table).rangeGet(rangeIntersection(unitRange(this.#id), range))
27
+ }
28
+ range(range) {
29
+ return new RangeReader(this.#table, rangeIntersection(unitRange(this.#id), range))
30
+ }
31
+ async count(range = {}) {
32
+ return await this.#table.countGet(rangeIntersection(unitRange(this.#id), range))
33
+ }
25
34
  }
26
35
 
27
36
  class RangeReader extends ChangeStream {
@@ -45,6 +54,22 @@ class RangeReader extends ChangeStream {
45
54
  async get() {
46
55
  return await (await this.#table).rangeGet(this.#range)
47
56
  }
57
+
58
+ async rangeGet(range) {
59
+ return await (await this.#table).rangeGet(rangeIntersection(this.#range, range))
60
+ }
61
+ range(range) {
62
+ return new RangeReader(this.#table, rangeIntersection(this.#range, range), this.#time)
63
+ }
64
+ object(id) {
65
+ return new ObjectReader(this.#table, id, this.#time)
66
+ }
67
+ async objectGet(id) {
68
+ return await (await this.#table).objectGet(id)
69
+ }
70
+ async count(range = {}) {
71
+ return await this.#table.countGet(rangeIntersection(this.#range, range))
72
+ }
48
73
  }
49
74
 
50
75
  class TableReader extends ChangeStream {
@@ -73,20 +98,20 @@ class TableReader extends ChangeStream {
73
98
  return results
74
99
  }
75
100
  unobserve(obs) {}
101
+ async rangeGet(range) {
102
+ return new RangeReader(this.#table, range, this.#time)
103
+ }
76
104
  range(range) {
77
105
  return new RangeReader(this.#table, range, this.#time)
78
106
  }
79
107
  object(id) {
80
108
  return new ObjectReader(this.#table, id, this.#time)
81
109
  }
82
- objectGet(id) {
83
- return this.#table.objectGet(id)
84
- }
85
- async get(range = {}) {
86
- return (await this.#table).rangeGet(range)
110
+ async objectGet(id) {
111
+ return await (await this.#table).objectGet(id)
87
112
  }
88
113
  async count(range = {}) {
89
- return (await this.#table).countGet(range)
114
+ return await (await this.#table).countGet(range)
90
115
  }
91
116
  }
92
117
 
@@ -31,7 +31,7 @@ class ObjectObserver {
31
31
  }
32
32
  readPromise() {
33
33
  return this.#valuePromise
34
- }
34
+ }
35
35
  }
36
36
 
37
37
  class RangeObserver {
@@ -119,11 +119,12 @@ class Reader extends ChangeStream {
119
119
  class ObjectReader extends Reader {
120
120
  #table = null
121
121
  #id = null
122
-
123
- constructor(queryReader, table, id) {
122
+ #tableReader = null
123
+ constructor(queryReader, table, id, tableReader) {
124
124
  super(queryReader)
125
125
  this.#table = table
126
126
  this.#id = id
127
+ this.#tableReader = tableReader
127
128
  }
128
129
  observableFactory() {
129
130
  return this.#table.objectObservable(this.#id)
@@ -132,19 +133,32 @@ class ObjectReader extends Reader {
132
133
  return this.startObserver((r) => new ObjectObserver(r, cb))
133
134
  }
134
135
  get() {
135
- //console.log("OBJ GET", this.#table.name, this.#id)
136
136
  return this.#table.objectGet(this.#id)
137
137
  }
138
+ async rangeGet(range) {
139
+ return this.#tableReader.rangeGet(rangeIntersection(unitRange(this.#id), range))
140
+ }
141
+ async objectGet(id) {
142
+ return this.#table.objectGet(id)
143
+ }
144
+ object(id) {
145
+ return this.#tableReader.object(id)
146
+ }
147
+ async count(range = {}) {
148
+ return await this.#table.count(rangeIntersection(unitRange(this.#id), range))
149
+ }
138
150
  }
139
151
 
140
152
  class RangeReader extends Reader {
141
153
  #table = null
142
154
  #range = null
155
+ #tableReader = null
143
156
 
144
- constructor(queryReader, table, range) {
157
+ constructor(queryReader, table, range, tableReader) {
145
158
  super(queryReader)
146
159
  this.#table = table
147
160
  this.#range = range
161
+ this.#tableReader = tableReader
148
162
  }
149
163
  async observableFactory() {
150
164
  return await (await this.#table).rangeObservable(this.#range)
@@ -155,6 +169,18 @@ class RangeReader extends Reader {
155
169
  async get() {
156
170
  return await (await this.#table).rangeGet(this.#range)
157
171
  }
172
+ async rangeGet(range) {
173
+ return await this.#tableReader.rangeGet(rangeIntersection(this.#range, range))
174
+ }
175
+ async objectGet(id) {
176
+ return await this.#table.objectGet(id)
177
+ }
178
+ object(id) {
179
+ return this.#tableReader.object(id)
180
+ }
181
+ async count(range = {}) {
182
+ return await this.#table.count(rangeIntersection(this.#range, range))
183
+ }
158
184
  }
159
185
 
160
186
  class TableReader extends Reader {
@@ -172,16 +198,19 @@ class TableReader extends Reader {
172
198
  onChange(cb) {
173
199
  return this.startObserver((r) => new RangeObserver(r, cb))
174
200
  }
201
+ async rangeGet(range) {
202
+ return await this.#table.rangeGet(range)
203
+ }
175
204
  range(range) {
176
205
  return this._queryReader.getExistingReaderOrCreate(this.#prefix+':'+JSON.stringify(range),
177
- () => new RangeReader(this._queryReader, this.#table, range))
206
+ () => new RangeReader(this._queryReader, this.#table, range, this))
178
207
  }
179
208
  object(id) {
180
209
  return this._queryReader.getExistingReaderOrCreate(this.#prefix+'#'+id,
181
- () => new ObjectReader(this._queryReader, this.#table, id))
210
+ () => new ObjectReader(this._queryReader, this.#table, id, this))
182
211
  }
183
- objectGet(id) {
184
- return this.#table.objectGet(id)
212
+ async objectGet(id) {
213
+ return await this.#table.objectGet(id)
185
214
  }
186
215
  async get(range = {}) {
187
216
  return await (await this.#table).rangeGet(range)
package/lib/utils.js ADDED
@@ -0,0 +1,41 @@
1
+ export function min(a, b) {
2
+ if(a === undefined) return b
3
+ if(b === undefined) return a
4
+ return a < b ? a : b
5
+ }
6
+
7
+ export function max(a, b) {
8
+ if(a === undefined) return b
9
+ if(b === undefined) return a
10
+ return a > b ? a : b
11
+ }
12
+
13
+ export function rangeIntersection(range1, range2) {
14
+ return {
15
+ reverse: range2.reverse,
16
+ gt: max(range1.gt, range2.gt),
17
+ lt: min(range1.lt, range2.lt),
18
+ gte: max(range1.gte, range2.gte),
19
+ lte: min(range1.lte, range2.lte),
20
+ limit: range2.limit
21
+ }
22
+ }
23
+
24
+ export function rangeUnion(range1, range2) {
25
+ return {
26
+ reverse: range1.reverse,
27
+ gt: min(range1.gt, range2.gt),
28
+ lt: max(range1.lt, range2.lt),
29
+ gte: min(range1.gte, range2.gte),
30
+ lte: max(range1.lte, range2.lte),
31
+ limit: range1.limit + range2.limit
32
+ }
33
+ }
34
+
35
+ export function unitRange(id) {
36
+ return {
37
+ gte: id,
38
+ lt: id,
39
+ limit: 1
40
+ }
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-change/db",
3
- "version": "0.9.61",
3
+ "version": "0.9.63",
4
4
  "description": "Database with observable data for live queries",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -22,8 +22,8 @@
22
22
  "type": "module",
23
23
  "homepage": "https://github.com/live-change/live-change-stack",
24
24
  "devDependencies": {
25
- "@live-change/db-store-level": "^0.9.61",
26
- "@live-change/db-store-lmdb": "^0.9.61",
25
+ "@live-change/db-store-level": "^0.9.63",
26
+ "@live-change/db-store-lmdb": "^0.9.63",
27
27
  "minimist": ">=1.2.3",
28
28
  "next-tick": "^1.1.0",
29
29
  "rimraf": "^5.0.5",
@@ -32,9 +32,9 @@
32
32
  "websocket-extensions": ">=0.1.4"
33
33
  },
34
34
  "dependencies": {
35
- "@live-change/dao": "^0.9.61",
35
+ "@live-change/dao": "^0.9.63",
36
36
  "get-random-values": "^1.2.2",
37
37
  "node-interval-tree": "^1.3.3"
38
38
  },
39
- "gitHead": "f750f29f912b9a76b5ffa2bdf5c46954cc7e7a67"
39
+ "gitHead": "89b9647aeaff9fc66add1f07d225fe4f44d91a39"
40
40
  }