Compare commits
No commits in common. "2a65449959b6038eb3ef044f5c57807bd328f339" and "fa31073482958d9f944878e5fe8e75b0ed42fb04" have entirely different histories.
2a65449959
...
fa31073482
@ -1,17 +0,0 @@
|
|||||||
async function main() {
|
|
||||||
const {CouchDatabaseClient} = require('../src/classes/couchdb/CouchDatabaseClient');
|
|
||||||
|
|
||||||
const config = {
|
|
||||||
protocol: 'http',
|
|
||||||
host: 'localhost',
|
|
||||||
port: 5984,
|
|
||||||
username: 'admin',
|
|
||||||
password: 'secret'
|
|
||||||
}
|
|
||||||
const client = new CouchDatabaseClient(config);
|
|
||||||
const users = client.getCollection('user');
|
|
||||||
|
|
||||||
const user = await users.create({name: 'testUser1', email: 'testUser1@example.com' });
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
174
package-lock.json
generated
174
package-lock.json
generated
@ -13,8 +13,7 @@
|
|||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"minio": "^8.0.5",
|
"minio": "^8.0.5"
|
||||||
"nano": "^10.1.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jsdoc": "^4.0.4"
|
"jsdoc": "^4.0.4"
|
||||||
@ -374,32 +373,6 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
|
||||||
"version": "1.8.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
|
|
||||||
"integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"follow-redirects": "^1.15.6",
|
|
||||||
"form-data": "^4.0.0",
|
|
||||||
"proxy-from-env": "^1.1.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/axios/node_modules/form-data": {
|
|
||||||
"version": "4.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
|
|
||||||
"integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"asynckit": "^0.4.0",
|
|
||||||
"combined-stream": "^1.0.8",
|
|
||||||
"es-set-tostringtag": "^2.1.0",
|
|
||||||
"mime-types": "^2.1.12"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/b4a": {
|
"node_modules/b4a": {
|
||||||
"version": "1.6.7",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/b4a/-/b4a-1.6.7.tgz",
|
||||||
@ -954,26 +927,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
|
||||||
"version": "1.15.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
|
||||||
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"debug": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/for-each": {
|
"node_modules/for-each": {
|
||||||
"version": "0.3.5",
|
"version": "0.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
|
||||||
@ -1758,26 +1711,6 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/nano": {
|
|
||||||
"version": "10.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/nano/-/nano-10.1.4.tgz",
|
|
||||||
"integrity": "sha512-bJOFIPLExIbF6mljnfExXX9Cub4W0puhDjVMp+qV40xl/DBvgKao7St4+6/GB6EoHZap7eFnrnx4mnp5KYgwJA==",
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"axios": "^1.7.4",
|
|
||||||
"node-abort-controller": "^3.1.1",
|
|
||||||
"qs": "^6.13.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/node-abort-controller": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
||||||
@ -1807,18 +1740,6 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/object-inspect": {
|
|
||||||
"version": "1.13.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
|
||||||
"integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/once": {
|
"node_modules/once": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
@ -1898,12 +1819,6 @@
|
|||||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/proxy-from-env": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/punycode.js": {
|
"node_modules/punycode.js": {
|
||||||
"version": "2.3.1",
|
"version": "2.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
|
||||||
@ -1914,21 +1829,6 @@
|
|||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/qs": {
|
|
||||||
"version": "6.14.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz",
|
|
||||||
"integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"dependencies": {
|
|
||||||
"side-channel": "^1.1.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.6"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/query-string": {
|
"node_modules/query-string": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
|
||||||
@ -2098,78 +1998,6 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/side-channel": {
|
|
||||||
"version": "1.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
|
|
||||||
"integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.3",
|
|
||||||
"side-channel-list": "^1.0.0",
|
|
||||||
"side-channel-map": "^1.0.1",
|
|
||||||
"side-channel-weakmap": "^1.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-list": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"object-inspect": "^1.13.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-map": {
|
|
||||||
"version": "1.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
|
|
||||||
"integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/side-channel-weakmap": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"call-bound": "^1.0.2",
|
|
||||||
"es-errors": "^1.3.0",
|
|
||||||
"get-intrinsic": "^1.2.5",
|
|
||||||
"object-inspect": "^1.13.3",
|
|
||||||
"side-channel-map": "^1.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 0.4"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/signal-exit": {
|
"node_modules/signal-exit": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
"archiver": "^7.0.1",
|
"archiver": "^7.0.1",
|
||||||
"csv-writer": "^1.6.0",
|
"csv-writer": "^1.6.0",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"minio": "^8.0.5",
|
"minio": "^8.0.5"
|
||||||
"nano": "^10.1.4"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"jsdoc": "^4.0.4"
|
"jsdoc": "^4.0.4"
|
||||||
|
@ -1,97 +0,0 @@
|
|||||||
// couchdb/CouchCollection.js
|
|
||||||
const { CouchDocument } = require('./CouchDocument.js');
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
*/
|
|
||||||
class CouchCollection {
|
|
||||||
/**
|
|
||||||
* @param {import('nano').DocumentScope<any>} db
|
|
||||||
*/
|
|
||||||
constructor(db) {
|
|
||||||
this.db = db;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} id
|
|
||||||
* @returns {Promise<import('../interfaces').IDocument<T> | null>}
|
|
||||||
*/
|
|
||||||
async get(id) {
|
|
||||||
try {
|
|
||||||
const doc = await this.db.get(id);
|
|
||||||
return new CouchDocument(
|
|
||||||
doc._id,
|
|
||||||
doc.data,
|
|
||||||
doc.createdAt ? new Date(doc.createdAt) : undefined,
|
|
||||||
doc.updatedAt ? new Date(doc.updatedAt) : undefined,
|
|
||||||
doc._rev
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
if (err.statusCode === 404) return null;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Promise<Array<import('../interfaces').IDocument<T>>>}
|
|
||||||
*/
|
|
||||||
async list() {
|
|
||||||
const result = await this.db.list({ include_docs: true });
|
|
||||||
return result.rows
|
|
||||||
.filter(row => row.doc)
|
|
||||||
.map(row =>
|
|
||||||
new CouchDocument(
|
|
||||||
row.doc._id,
|
|
||||||
row.doc.data,
|
|
||||||
row.doc.createdAt ? new Date(row.doc.createdAt) : undefined,
|
|
||||||
row.doc.updatedAt ? new Date(row.doc.updatedAt) : undefined,
|
|
||||||
row.doc._rev
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {T} data
|
|
||||||
* @returns {Promise<import('../interfaces').IDocument<T>>}
|
|
||||||
*/
|
|
||||||
async create(data) {
|
|
||||||
const now = new Date();
|
|
||||||
const doc = {
|
|
||||||
data,
|
|
||||||
createdAt: now.toISOString(),
|
|
||||||
updatedAt: now.toISOString(),
|
|
||||||
};
|
|
||||||
const response = await this.db.insert(doc);
|
|
||||||
return new CouchDocument(response.id, data, now, now, response.rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} id
|
|
||||||
* @param {Partial<T>} data
|
|
||||||
* @returns {Promise<import('../interfaces').IDocument<T>>}
|
|
||||||
*/
|
|
||||||
async update(id, data) {
|
|
||||||
const existing = await this.db.get(id);
|
|
||||||
const updated = {
|
|
||||||
...existing,
|
|
||||||
data: {
|
|
||||||
...existing.data,
|
|
||||||
...data,
|
|
||||||
},
|
|
||||||
updatedAt: new Date().toISOString(),
|
|
||||||
};
|
|
||||||
const response = await this.db.insert(updated);
|
|
||||||
return new CouchDocument(response.id, updated.data, existing.createdAt, new Date(), response.rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} id
|
|
||||||
* @returns {Promise<void>}
|
|
||||||
*/
|
|
||||||
async delete(id) {
|
|
||||||
const doc = await this.db.get(id);
|
|
||||||
await this.db.destroy(id, doc._rev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
module.exports = { CouchCollection };
|
|
@ -1,40 +0,0 @@
|
|||||||
// couchdb/CouchDatabaseClient.js
|
|
||||||
const nano = require('nano');
|
|
||||||
const { CouchCollection } = require('./CouchCollection.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @typedef {import('../interfaces').ICollection<T>} ICollection
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef {Object} CouchDBConfig
|
|
||||||
* @property {string} protocol
|
|
||||||
* @property {string} host
|
|
||||||
* @property {number} port
|
|
||||||
* @property {string} username
|
|
||||||
* @property {string} password
|
|
||||||
*/
|
|
||||||
|
|
||||||
class CouchDatabaseClient {
|
|
||||||
/**
|
|
||||||
* @param {CouchDBConfig} config
|
|
||||||
*/
|
|
||||||
constructor(config) {
|
|
||||||
const { protocol, host, port, username, password } = config;
|
|
||||||
const url = `${protocol}://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}`;
|
|
||||||
this.server = nano(url);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param {string} name
|
|
||||||
* @returns {ICollection<T>}
|
|
||||||
*/
|
|
||||||
getCollection(name) {
|
|
||||||
const db = this.server.db.use(name);
|
|
||||||
return new CouchCollection(db);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { CouchDatabaseClient };
|
|
@ -1,34 +0,0 @@
|
|||||||
// couchdb/CouchDocument.js
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @typedef {Object} IDocument
|
|
||||||
* @property {string} id
|
|
||||||
* @property {T} data
|
|
||||||
* @property {Date=} createdAt
|
|
||||||
* @property {Date=} updatedAt
|
|
||||||
* @property {string|number=} version
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @implements {IDocument<T>}
|
|
||||||
*/
|
|
||||||
class CouchDocument {
|
|
||||||
/**
|
|
||||||
* @param {string} id
|
|
||||||
* @param {T} data
|
|
||||||
* @param {Date=} createdAt
|
|
||||||
* @param {Date=} updatedAt
|
|
||||||
* @param {string|number=} version
|
|
||||||
*/
|
|
||||||
constructor(id, data, createdAt, updatedAt, version) {
|
|
||||||
this.id = id;
|
|
||||||
this.data = data;
|
|
||||||
this.createdAt = createdAt;
|
|
||||||
this.updatedAt = updatedAt;
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { CouchDocument };
|
|
@ -1,175 +0,0 @@
|
|||||||
# [NoSQL][CouchDB]Node.jsの利用ガイド(nanoライブラリ)
|
|
||||||
|
|
||||||
CouchDBはドキュメント指向のNoSQLデータベースで、
|
|
||||||
HTTPベースのRESTful APIで操作可能です。
|
|
||||||
|
|
||||||
Node.jsでは公式の軽量クライアントであるnanoを使うことで
|
|
||||||
簡単にCouchDBとやりとりできます。
|
|
||||||
|
|
||||||
[戻る](https://wiki.pglikers.com/e/en/private/db/nosql/couch-db)
|
|
||||||
|
|
||||||
|
|
||||||
## nanoのインストール
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install nano
|
|
||||||
```
|
|
||||||
|
|
||||||
nanoはNode.js向けに設計されているため
|
|
||||||
ブラウザ(フロントエンド)では使用できません。
|
|
||||||
|
|
||||||
## CouchDBとの接続
|
|
||||||
|
|
||||||
```sh
|
|
||||||
const nano = require('nano')('http://admin:secret@localhost:5984');
|
|
||||||
```
|
|
||||||
|
|
||||||
**envなどを活用して設定する場合**
|
|
||||||
|
|
||||||
```conf
|
|
||||||
COUCHDB_HOST=localhost
|
|
||||||
COUCHDB_PORT=5984
|
|
||||||
COUCHDB_USER=admin
|
|
||||||
COUCHDB_PASSWORD=secret
|
|
||||||
```
|
|
||||||
|
|
||||||
サンプルコード
|
|
||||||
|
|
||||||
```js
|
|
||||||
require('dotenv').config();
|
|
||||||
const protocol = 'http'; // or https
|
|
||||||
const {
|
|
||||||
COUCHDB_HOST,
|
|
||||||
COUCHDB_PORT,
|
|
||||||
COUCHDB_USER,
|
|
||||||
COUCHDB_PASSWORD
|
|
||||||
} = process.env;
|
|
||||||
const url = `${protocol}://${COUCHDB_USER}:${COUCHDB_PASSWORD}@${COUCHDB_HOST}:${COUCHDB_PORT}`;
|
|
||||||
const nano = require('nano')(url);
|
|
||||||
```
|
|
||||||
|
|
||||||
## CouchDBの使い方
|
|
||||||
|
|
||||||
### データベースの操作
|
|
||||||
|
|
||||||
```js
|
|
||||||
// データベース作成
|
|
||||||
await nano.db.create('user');
|
|
||||||
|
|
||||||
// データベース削除
|
|
||||||
await nano.db.destroy('user');
|
|
||||||
|
|
||||||
// 一覧取得
|
|
||||||
const dbs = await nano.db.list();
|
|
||||||
console.log(dbs);
|
|
||||||
```
|
|
||||||
|
|
||||||
### ドキュメントの操作
|
|
||||||
|
|
||||||
```js
|
|
||||||
const db = nano.db.use('user');
|
|
||||||
|
|
||||||
// 作成
|
|
||||||
await db.insert({ name: 'Alice', email: 'alice@example.com' });
|
|
||||||
|
|
||||||
// 取得
|
|
||||||
const doc = await db.get('document_id');
|
|
||||||
|
|
||||||
// 更新
|
|
||||||
doc.age = 30;
|
|
||||||
await db.insert(doc);
|
|
||||||
|
|
||||||
// 削除
|
|
||||||
await db.destroy(doc._id, doc._rev);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### インデックス作成(パフォーマンス向上)
|
|
||||||
|
|
||||||
Mangoクエリ(find)はインデックスがあると高速になります。
|
|
||||||
例としてユーザーの年齢でよく検索するなら、
|
|
||||||
事前に下記のようにインデックスを作ることを推奨します
|
|
||||||
|
|
||||||
```js
|
|
||||||
await db.createIndex({
|
|
||||||
index: { fields: ['age'] },
|
|
||||||
name: 'age-index',
|
|
||||||
type: 'json'
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tips
|
|
||||||
|
|
||||||
### nanoの初期化オプション
|
|
||||||
|
|
||||||
```js
|
|
||||||
const nano = require('nano')({
|
|
||||||
url: 'http://localhost:5984',
|
|
||||||
requestDefaults: {
|
|
||||||
headers: {
|
|
||||||
Authorization: 'Basic ...' // 任意のカスタムヘッダー
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### 検索のサンプル
|
|
||||||
|
|
||||||
#### 名前が'A'から始まる人だけ取得
|
|
||||||
|
|
||||||
CouchDBのMangoクエリでは文字列の部分一致は
|
|
||||||
範囲指定($gte, $lt)を使う方法が一般的です
|
|
||||||
("startkey" などでは存在しない)
|
|
||||||
|
|
||||||
```js
|
|
||||||
const result = await db.find({
|
|
||||||
selector: {
|
|
||||||
name: {
|
|
||||||
"$gte": "A",
|
|
||||||
"$lt": "B"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sort: [{ name: "asc" }]
|
|
||||||
});
|
|
||||||
result.docs.forEach(doc => console.log(doc));
|
|
||||||
```
|
|
||||||
|
|
||||||
#### OR条件を使いたい($or)
|
|
||||||
|
|
||||||
```js
|
|
||||||
const result = await db.find({
|
|
||||||
selector: {
|
|
||||||
"$or": [
|
|
||||||
{ age: { "$lt": 30 } },
|
|
||||||
{
|
|
||||||
name: {
|
|
||||||
"$gte": "Z",
|
|
||||||
"$lt": "Z\ufff0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
複数条件やソートを行う場合は
|
|
||||||
インデックスも複数フィールドに対応しておくと効率が良くなります
|
|
||||||
|
|
||||||
```js
|
|
||||||
await db.createIndex({
|
|
||||||
index: {
|
|
||||||
fields: ['age', 'name']
|
|
||||||
},
|
|
||||||
name: 'age-name-index'
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### フロントエンドで使えるのか
|
|
||||||
|
|
||||||
認証情報がURLに含まれるためセキュリティ上もフロントでの使用は不適切
|
|
||||||
|
|
||||||
* フロントエンドからCouchDBを扱いたい場合:
|
|
||||||
1. **PouchDBを活用する**
|
|
||||||
* フロント向けCouchDB互換DB。リアルタイム同期・オフライン対応も可能
|
|
||||||
2. サーバー側でnanoを使いREST APIを作ってフロントから叩く
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* Create a new database
|
|
||||||
* nanoでデータベースを作成する
|
|
||||||
*/
|
|
||||||
const nano = require('nano')('http://admin:secret@localhost:5984');
|
|
||||||
|
|
||||||
// データベース作成
|
|
||||||
const createDB = async (dbName) => {
|
|
||||||
nano.db.create(dbName, (err, body) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(body);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// データベース削除
|
|
||||||
const deleteDB = async (dbName) => {
|
|
||||||
nano.db.destroy(dbName, (err, body) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(body);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// データベース一覧表示
|
|
||||||
const getDbList = async () => {
|
|
||||||
nano.db.list((err, body) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(body);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
// await deleteDB('user');
|
|
||||||
// await createDB('product');
|
|
||||||
// await createDB('item');
|
|
||||||
// await getDbList();
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
@ -1,85 +0,0 @@
|
|||||||
/**
|
|
||||||
* Create a new database
|
|
||||||
* nanoでデータベースを作成する
|
|
||||||
*/
|
|
||||||
const nano = require('nano')('http://admin:secret@localhost:5984');
|
|
||||||
|
|
||||||
// ドキュメント作成
|
|
||||||
const createDocument = async (dbName,post) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
db.insert(post, (err, body) => {
|
|
||||||
if (err) {
|
|
||||||
console.log(err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(body);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ドキュメントを一覧を取得する
|
|
||||||
const getDocumentList = async (dbName) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
const result = await db.list({ include_docs: true });
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ドキュメントを一覧を取得する(抽出する)
|
|
||||||
const findDocument = async (dbName,selector) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
const result = await db.find({selector: selector});
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ドキュメントを取得する
|
|
||||||
const getDocument = async (dbName,docId) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
const doc = await db.get(docId);
|
|
||||||
return doc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ドキュメントを更新する
|
|
||||||
const updateDocument = async (dbName,doc) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
return await db.insert(doc);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ドキュメントを削除する
|
|
||||||
const deleteDocument = async (dbName,doc) => {
|
|
||||||
const db = nano.use(dbName);
|
|
||||||
return await db.destroy(doc._id, doc._rev);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function main() {
|
|
||||||
// ドキュメントを作成する
|
|
||||||
await createDocument('user',{id:1,name:'Alice',email:'alice@example.com'});
|
|
||||||
// IDを指定する
|
|
||||||
await createDocument('user',{_id:'user_1',id:2,name:'Bob',email:'bob@example.com'});
|
|
||||||
|
|
||||||
// ドキュメントを取得する
|
|
||||||
const doc = await getDocument('user','user_1');
|
|
||||||
console.log(doc.name);
|
|
||||||
|
|
||||||
// ドキュメントを更新する
|
|
||||||
doc.age = 20;
|
|
||||||
await updateDocument('user',doc);
|
|
||||||
|
|
||||||
// ドキュメントを削除する
|
|
||||||
await deleteDocument('user',doc);
|
|
||||||
|
|
||||||
// ドキュメントの一覧を取得する
|
|
||||||
const docs = await getDocumentList('user');
|
|
||||||
console.log(docs);
|
|
||||||
|
|
||||||
// ドキュメントの検索する
|
|
||||||
const selector = {name: 'Bob'};
|
|
||||||
docs = await findDocument('user',selector);
|
|
||||||
console.log(docs);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
|
@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* Interface for NoSQL document
|
|
||||||
* @param T - Type of data
|
|
||||||
* @param id - Document ID
|
|
||||||
*/
|
|
||||||
export interface IDocument<T> {
|
|
||||||
id: string;
|
|
||||||
data: T;
|
|
||||||
createdAt?: Date;
|
|
||||||
updatedAt?: Date;
|
|
||||||
version?: string | number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to unsubscribe from updates *
|
|
||||||
* watch()を使わない・購読解除の必要もないならvoid戻り値でも問題なし
|
|
||||||
* 将来的には、unsubscribeするための関数を返す
|
|
||||||
*/
|
|
||||||
export type UnsubscribeFn = () => void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for NoSQL collection
|
|
||||||
* @param T - Type of data
|
|
||||||
*/
|
|
||||||
export interface ICollection<T> {
|
|
||||||
get(id: string): Promise<IDocument<T> | null>;
|
|
||||||
list(): Promise<IDocument<T>[]>;
|
|
||||||
create(data: T): Promise<IDocument<T>>;
|
|
||||||
update(id: string, data: Partial<T>): Promise<IDocument<T>>;
|
|
||||||
delete(id: string): Promise<void>;
|
|
||||||
// オプション: 更新監視など
|
|
||||||
watch?(onChange: (doc: IDocument<T>) => void): UnsubscribeFn;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IDatabaseClient {
|
|
||||||
getCollection<T>(name: string): ICollection<T>;
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user