@eldoy/webdb 0.4.1 → 0.5.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.
- package/README.md +149 -167
- package/index.js +145 -221
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,279 +1,261 @@
|
|
|
1
1
|
# WebDB
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A small, fast document database API with a portable query language and native backend execution.
|
|
4
|
+
|
|
5
|
+
WebDB exposes a **single, well-defined interface** (`get` / `set`) designed to cover most web application use cases without sacrificing performance or backend features.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
4
10
|
|
|
5
|
-
### Installation
|
|
6
11
|
```sh
|
|
7
12
|
npm i @eldoy/webdb
|
|
8
13
|
```
|
|
9
14
|
|
|
10
|
-
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
11
18
|
|
|
12
19
|
```js
|
|
13
20
|
var webdb = require('@eldoy/webdb')
|
|
14
21
|
var db = webdb('http://admin:mysecretpassword@localhost:5984')
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
WebDB supports **named collections**:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
var users = db('user')
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Databases are created automatically on first use.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Insert
|
|
35
|
+
|
|
36
|
+
### Insert one document
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
var doc = await users.set({ name: 'Alice' })
|
|
40
|
+
console.log(doc.id)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The input object may be mutated to attach `id`.
|
|
44
|
+
|
|
45
|
+
---
|
|
15
46
|
|
|
16
|
-
|
|
17
|
-
var doc = await db('user').create({ name: 'Heimdal' })
|
|
47
|
+
### Bulk insert
|
|
18
48
|
|
|
19
|
-
|
|
20
|
-
var
|
|
49
|
+
```js
|
|
50
|
+
var docs = await users.set([
|
|
21
51
|
{ name: 'A' },
|
|
22
52
|
{ name: 'B' }
|
|
23
53
|
])
|
|
54
|
+
```
|
|
24
55
|
|
|
25
|
-
|
|
26
|
-
var n = await db('user').update(
|
|
27
|
-
{ name: 'A' },
|
|
28
|
-
{ active: true }
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
// Set (update exactly one)
|
|
32
|
-
var updated = await db('user').set(
|
|
33
|
-
{ name: 'A' },
|
|
34
|
-
{ name: 'B', active: true }
|
|
35
|
-
)
|
|
56
|
+
Returns the inserted documents (same object references).
|
|
36
57
|
|
|
37
|
-
|
|
38
|
-
var removed = await db('user').remove(
|
|
39
|
-
{ name: 'B' }
|
|
40
|
-
)
|
|
58
|
+
---
|
|
41
59
|
|
|
42
|
-
|
|
43
|
-
var doc = await db('user').put(
|
|
44
|
-
{ email: 'a@example.com' },
|
|
45
|
-
{ email: 'a@example.com', name: 'Alice' }
|
|
46
|
-
)
|
|
60
|
+
## Query (Read)
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
var doc = await db('user').get({ name: 'Heimdal' })
|
|
62
|
+
### Get first matching document
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
await
|
|
53
|
-
|
|
54
|
-
['name', 'email']
|
|
55
|
-
])
|
|
64
|
+
```js
|
|
65
|
+
var doc = await users.get({ name: 'Alice' })
|
|
66
|
+
```
|
|
56
67
|
|
|
57
|
-
|
|
58
|
-
var docs = await db('user').find({ name: 'Heimdal' })
|
|
68
|
+
Returns `null` if no match exists.
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
var docs = await db('user').find(
|
|
62
|
-
{ name: 'Heimdal' },
|
|
63
|
-
{ sort: [{ name: 'asc' }] }
|
|
64
|
-
)
|
|
70
|
+
---
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
var docs = await db('user').find(
|
|
68
|
-
{ name: 'Heimdal' },
|
|
69
|
-
{ limit: 1 }
|
|
70
|
-
)
|
|
72
|
+
### Count
|
|
71
73
|
|
|
72
|
-
|
|
73
|
-
var
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
)
|
|
74
|
+
```js
|
|
75
|
+
var r = await users.get({ active: true }, { count: true })
|
|
76
|
+
console.log(r.count)
|
|
77
|
+
```
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
var n = await db('user').delete({ active: false })
|
|
79
|
+
---
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
var n = await db('user').count({ name: 'Heimdal' })
|
|
81
|
+
### Streaming / batch reads
|
|
83
82
|
|
|
84
|
-
|
|
85
|
-
await
|
|
83
|
+
```js
|
|
84
|
+
await users.get(
|
|
86
85
|
{ active: true },
|
|
87
|
-
{
|
|
86
|
+
{ batch: 100, sort: { created: 1 } },
|
|
88
87
|
async function (docs) {
|
|
89
|
-
//
|
|
88
|
+
// process a batch
|
|
90
89
|
}
|
|
91
90
|
)
|
|
92
|
-
|
|
93
|
-
// Drop a specific database
|
|
94
|
-
await db('user').drop()
|
|
95
|
-
|
|
96
|
-
// Server-level compact
|
|
97
|
-
await db.compact('user')
|
|
98
|
-
|
|
99
|
-
// Drop all databases
|
|
100
|
-
await db.drop()
|
|
101
91
|
```
|
|
102
92
|
|
|
103
|
-
|
|
93
|
+
Streaming controls **delivery**, not execution.
|
|
94
|
+
Internal buffering is allowed.
|
|
95
|
+
|
|
96
|
+
---
|
|
104
97
|
|
|
105
|
-
|
|
98
|
+
## Query Operators
|
|
106
99
|
|
|
107
|
-
|
|
108
|
-
Mango supports standard comparison operators inside selectors:
|
|
100
|
+
Supported predicates:
|
|
109
101
|
|
|
110
102
|
```
|
|
111
|
-
$eq
|
|
112
|
-
$
|
|
113
|
-
$
|
|
114
|
-
$
|
|
115
|
-
$
|
|
116
|
-
$
|
|
117
|
-
$regex regular expression matching
|
|
103
|
+
$eq $ne
|
|
104
|
+
$gt $gte
|
|
105
|
+
$lt $lte
|
|
106
|
+
$in $nin
|
|
107
|
+
$regex
|
|
108
|
+
$exists
|
|
118
109
|
```
|
|
119
110
|
|
|
120
|
-
|
|
111
|
+
Examples:
|
|
121
112
|
|
|
122
113
|
```js
|
|
123
|
-
await
|
|
124
|
-
|
|
125
|
-
})
|
|
114
|
+
await users.get({ age: { $gte: 18 } })
|
|
115
|
+
await users.get({ email: { $regex: '@example.com$' } })
|
|
126
116
|
```
|
|
127
117
|
|
|
128
|
-
|
|
118
|
+
Logical operators:
|
|
129
119
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
```
|
|
133
|
-
$and
|
|
134
|
-
$or
|
|
135
|
-
$not
|
|
136
|
-
$nor
|
|
120
|
+
```js
|
|
121
|
+
$and $or $not
|
|
137
122
|
```
|
|
138
123
|
|
|
139
124
|
Example:
|
|
140
125
|
|
|
141
126
|
```js
|
|
142
|
-
await
|
|
127
|
+
await users.get({
|
|
143
128
|
$or: [{ role: 'admin' }, { active: true }]
|
|
144
129
|
})
|
|
145
130
|
```
|
|
146
131
|
|
|
147
|
-
|
|
132
|
+
---
|
|
148
133
|
|
|
149
|
-
Sorting
|
|
134
|
+
## Sorting, Limiting, Projection
|
|
150
135
|
|
|
151
|
-
|
|
152
|
-
await db('user').index([['created']])
|
|
136
|
+
### Sort
|
|
153
137
|
|
|
154
|
-
|
|
138
|
+
```js
|
|
139
|
+
await users.get(
|
|
155
140
|
{},
|
|
156
|
-
{ sort:
|
|
141
|
+
{ sort: { created: 1 } }
|
|
157
142
|
)
|
|
158
143
|
```
|
|
159
144
|
|
|
160
|
-
|
|
145
|
+
Sorting may require backend support or indexes.
|
|
146
|
+
If unsupported, the adapter may throw.
|
|
161
147
|
|
|
162
|
-
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
### Limit / skip
|
|
163
151
|
|
|
164
152
|
```js
|
|
165
|
-
await
|
|
153
|
+
await users.get({}, { skip: 10, limit: 5 })
|
|
166
154
|
```
|
|
167
155
|
|
|
168
|
-
|
|
156
|
+
---
|
|
169
157
|
|
|
170
|
-
|
|
158
|
+
### Projection (fields)
|
|
171
159
|
|
|
172
160
|
```js
|
|
173
|
-
await
|
|
161
|
+
await users.get(
|
|
174
162
|
{},
|
|
175
|
-
{ fields:
|
|
163
|
+
{ fields: { name: true, email: true } }
|
|
176
164
|
)
|
|
177
165
|
```
|
|
178
166
|
|
|
179
|
-
|
|
167
|
+
Notes:
|
|
180
168
|
|
|
181
|
-
|
|
182
|
-
|
|
169
|
+
* Projection is inclusive if any field is `true`
|
|
170
|
+
* `id` is included by default
|
|
171
|
+
* Excluding `id` is **best-effort**
|
|
172
|
+
* Adapters may still return `id`
|
|
183
173
|
|
|
184
|
-
|
|
185
|
-
* correct sorting
|
|
186
|
-
* correct `$gt` / `$lt` range queries
|
|
174
|
+
---
|
|
187
175
|
|
|
188
|
-
|
|
176
|
+
## Update
|
|
177
|
+
|
|
178
|
+
### Update matching documents
|
|
189
179
|
|
|
190
180
|
```js
|
|
191
|
-
await
|
|
192
|
-
|
|
193
|
-
}
|
|
181
|
+
var r = await users.set(
|
|
182
|
+
{ active: false },
|
|
183
|
+
{ active: true }
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
console.log(r.n)
|
|
194
187
|
```
|
|
195
188
|
|
|
196
|
-
|
|
189
|
+
Rules:
|
|
197
190
|
|
|
198
|
-
|
|
191
|
+
* Shallow merge
|
|
192
|
+
* `undefined` removes a field
|
|
193
|
+
* `null` sets field to `null`
|
|
199
194
|
|
|
200
|
-
|
|
195
|
+
---
|
|
201
196
|
|
|
202
|
-
|
|
203
|
-
* `limit`
|
|
204
|
-
* `sort`
|
|
205
|
-
* `fields`
|
|
206
|
-
* standard selectors
|
|
197
|
+
## Delete
|
|
207
198
|
|
|
208
|
-
|
|
199
|
+
### Delete matching documents
|
|
209
200
|
|
|
210
201
|
```js
|
|
211
|
-
await
|
|
212
|
-
|
|
213
|
-
{ size: 100, sort: [{ created: 'asc' }] },
|
|
214
|
-
async function (docs) {
|
|
215
|
-
// process chunk
|
|
216
|
-
}
|
|
217
|
-
)
|
|
202
|
+
var r = await users.set({ inactive: true }, null)
|
|
203
|
+
console.log(r.n)
|
|
218
204
|
```
|
|
219
205
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
### Notes on Indexing, Sorting, and Mango Queries
|
|
206
|
+
---
|
|
224
207
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
Mango performs best when the selector matches an existing index.
|
|
228
|
-
Any field used in `{ selector: … }` benefits from being part of an index, but it is not required unless sorting is used.
|
|
229
|
-
|
|
230
|
-
**2. Sorting requires indexing**
|
|
231
|
-
|
|
232
|
-
Mango enforces that **every field in the sort must be indexed**.
|
|
233
|
-
Example:
|
|
208
|
+
### Clear collection
|
|
234
209
|
|
|
235
210
|
```js
|
|
236
|
-
await
|
|
237
|
-
await db('user').find({}, { sort: [{ age: 'asc' }] }) // valid
|
|
211
|
+
await users.set({}, null)
|
|
238
212
|
```
|
|
239
213
|
|
|
240
|
-
|
|
214
|
+
Deletes all documents in the collection.
|
|
241
215
|
|
|
242
|
-
|
|
216
|
+
---
|
|
243
217
|
|
|
244
|
-
|
|
218
|
+
## Adapter Extensions (Optional)
|
|
219
|
+
|
|
220
|
+
Adapters may expose additional APIs outside dbspec:
|
|
245
221
|
|
|
246
222
|
```js
|
|
247
|
-
await
|
|
223
|
+
await users.drop() // drop a collection
|
|
224
|
+
await db.drop() // drop all collections
|
|
225
|
+
await db.compact('user')
|
|
226
|
+
await db.info()
|
|
248
227
|
```
|
|
249
228
|
|
|
250
|
-
|
|
229
|
+
These are **adapter-specific** and non-portable.
|
|
251
230
|
|
|
252
|
-
|
|
231
|
+
---
|
|
253
232
|
|
|
254
|
-
|
|
255
|
-
Unindexed projection works fine; indexing does not affect `fields`.
|
|
233
|
+
## Escape Hatch (`data`)
|
|
256
234
|
|
|
257
|
-
|
|
235
|
+
Native backend access is available via:
|
|
258
236
|
|
|
259
|
-
|
|
260
|
-
|
|
237
|
+
```js
|
|
238
|
+
users.data
|
|
239
|
+
```
|
|
261
240
|
|
|
262
|
-
|
|
241
|
+
This exposes the underlying client directly.
|
|
263
242
|
|
|
264
|
-
|
|
243
|
+
Use of `data` is **explicitly non-portable** and bypasses dbspec guarantees.
|
|
265
244
|
|
|
266
|
-
|
|
245
|
+
---
|
|
267
246
|
|
|
268
|
-
|
|
247
|
+
## Design Notes
|
|
269
248
|
|
|
270
|
-
|
|
249
|
+
* The reference implementation prioritizes **native execution**
|
|
250
|
+
* No client-side emulation of query semantics
|
|
251
|
+
* Performance scales with the backend
|
|
252
|
+
* Most web apps do not require the escape hatch
|
|
253
|
+
* When switching adapters, application query logic remains stable
|
|
271
254
|
|
|
272
|
-
|
|
273
|
-
Use `doc.id` after `create()`, and `_id` for all queries and stored documents.
|
|
255
|
+
---
|
|
274
256
|
|
|
275
|
-
|
|
257
|
+
## License
|
|
276
258
|
|
|
277
|
-
|
|
259
|
+
ISC
|
|
278
260
|
|
|
279
|
-
|
|
261
|
+
Created by [Vidar Eldøy](https://eldoy.com)
|
package/index.js
CHANGED
|
@@ -3,14 +3,10 @@ var nano = require('nano')
|
|
|
3
3
|
module.exports = function (url) {
|
|
4
4
|
var server = nano(url)
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
return
|
|
6
|
+
function db(name) {
|
|
7
|
+
return collection(name, server)
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
-
//
|
|
11
|
-
// SERVER-LEVEL OPS (drop all, compact specific)
|
|
12
|
-
//
|
|
13
|
-
|
|
14
10
|
db.drop = async function () {
|
|
15
11
|
var list = await server.db.list()
|
|
16
12
|
for (var i = 0; i < list.length; i++) {
|
|
@@ -23,269 +19,197 @@ module.exports = function (url) {
|
|
|
23
19
|
}
|
|
24
20
|
|
|
25
21
|
db.info = async function () {
|
|
26
|
-
return
|
|
22
|
+
return server.info()
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
return db
|
|
30
26
|
}
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
function collection(name, server) {
|
|
29
|
+
var handle = null
|
|
30
|
+
var initPromise = null
|
|
35
31
|
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
//
|
|
39
|
-
// CRUD
|
|
40
|
-
//
|
|
32
|
+
function init() {
|
|
33
|
+
if (initPromise) return initPromise
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
initPromise = (async function () {
|
|
36
|
+
try {
|
|
37
|
+
await server.db.get(name)
|
|
38
|
+
} catch (e) {
|
|
39
|
+
try {
|
|
40
|
+
await server.db.create(name)
|
|
41
|
+
} catch (e2) {}
|
|
42
|
+
}
|
|
43
|
+
handle = server.db.use(name)
|
|
44
|
+
return handle
|
|
45
|
+
})()
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await db.bulk({ docs: docs })
|
|
50
|
-
return docs.length
|
|
51
|
-
},
|
|
47
|
+
return initPromise
|
|
48
|
+
}
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// create if not found
|
|
63
|
-
if (!r.docs.length) {
|
|
64
|
-
var created = await dbi.insert(update)
|
|
65
|
-
return {
|
|
66
|
-
_id: created.id,
|
|
67
|
-
_rev: created.rev,
|
|
68
|
-
...update
|
|
50
|
+
var data = new Proxy(
|
|
51
|
+
{},
|
|
52
|
+
{
|
|
53
|
+
get: function (_, prop) {
|
|
54
|
+
return async function () {
|
|
55
|
+
var db = await init()
|
|
56
|
+
return db[prop].apply(db, arguments)
|
|
69
57
|
}
|
|
70
58
|
}
|
|
59
|
+
}
|
|
60
|
+
)
|
|
71
61
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
var next = {}
|
|
75
|
-
|
|
76
|
-
for (var k in cur) next[k] = cur[k]
|
|
77
|
-
for (var k in update) next[k] = update[k]
|
|
78
|
-
|
|
79
|
-
var res = await dbi.insert(next)
|
|
80
|
-
|
|
81
|
-
next._id = res.id
|
|
82
|
-
next._rev = res.rev
|
|
83
|
-
return next
|
|
84
|
-
},
|
|
62
|
+
return {
|
|
63
|
+
data: data,
|
|
85
64
|
|
|
86
|
-
|
|
87
|
-
var db = await
|
|
88
|
-
|
|
89
|
-
if (!r.docs.length) return 0
|
|
65
|
+
get: async function (query, opts, onBatch) {
|
|
66
|
+
var db = await init()
|
|
67
|
+
opts = opts || {}
|
|
90
68
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
var next = {}
|
|
95
|
-
for (var k in cur) next[k] = cur[k]
|
|
96
|
-
for (var k in update) next[k] = update[k]
|
|
97
|
-
out.push(next)
|
|
69
|
+
if (opts.count) {
|
|
70
|
+
var r = await db.find({ selector: query })
|
|
71
|
+
return { count: r.docs.length }
|
|
98
72
|
}
|
|
99
73
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
74
|
+
if (onBatch) {
|
|
75
|
+
var batch = opts.batch || 100
|
|
76
|
+
var remaining = opts.limit
|
|
77
|
+
var bookmark = null
|
|
103
78
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
set: async function (query, update) {
|
|
111
|
-
var dbi = await ensure(name, server)
|
|
112
|
-
|
|
113
|
-
// find one
|
|
114
|
-
var r = await dbi.find({
|
|
115
|
-
selector: query,
|
|
116
|
-
limit: 1
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
if (!r.docs.length) return null
|
|
79
|
+
for (;;) {
|
|
80
|
+
var limit = batch
|
|
81
|
+
if (remaining !== undefined && remaining < limit) {
|
|
82
|
+
limit = remaining
|
|
83
|
+
}
|
|
120
84
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
// merge current doc + update
|
|
125
|
-
for (var k in cur) next[k] = cur[k]
|
|
126
|
-
for (var k in update) next[k] = update[k]
|
|
127
|
-
|
|
128
|
-
// write updated doc
|
|
129
|
-
var res = await dbi.insert(next)
|
|
130
|
-
|
|
131
|
-
// return updated document shape
|
|
132
|
-
next._id = res.id
|
|
133
|
-
next._rev = res.rev
|
|
134
|
-
return next
|
|
135
|
-
},
|
|
85
|
+
var q = { selector: query, limit: limit }
|
|
86
|
+
if (opts.sort) q.sort = normalizeSort(opts.sort)
|
|
136
87
|
|
|
137
|
-
|
|
138
|
-
|
|
88
|
+
var mf = opts.fields && normalizeFields(opts.fields)
|
|
89
|
+
if (mf) q.fields = mf
|
|
90
|
+
if (bookmark) q.bookmark = bookmark
|
|
139
91
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
selector: query,
|
|
143
|
-
limit: 1
|
|
144
|
-
})
|
|
92
|
+
var r = await db.find(q)
|
|
93
|
+
if (!r.docs.length) break
|
|
145
94
|
|
|
146
|
-
|
|
95
|
+
normalizeDocs(r.docs)
|
|
96
|
+
await onBatch(r.docs)
|
|
147
97
|
|
|
148
|
-
|
|
98
|
+
if (remaining !== undefined) {
|
|
99
|
+
remaining -= r.docs.length
|
|
100
|
+
if (remaining <= 0) break
|
|
101
|
+
}
|
|
149
102
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
_deleted: true
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
_id: res.id,
|
|
159
|
-
_rev: res.rev
|
|
103
|
+
if (!r.bookmark || r.docs.length < limit) break
|
|
104
|
+
bookmark = r.bookmark
|
|
105
|
+
}
|
|
106
|
+
return
|
|
160
107
|
}
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
delete: async function (query) {
|
|
164
|
-
var dbi = await ensure(name, server)
|
|
165
108
|
|
|
166
|
-
var
|
|
167
|
-
if (
|
|
109
|
+
var q = { selector: query }
|
|
110
|
+
if (opts.sort) q.sort = normalizeSort(opts.sort)
|
|
111
|
+
if (opts.limit !== undefined) q.limit = opts.limit
|
|
112
|
+
if (opts.skip) q.skip = opts.skip
|
|
168
113
|
|
|
169
|
-
var
|
|
170
|
-
|
|
171
|
-
var d = r.docs[i]
|
|
172
|
-
out.push({
|
|
173
|
-
_id: d._id,
|
|
174
|
-
_rev: d._rev,
|
|
175
|
-
_deleted: true
|
|
176
|
-
})
|
|
177
|
-
}
|
|
114
|
+
var mf = opts.fields && normalizeFields(opts.fields)
|
|
115
|
+
if (mf) q.fields = mf
|
|
178
116
|
|
|
179
|
-
await
|
|
180
|
-
|
|
117
|
+
var r = await db.find(q)
|
|
118
|
+
normalizeDocs(r.docs)
|
|
119
|
+
return r.docs[0] || null
|
|
181
120
|
},
|
|
182
121
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
//
|
|
122
|
+
set: async function (arg, values) {
|
|
123
|
+
var db = await init()
|
|
186
124
|
|
|
187
|
-
|
|
188
|
-
|
|
125
|
+
if (Array.isArray(arg)) {
|
|
126
|
+
var out = []
|
|
127
|
+
for (var i = 0; i < arg.length; i++) {
|
|
128
|
+
var d = arg[i]
|
|
129
|
+
var r = await db.insert(d)
|
|
130
|
+
d.id = r.id
|
|
131
|
+
out.push(d)
|
|
132
|
+
}
|
|
133
|
+
return out
|
|
134
|
+
}
|
|
189
135
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
if (opts.fields) q.fields = opts.fields
|
|
136
|
+
if (values === undefined) {
|
|
137
|
+
var r = await db.insert(arg)
|
|
138
|
+
arg.id = r.id
|
|
139
|
+
return arg
|
|
195
140
|
}
|
|
196
141
|
|
|
197
|
-
var r = await
|
|
198
|
-
|
|
199
|
-
|
|
142
|
+
var r = await db.find({ selector: arg })
|
|
143
|
+
if (!r.docs.length) return { n: 0 }
|
|
144
|
+
|
|
145
|
+
if (values === null) {
|
|
146
|
+
var del = []
|
|
147
|
+
for (var i = 0; i < r.docs.length; i++) {
|
|
148
|
+
del.push({
|
|
149
|
+
_id: r.docs[i]._id,
|
|
150
|
+
_rev: r.docs[i]._rev,
|
|
151
|
+
_deleted: true
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
await db.bulk({ docs: del })
|
|
155
|
+
return { n: del.length }
|
|
156
|
+
}
|
|
200
157
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
158
|
+
var upd = []
|
|
159
|
+
for (var i = 0; i < r.docs.length; i++) {
|
|
160
|
+
var cur = r.docs[i]
|
|
161
|
+
var next = {}
|
|
162
|
+
for (var k in cur) next[k] = cur[k]
|
|
204
163
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
164
|
+
for (var k in values) {
|
|
165
|
+
if (values[k] === undefined) delete next[k]
|
|
166
|
+
else next[k] = values[k]
|
|
167
|
+
}
|
|
168
|
+
upd.push(next)
|
|
209
169
|
}
|
|
210
|
-
},
|
|
211
170
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
//
|
|
215
|
-
|
|
216
|
-
count: async function (query) {
|
|
217
|
-
var dbi = await ensure(name, server)
|
|
218
|
-
var r = await dbi.find({ selector: query })
|
|
219
|
-
return r.docs.length
|
|
171
|
+
await db.bulk({ docs: upd })
|
|
172
|
+
return { n: upd.length }
|
|
220
173
|
},
|
|
221
174
|
|
|
222
|
-
//
|
|
223
|
-
// DROP (collection-level)
|
|
224
|
-
//
|
|
225
|
-
|
|
226
175
|
drop: async function () {
|
|
227
176
|
try {
|
|
228
177
|
await server.db.destroy(name)
|
|
229
178
|
} catch (e) {}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
batch: async function (query, opt, fn) {
|
|
237
|
-
var dbi = await ensure(name, server)
|
|
238
|
-
|
|
239
|
-
var size = opt && opt.size ? opt.size : 100
|
|
240
|
-
var limit = opt && opt.limit
|
|
241
|
-
var sort = opt && opt.sort
|
|
242
|
-
var fields = opt && opt.fields
|
|
243
|
-
|
|
244
|
-
var bookmark = null
|
|
245
|
-
var remaining = limit
|
|
246
|
-
|
|
247
|
-
for (;;) {
|
|
248
|
-
var effectiveSize = size
|
|
249
|
-
if (remaining && remaining < effectiveSize) {
|
|
250
|
-
effectiveSize = remaining
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
var q = {
|
|
254
|
-
selector: query,
|
|
255
|
-
limit: effectiveSize
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
if (sort) q.sort = sort
|
|
259
|
-
if (fields) q.fields = fields
|
|
260
|
-
if (bookmark) q.bookmark = bookmark
|
|
261
|
-
|
|
262
|
-
var r = await dbi.find(q)
|
|
263
|
-
var docs = r.docs
|
|
264
|
-
if (!docs.length) break
|
|
265
|
-
|
|
266
|
-
await fn(docs)
|
|
179
|
+
handle = null
|
|
180
|
+
initPromise = null
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
267
184
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
185
|
+
function normalizeSort(sort) {
|
|
186
|
+
var out = []
|
|
187
|
+
for (var k in sort) {
|
|
188
|
+
if (sort[k] === 1) out.push({ [k]: 'asc' })
|
|
189
|
+
else if (sort[k] === -1) out.push({ [k]: 'desc' })
|
|
190
|
+
}
|
|
191
|
+
return out
|
|
192
|
+
}
|
|
272
193
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
194
|
+
function normalizeFields(fields) {
|
|
195
|
+
var include = []
|
|
196
|
+
for (var k in fields) {
|
|
197
|
+
if (fields[k]) {
|
|
198
|
+
include.push(k === 'id' ? '_id' : k)
|
|
276
199
|
}
|
|
277
200
|
}
|
|
201
|
+
if (include.length) {
|
|
202
|
+
if (fields.id !== false && include.indexOf('_id') === -1)
|
|
203
|
+
include.push('_id')
|
|
204
|
+
return include
|
|
205
|
+
}
|
|
206
|
+
return null
|
|
278
207
|
}
|
|
279
208
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
try {
|
|
286
|
-
await server.db.get(name)
|
|
287
|
-
} catch (e) {
|
|
288
|
-
await server.db.create(name)
|
|
209
|
+
function normalizeDocs(docs) {
|
|
210
|
+
for (var i = 0; i < docs.length; i++) {
|
|
211
|
+
docs[i].id = docs[i]._id
|
|
212
|
+
delete docs[i]._id
|
|
213
|
+
delete docs[i]._rev
|
|
289
214
|
}
|
|
290
|
-
return server.db.use(name)
|
|
291
215
|
}
|