NamH 84dd54a98c
Fix/250 (#349)
* fix(UI): #250 better chat left side bar

Signed-off-by: James <james@jan.ai>
Co-authored-by: James <james@jan.ai>
2023-10-15 07:46:15 -07:00

240 lines
7.4 KiB
TypeScript

var PouchDB = require("pouchdb-node");
PouchDB.plugin(require("pouchdb-find"));
var path = require("path");
var { app } = require("electron");
var fs = require("fs");
const dbs: Record<string, any> = {};
/**
* Create a collection on data store
*
* @param name name of the collection to create
* @param schema schema of the collection to create, include fields and their types
* @returns Promise<void>
*
*/
function createCollection(name: string, schema?: { [key: string]: any }): Promise<void> {
return new Promise<void>((resolve) => {
const dbPath = path.join(app.getPath("userData"), "databases");
if (!fs.existsSync(dbPath)) fs.mkdirSync(dbPath);
const db = new PouchDB(`${path.join(dbPath, name)}`);
dbs[name] = db;
resolve();
});
}
/**
* Delete a collection
*
* @param name name of the collection to delete
* @returns Promise<void>
*
*/
function deleteCollection(name: string): Promise<void> {
// Do nothing with Unstructured Database
return dbs[name].destroy();
}
/**
* Insert a value to a collection
*
* @param collectionName name of the collection
* @param value value to insert
* @returns Promise<any>
*
*/
function insertOne(collectionName: string, value: any): Promise<any> {
if (!value._id) return dbs[collectionName].post(value).then((doc) => doc.id);
return dbs[collectionName].put(value).then((doc) => doc.id);
}
/**
* Update value of a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateOne(collectionName: string, key: string, value: any): Promise<void> {
console.debug(`updateOne ${collectionName}: ${key} - ${JSON.stringify(value)}`);
return dbs[collectionName].get(key).then((doc) => {
return dbs[collectionName].put({
_id: key,
_rev: doc._rev,
...value,
},
{ force: true });
}).then((res: any) => {
console.info(`updateOne ${collectionName} result: ${JSON.stringify(res)}`);
}).catch((err: any) => {
console.error(`updateOne ${collectionName} error: ${err}`);
});
}
/**
* Update value of a collection's records
*
* @param collectionName name of the collection
* @param selector selector of records to update
* @param value value to update
* @returns Promise<void>
*
*/
function updateMany(collectionName: string, value: any, selector?: { [key: string]: any }): Promise<any> {
// Creates keys from selector for indexing
const keys = selector ? Object.keys(selector) : [];
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return (
keys.length > 0
? dbs[collectionName].createIndex({
// There is selector so we need to create index
index: { fields: keys },
})
: Promise.resolve()
) // No selector, so no need to create index
.then(() =>
dbs[collectionName].find({
// Find documents using Mango queries
selector,
})
)
.then((data) => {
const docs = data.docs.map((doc) => {
// Update doc with new value
return (doc = {
...doc,
...value,
});
});
return dbs[collectionName].bulkDocs(docs);
});
}
/**
* Delete a collection's record
*
* @param collectionName name of the collection
* @param key key of the record to delete
* @returns Promise<void>
*
*/
function deleteOne(collectionName: string, key: string): Promise<void> {
return findOne(collectionName, key).then((doc) => dbs[collectionName].remove(doc));
}
/**
* Delete a collection records by selector
*
* @param {string} collectionName name of the collection
* @param {{ [key: string]: any }} selector selector for retrieving records.
* @returns Promise<void>
*
*/
function deleteMany(collectionName: string, selector?: { [key: string]: any }): Promise<void> {
// Creates keys from selector for indexing
const keys = selector ? Object.keys(selector) : [];
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return (
keys.length > 0
? dbs[collectionName].createIndex({
// There is selector so we need to create index
index: { fields: keys },
})
: Promise.resolve()
) // No selector, so no need to create index
.then(() =>
dbs[collectionName].find({
// Find documents using Mango queries
selector,
})
)
.then((data) => {
return Promise.all(
// Remove documents
data.docs.map((doc) => {
return dbs[collectionName].remove(doc);
})
);
});
}
/**
* Retrieve a record from a collection in the data store.
* @param {string} collectionName - The name of the collection containing the record to retrieve.
* @param {string} key - The key of the record to retrieve.
* @returns {Promise<any>} A promise that resolves when the record is retrieved.
*/
function findOne(collectionName: string, key: string): Promise<any> {
return dbs[collectionName].get(key).catch(() => undefined);
}
/**
* Gets records in a collection in the data store using a selector.
* @param {string} collectionName - The name of the collection containing records to retrieve.
* @param {{ [key: string]: any }} selector - The selector to use to retrieve records.
* @param {[{ [key: string]: any }]} sort - The sort options to use to retrieve records.
* @returns {Promise<any>} A promise that resolves with the selected records.
*/
function findMany(
collectionName: string,
selector?: { [key: string]: any },
sort?: [{ [key: string]: any }]
): Promise<any> {
const keys = selector ? Object.keys(selector) : [];
const sortKeys = sort ? sort.flatMap((e) => (e ? Object.keys(e) : undefined)) : [];
// Note that we are specifying that the field must be greater than or equal to null
// which is a workaround for the fact that the Mango query language requires us to have a selector.
// In CouchDB collation order, null is the "lowest" value, and so this will return all documents regardless of their field value.
sortKeys.forEach((key) => {
if (!keys.includes(key)) {
selector = { ...selector, [key]: { $gt: null } };
}
});
// There is no selector & sort, so we can just use allDocs() to get all the documents.
if (sortKeys.concat(keys).length === 0) {
return dbs[collectionName]
.allDocs({
include_docs: true,
endkey: "_design",
inclusive_end: false,
})
.then((data) => data.rows.map((row) => row.doc));
}
// At a basic level, there are two steps to running a query: createIndex()
// (to define which fields to index) and find() (to query the index).
return dbs[collectionName]
.createIndex({
// Create index for selector & sort
index: { fields: sortKeys.concat(keys) },
})
.then(() => {
// Find documents using Mango queries
return dbs[collectionName].find({
selector,
sort,
});
})
.then((data) => data.docs); // Return documents
}
module.exports = {
createCollection,
deleteCollection,
insertOne,
findOne,
findMany,
updateOne,
updateMany,
deleteOne,
deleteMany,
};