@eldoy/webdb 0.1.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/LICENSE +7 -0
- package/README.md +142 -0
- package/index.js +205 -0
- package/package.json +37 -0
- package/spec/tests/spekk.js +7 -0
- package/spec/tests/webdb.test.js +242 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vidar Eldøy
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# WebDB
|
|
2
|
+
|
|
3
|
+
Document database API backed by CouchDB, exposed through a Mongo-style client.
|
|
4
|
+
|
|
5
|
+
### Installation
|
|
6
|
+
```sh
|
|
7
|
+
npm i @eldoy/webdb
|
|
8
|
+
````
|
|
9
|
+
|
|
10
|
+
### Usage
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
var webdb = require('@eldoy/webdb')
|
|
14
|
+
var db = webdb('http://admin:mysecretpassword@localhost:5984')
|
|
15
|
+
|
|
16
|
+
// Create one
|
|
17
|
+
var doc = await db('user').create({ name: 'Heimdal' })
|
|
18
|
+
|
|
19
|
+
// Bulk insert
|
|
20
|
+
var n = await db('user').bulk([
|
|
21
|
+
{ name: 'A' },
|
|
22
|
+
{ name: 'B' }
|
|
23
|
+
])
|
|
24
|
+
|
|
25
|
+
// Update one or many
|
|
26
|
+
var n = await db('user').update(
|
|
27
|
+
{ name: 'A' },
|
|
28
|
+
{ active: true }
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
// Get one (first match)
|
|
32
|
+
var doc = await db('user').get({ name: 'Heimdal' })
|
|
33
|
+
|
|
34
|
+
// Create indexes
|
|
35
|
+
await db('user').index([
|
|
36
|
+
['name'],
|
|
37
|
+
['name', 'email']
|
|
38
|
+
])
|
|
39
|
+
|
|
40
|
+
// Find
|
|
41
|
+
var docs = await db('user').find({ name: 'Heimdal' })
|
|
42
|
+
|
|
43
|
+
// Find with sort
|
|
44
|
+
var docs = await db('user').find(
|
|
45
|
+
{ name: 'Heimdal' },
|
|
46
|
+
{ sort: [{ name: 'asc' }] }
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
// Find with limit
|
|
50
|
+
var docs = await db('user').find(
|
|
51
|
+
{ name: 'Heimdal' },
|
|
52
|
+
{ limit: 1 }
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
// Find with projection
|
|
56
|
+
var docs = await db('user').find(
|
|
57
|
+
{ name: 'Heimdal' },
|
|
58
|
+
{ fields: ['name', 'email'] }
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
// Delete one or many
|
|
62
|
+
var n = await db('user').delete({ active: false })
|
|
63
|
+
|
|
64
|
+
// Count
|
|
65
|
+
var n = await db('user').count({ name: 'Heimdal' })
|
|
66
|
+
|
|
67
|
+
// Batch processing (streamed pagination)
|
|
68
|
+
await db('user').batch(
|
|
69
|
+
{ active: true },
|
|
70
|
+
{ size: 500, sort: [{ created: 'asc' }] },
|
|
71
|
+
async function (docs) {
|
|
72
|
+
// handle a chunk
|
|
73
|
+
}
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// Drop a specific database
|
|
77
|
+
await db('user').drop()
|
|
78
|
+
|
|
79
|
+
// Server-level compact
|
|
80
|
+
await db.compact('user')
|
|
81
|
+
|
|
82
|
+
// Drop all databases
|
|
83
|
+
await db.drop()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Notes on Indexing, Sorting, and Mango Queries
|
|
87
|
+
|
|
88
|
+
**1. Selector fields and indexes**
|
|
89
|
+
|
|
90
|
+
Mango performs best when the selector matches an existing index.
|
|
91
|
+
Any field used in `{ selector: … }` benefits from being part of an index, but it is not required unless sorting is used.
|
|
92
|
+
|
|
93
|
+
**2. Sorting requires indexing**
|
|
94
|
+
|
|
95
|
+
Mango enforces that **every field in the sort must be indexed**.
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
await db('user').index([['age']])
|
|
100
|
+
await db('user').find({}, { sort: [{ age: 'asc' }] }) // valid
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Sorting without the correct index returns an error.
|
|
104
|
+
|
|
105
|
+
**3. Compound indexes**
|
|
106
|
+
|
|
107
|
+
An index like:
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
await db('user').index([['name', 'email']])
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
supports selectors and sorts that use `name`, or `name` and `email` together, in the defined order.
|
|
114
|
+
|
|
115
|
+
**4. Projection (`fields`)**
|
|
116
|
+
|
|
117
|
+
Projection returns only selected fields.
|
|
118
|
+
Unindexed projection works fine; indexing does not affect `fields`.
|
|
119
|
+
|
|
120
|
+
**5. Pagination (batch)**
|
|
121
|
+
|
|
122
|
+
`batch()` uses Mango bookmarks internally.
|
|
123
|
+
It respects all options: `sort`, `limit`, `fields`, and `size`.
|
|
124
|
+
|
|
125
|
+
**6. Create-on-first-use**
|
|
126
|
+
|
|
127
|
+
Databases are auto-created when used. Explicit `drop()` allows clean-state tests.
|
|
128
|
+
|
|
129
|
+
**7. Null results**
|
|
130
|
+
|
|
131
|
+
`get()` returns `null` when no match exists.
|
|
132
|
+
|
|
133
|
+
### ID note
|
|
134
|
+
|
|
135
|
+
CouchDB stores documents with `_id`, but write responses return the same value as `id`.
|
|
136
|
+
Use `doc.id` after `create()`, and `_id` for all queries and stored documents.
|
|
137
|
+
|
|
138
|
+
### Acknowledgements
|
|
139
|
+
|
|
140
|
+
Created by [Tekki AS](https://tekki.no)
|
|
141
|
+
|
|
142
|
+
ISC Licensed.
|
package/index.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
var nano = require('nano')
|
|
2
|
+
|
|
3
|
+
module.exports = function (url) {
|
|
4
|
+
var server = nano(url)
|
|
5
|
+
|
|
6
|
+
var db = function (name) {
|
|
7
|
+
return api(name, server)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
//
|
|
11
|
+
// SERVER-LEVEL OPS (drop all, compact specific)
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
db.drop = async function () {
|
|
15
|
+
var list = await server.db.list()
|
|
16
|
+
for (var i = 0; i < list.length; i++) {
|
|
17
|
+
await server.db.destroy(list[i])
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
db.compact = async function (name) {
|
|
22
|
+
await server.db.compact(name)
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return db
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//
|
|
29
|
+
// COLLECTION-LEVEL API (ordered to match test suite)
|
|
30
|
+
//
|
|
31
|
+
|
|
32
|
+
function api(name, server) {
|
|
33
|
+
return {
|
|
34
|
+
//
|
|
35
|
+
// CRUD
|
|
36
|
+
//
|
|
37
|
+
|
|
38
|
+
create: async function (doc) {
|
|
39
|
+
var db = await ensure(name, server)
|
|
40
|
+
return db.insert(doc)
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
bulk: async function (docs) {
|
|
44
|
+
var db = await ensure(name, server)
|
|
45
|
+
await db.bulk({ docs: docs })
|
|
46
|
+
return docs.length
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
update: async function (query, update) {
|
|
50
|
+
var db = await ensure(name, server)
|
|
51
|
+
var r = await db.find({ selector: query })
|
|
52
|
+
if (!r.docs.length) return 0
|
|
53
|
+
|
|
54
|
+
var out = []
|
|
55
|
+
for (var i = 0; i < r.docs.length; i++) {
|
|
56
|
+
var cur = r.docs[i]
|
|
57
|
+
var next = {}
|
|
58
|
+
for (var k in cur) next[k] = cur[k]
|
|
59
|
+
for (var k in update) next[k] = update[k]
|
|
60
|
+
out.push(next)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
await db.bulk({ docs: out })
|
|
64
|
+
return out.length
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
get: async function (query) {
|
|
68
|
+
var dbi = await ensure(name, server)
|
|
69
|
+
var r = await dbi.find({ selector: query, limit: 1 })
|
|
70
|
+
return r.docs[0] || null
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
//
|
|
74
|
+
// FIND
|
|
75
|
+
//
|
|
76
|
+
|
|
77
|
+
find: async function (selector, opts) {
|
|
78
|
+
var dbi = await ensure(name, server)
|
|
79
|
+
|
|
80
|
+
var q = { selector: selector }
|
|
81
|
+
if (opts) {
|
|
82
|
+
if (opts.sort) q.sort = opts.sort
|
|
83
|
+
if (opts.limit) q.limit = opts.limit
|
|
84
|
+
if (opts.fields) q.fields = opts.fields
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var r = await dbi.find(q)
|
|
88
|
+
return r.docs
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
//
|
|
92
|
+
// INDEX
|
|
93
|
+
//
|
|
94
|
+
|
|
95
|
+
index: async function (list) {
|
|
96
|
+
var dbi = await ensure(name, server)
|
|
97
|
+
for (var i = 0; i < list.length; i++) {
|
|
98
|
+
await dbi.createIndex({ index: { fields: list[i] } })
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
//
|
|
103
|
+
// DELETE
|
|
104
|
+
//
|
|
105
|
+
|
|
106
|
+
delete: async function (query) {
|
|
107
|
+
var dbi = await ensure(name, server)
|
|
108
|
+
|
|
109
|
+
var r = await dbi.find({ selector: query })
|
|
110
|
+
if (!r.docs.length) return 0
|
|
111
|
+
|
|
112
|
+
var out = []
|
|
113
|
+
for (var i = 0; i < r.docs.length; i++) {
|
|
114
|
+
var d = r.docs[i]
|
|
115
|
+
out.push({
|
|
116
|
+
_id: d._id,
|
|
117
|
+
_rev: d._rev,
|
|
118
|
+
_deleted: true
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
await dbi.bulk({ docs: out })
|
|
123
|
+
return out.length
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
//
|
|
127
|
+
// COUNT
|
|
128
|
+
//
|
|
129
|
+
|
|
130
|
+
count: async function (query) {
|
|
131
|
+
var dbi = await ensure(name, server)
|
|
132
|
+
var r = await dbi.find({ selector: query })
|
|
133
|
+
return r.docs.length
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
//
|
|
137
|
+
// DROP (collection-level)
|
|
138
|
+
//
|
|
139
|
+
|
|
140
|
+
drop: async function () {
|
|
141
|
+
try {
|
|
142
|
+
await server.db.destroy(name)
|
|
143
|
+
} catch (e) {}
|
|
144
|
+
},
|
|
145
|
+
|
|
146
|
+
//
|
|
147
|
+
// BATCH
|
|
148
|
+
//
|
|
149
|
+
|
|
150
|
+
batch: async function (query, opt, fn) {
|
|
151
|
+
var dbi = await ensure(name, server)
|
|
152
|
+
|
|
153
|
+
var size = opt && opt.size ? opt.size : 100
|
|
154
|
+
var limit = opt && opt.limit
|
|
155
|
+
var sort = opt && opt.sort
|
|
156
|
+
var fields = opt && opt.fields
|
|
157
|
+
|
|
158
|
+
var bookmark = null
|
|
159
|
+
var remaining = limit
|
|
160
|
+
|
|
161
|
+
for (;;) {
|
|
162
|
+
var effectiveSize = size
|
|
163
|
+
if (remaining && remaining < effectiveSize) {
|
|
164
|
+
effectiveSize = remaining
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
var q = {
|
|
168
|
+
selector: query,
|
|
169
|
+
limit: effectiveSize
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (sort) q.sort = sort
|
|
173
|
+
if (fields) q.fields = fields
|
|
174
|
+
if (bookmark) q.bookmark = bookmark
|
|
175
|
+
|
|
176
|
+
var r = await dbi.find(q)
|
|
177
|
+
var docs = r.docs
|
|
178
|
+
if (!docs.length) break
|
|
179
|
+
|
|
180
|
+
await fn(docs)
|
|
181
|
+
|
|
182
|
+
if (remaining) {
|
|
183
|
+
remaining -= docs.length
|
|
184
|
+
if (remaining <= 0) break
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (!r.bookmark || docs.length < effectiveSize) break
|
|
188
|
+
bookmark = r.bookmark
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
//
|
|
195
|
+
// ENSURE DB EXISTS
|
|
196
|
+
//
|
|
197
|
+
|
|
198
|
+
async function ensure(name, server) {
|
|
199
|
+
try {
|
|
200
|
+
await server.db.get(name)
|
|
201
|
+
} catch (e) {
|
|
202
|
+
await server.db.create(name)
|
|
203
|
+
}
|
|
204
|
+
return server.db.use(name)
|
|
205
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eldoy/webdb",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Document database client powered by CouchDB.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"database",
|
|
7
|
+
"couchdb",
|
|
8
|
+
"mango",
|
|
9
|
+
"document-store",
|
|
10
|
+
"nosql",
|
|
11
|
+
"json",
|
|
12
|
+
"client",
|
|
13
|
+
"db",
|
|
14
|
+
"nano",
|
|
15
|
+
"webdb"
|
|
16
|
+
],
|
|
17
|
+
"scripts": {
|
|
18
|
+
"test": "spekk",
|
|
19
|
+
"test:watch": "nodemon --exec spekk"
|
|
20
|
+
},
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/eldoy/webdb.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/eldoy/webdb/issues"
|
|
27
|
+
},
|
|
28
|
+
"homepage": "https://github.com/eldoy/webdb#readme",
|
|
29
|
+
"author": "Vidar Eldøy <vidar@eldoy.com>",
|
|
30
|
+
"license": "ISC",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"nano": "^11.0.3"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"spekk": "^0.3.2"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
var webdb = require('../../index.js')
|
|
2
|
+
var db = webdb('http://admin:mysecretpassword@localhost:5984')
|
|
3
|
+
|
|
4
|
+
beforeEach(async function ({ t }) {
|
|
5
|
+
await db.drop()
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
//
|
|
9
|
+
// CRUD: create, bulk, update, get
|
|
10
|
+
//
|
|
11
|
+
|
|
12
|
+
test('create', async function ({ t }) {
|
|
13
|
+
var doc = await db('user').create({ name: 'Heimdal' })
|
|
14
|
+
t.ok(doc && doc.id)
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
test('bulk', async function ({ t }) {
|
|
18
|
+
var n = await db('user').bulk([{ name: 'A' }, { name: 'B' }])
|
|
19
|
+
t.equal(n, 2)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('update one', async function ({ t }) {
|
|
23
|
+
var doc = await db('user').create({ name: 'Old' })
|
|
24
|
+
var n = await db('user').update({ _id: doc.id }, { name: 'New' })
|
|
25
|
+
t.equal(n, 1)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('update many', async function ({ t }) {
|
|
29
|
+
await db('user').bulk([{ role: 'x' }, { role: 'x' }, { role: 'y' }])
|
|
30
|
+
var n = await db('user').update({ role: 'x' }, { role: 'z' })
|
|
31
|
+
t.equal(n, 2)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('get', async function ({ t }) {
|
|
35
|
+
await db('user').create({ name: 'Heimdal' })
|
|
36
|
+
var doc = await db('user').get({ name: 'Heimdal' })
|
|
37
|
+
t.ok(doc && doc._id)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
//
|
|
41
|
+
// FIND: base, sort, limit, fields
|
|
42
|
+
//
|
|
43
|
+
|
|
44
|
+
test('find', async function ({ t }) {
|
|
45
|
+
await db('user').bulk([
|
|
46
|
+
{ name: 'A', age: 1 },
|
|
47
|
+
{ name: 'A', age: 2 },
|
|
48
|
+
{ name: 'B', age: 3 }
|
|
49
|
+
])
|
|
50
|
+
|
|
51
|
+
var docs = await db('user').find({ name: 'A' })
|
|
52
|
+
t.ok(Array.isArray(docs))
|
|
53
|
+
t.equal(docs.length, 2)
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('find sort', async function ({ t }) {
|
|
57
|
+
await db('user').index([['n']])
|
|
58
|
+
|
|
59
|
+
await db('user').bulk([
|
|
60
|
+
{ name: 'A', n: 3 },
|
|
61
|
+
{ name: 'A', n: 1 },
|
|
62
|
+
{ name: 'A', n: 2 }
|
|
63
|
+
])
|
|
64
|
+
|
|
65
|
+
var docs = await db('user').find({ name: 'A' }, { sort: [{ n: 'asc' }] })
|
|
66
|
+
t.equal(docs[0].n, 1)
|
|
67
|
+
t.equal(docs[2].n, 3)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('find limit', async function ({ t }) {
|
|
71
|
+
await db('user').bulk([{ name: 'A' }, { name: 'A' }, { name: 'A' }])
|
|
72
|
+
var docs = await db('user').find({ name: 'A' }, { limit: 1 })
|
|
73
|
+
t.equal(docs.length, 1)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
test('find fields', async function ({ t }) {
|
|
77
|
+
await db('user').bulk([{ name: 'A', age: 10 }])
|
|
78
|
+
var docs = await db('user').find({ name: 'A' }, { fields: ['name'] })
|
|
79
|
+
t.ok(docs[0].name)
|
|
80
|
+
t.equal(docs[0].age, undefined)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
//
|
|
84
|
+
// INDEX
|
|
85
|
+
//
|
|
86
|
+
|
|
87
|
+
test('index', async function ({ t }) {
|
|
88
|
+
await db('user').index([['name', 'email']])
|
|
89
|
+
var docs = await db('user').find(
|
|
90
|
+
{ name: 'X' },
|
|
91
|
+
{ sort: [{ name: 'asc' }, { email: 'asc' }] }
|
|
92
|
+
)
|
|
93
|
+
t.ok(Array.isArray(docs))
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
//
|
|
97
|
+
// DELETE: one and many
|
|
98
|
+
//
|
|
99
|
+
|
|
100
|
+
test('delete one', async function ({ t }) {
|
|
101
|
+
var a = await db('user').create({ name: 'X' })
|
|
102
|
+
await db('user').create({ name: 'Y' })
|
|
103
|
+
|
|
104
|
+
var n = await db('user').delete({ _id: a.id })
|
|
105
|
+
t.equal(n, 1)
|
|
106
|
+
|
|
107
|
+
var doc = await db('user').get({ _id: a.id })
|
|
108
|
+
t.equal(doc, null)
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
test('delete many', async function ({ t }) {
|
|
112
|
+
await db('user').bulk([{ role: 'x' }, { role: 'x' }, { role: 'y' }])
|
|
113
|
+
var n = await db('user').delete({ role: 'x' })
|
|
114
|
+
t.equal(n, 2)
|
|
115
|
+
|
|
116
|
+
var docs = await db('user').find({ role: 'x' })
|
|
117
|
+
t.equal(docs.length, 0)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
//
|
|
121
|
+
// COUNT
|
|
122
|
+
//
|
|
123
|
+
|
|
124
|
+
test('count', async function ({ t }) {
|
|
125
|
+
await db('user').bulk([{ type: 'a' }, { type: 'a' }, { type: 'b' }])
|
|
126
|
+
var n = await db('user').count({ type: 'a' })
|
|
127
|
+
t.equal(n, 2)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
//
|
|
131
|
+
// BATCH: base + query + size + sort + limit + fields
|
|
132
|
+
//
|
|
133
|
+
|
|
134
|
+
test('batch', async function ({ t }) {
|
|
135
|
+
await db('user').bulk([{ v: 1 }, { v: 2 }, { v: 3 }, { v: 4 }])
|
|
136
|
+
|
|
137
|
+
var collected = []
|
|
138
|
+
|
|
139
|
+
await db('user').batch({}, { size: 2 }, async function (docs) {
|
|
140
|
+
for (var i = 0; i < docs.length; i++) collected.push(docs[i].v)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
t.equal(collected.length, 4)
|
|
144
|
+
t.ok(collected.includes(1))
|
|
145
|
+
t.ok(collected.includes(4))
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('batch with query', async function ({ t }) {
|
|
149
|
+
await db('user').bulk([
|
|
150
|
+
{ type: 'a', v: 1 },
|
|
151
|
+
{ type: 'a', v: 2 },
|
|
152
|
+
{ type: 'b', v: 3 }
|
|
153
|
+
])
|
|
154
|
+
|
|
155
|
+
var out = []
|
|
156
|
+
|
|
157
|
+
await db('user').batch({ type: 'a' }, { size: 10 }, async function (docs) {
|
|
158
|
+
for (var i = 0; i < docs.length; i++) out.push(docs[i].v)
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
t.equal(out.length, 2)
|
|
162
|
+
t.ok(out.includes(1))
|
|
163
|
+
t.ok(out.includes(2))
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('batch with size', async function ({ t }) {
|
|
167
|
+
await db('user').bulk([{ n: 1 }, { n: 2 }, { n: 3 }, { n: 4 }])
|
|
168
|
+
|
|
169
|
+
var chunks = []
|
|
170
|
+
|
|
171
|
+
await db('user').batch({}, { size: 2 }, async function (docs) {
|
|
172
|
+
chunks.push(docs.length)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
t.equal(chunks.length, 2)
|
|
176
|
+
t.equal(chunks[0], 2)
|
|
177
|
+
t.equal(chunks[1], 2)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('batch respects sort', async function ({ t }) {
|
|
181
|
+
await db('user').index([['n']])
|
|
182
|
+
|
|
183
|
+
await db('user').bulk([{ n: 3 }, { n: 1 }, { n: 2 }])
|
|
184
|
+
|
|
185
|
+
var list = []
|
|
186
|
+
|
|
187
|
+
await db('user').batch(
|
|
188
|
+
{},
|
|
189
|
+
{ size: 1, sort: [{ n: 'asc' }] },
|
|
190
|
+
async function (docs) {
|
|
191
|
+
list.push(docs[0].n)
|
|
192
|
+
}
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
t.deepEqual(list, [1, 2, 3])
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
test('batch respects limit', async function ({ t }) {
|
|
199
|
+
await db('user').bulk([{ n: 1 }, { n: 2 }, { n: 3 }])
|
|
200
|
+
|
|
201
|
+
var list = []
|
|
202
|
+
|
|
203
|
+
await db('user').batch({}, { limit: 2 }, async function (docs) {
|
|
204
|
+
for (var i = 0; i < docs.length; i++) list.push(docs[i].n)
|
|
205
|
+
})
|
|
206
|
+
|
|
207
|
+
t.equal(list.length, 2)
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
test('batch respects fields', async function ({ t }) {
|
|
211
|
+
await db('user').bulk([{ name: 'A', age: 10 }])
|
|
212
|
+
|
|
213
|
+
var fields = []
|
|
214
|
+
|
|
215
|
+
await db('user').batch(
|
|
216
|
+
{ name: 'A' },
|
|
217
|
+
{ fields: ['name'] },
|
|
218
|
+
async function (docs) {
|
|
219
|
+
fields.push(Object.keys(docs[0]))
|
|
220
|
+
}
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
t.deepEqual(fields[0], ['name'])
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
//
|
|
227
|
+
// DATABASE-LEVEL OPS
|
|
228
|
+
//
|
|
229
|
+
|
|
230
|
+
test('drop database', async function ({ t }) {
|
|
231
|
+
await db('user').create({ name: 'A' })
|
|
232
|
+
await db('user').drop()
|
|
233
|
+
var doc = await db('user').get({ name: 'A' })
|
|
234
|
+
t.equal(doc, null)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
test('compact', async function ({ t }) {
|
|
238
|
+
await db('user').create({ name: 'A' })
|
|
239
|
+
await db('user').update({ name: 'A' }, { name: 'B' })
|
|
240
|
+
await db.compact('user')
|
|
241
|
+
t.ok(true)
|
|
242
|
+
})
|