Comparing version 1.0.5 to 2.0.0
145
example.js
const ComfyDB = require( "./index" ); | ||
async function testComfy() { | ||
try { | ||
console.log( "initializing..." ); | ||
await ComfyDB.Init(); | ||
console.log( "creating collection..." ); | ||
await ComfyDB.Collections.Create( "test" ); | ||
console.log( "listing collections..." ); | ||
var collections = await ComfyDB.Collections.List(); | ||
console.log( collections ); | ||
await ComfyDB.Data.SetByKey( "test", "a", { name: "test" }, true ); | ||
var item = await ComfyDB.Data.FindByKey( "test", "a" ); | ||
console.log( item ); | ||
await new Promise((res) => setTimeout(res, 1000)); | ||
await ComfyDB.Data.SetByKey( "test", [ "a", "b", "c" ], [ { name: "A", points: 5 }, { name: "B" }, { name: "C" } ], true ); | ||
await ComfyDB.Data.SetByKey( "test", "a", { name: "Z" }, true ); | ||
var all = await ComfyDB.Data.FindLatest( "test" );//await ComfyDB.Data.Find( "test", {} ); | ||
console.table( all ); | ||
await ComfyDB.Data.Increment( "test", [ "a", "b" ], "points", 10 ); | ||
var itemA = await ComfyDB.Data.FindByField( "test", "name", ComfyDB.Is.NotEqual, "C" ); | ||
console.table( itemA ); | ||
await ComfyDB.Data.Decrement( "test", [ "a", "b" ], "points", 3 ); | ||
var itemA = await ComfyDB.Data.FindByField( "test", "name", ComfyDB.Is.NotEqual, "C" ); | ||
console.table( itemA ); | ||
await ComfyDB.Data.DeleteByKey( "test", "b" ); | ||
all = await ComfyDB.Data.FindLatest( "test" );//await ComfyDB.Data.Find( "test", {} ); | ||
console.table( all ); | ||
console.log( "deleting collection..." ); | ||
await ComfyDB.Collections.Delete( "test" ); | ||
console.log( "listing collections..." ); | ||
collections = await ComfyDB.Collections.List(); | ||
console.log( collections ); | ||
} | ||
catch( ex ) { | ||
console.log( ex ); | ||
} | ||
finally { | ||
console.log( "closing..." ); | ||
ComfyDB.Close(); | ||
} | ||
try { | ||
console.log( "connecting..." ); | ||
await ComfyDB.Connect(); // Defaults to { url = "mongodb://localhost:27017", dbname = "ComfyDB" } | ||
console.log( "isConnected:", ComfyDB.IsConnected() ); | ||
// NOTE: All functions default to the "ComfyDB" collection | ||
// Clear the database | ||
ComfyDB.DeleteAll(); | ||
// Add users | ||
console.log( "saving users..." ); | ||
await ComfyDB.Store( "user1", { username: "Instafluff", website: "https://www.instafluff.tv", profile: "Comfiest Coder and Mug Chef!" } ); | ||
await ComfyDB.Store( "user2", { username: "Fluffington", website: "", profile: "Fluffy, Yellow, Hamsterbear." } ); | ||
await ComfyDB.Store( "user3", { username: "Teapup", website: "", profile: "Semi-licensed rainbow skittle tricycle pupper. I'm the greatest!" } ); | ||
await ComfyDB.Store( "user4", { username: "Catastrophe", website: "", profile: "BOW WOW, I'm the Captain of Catastrophe. FEEL MY WRATH!" } ); | ||
// Count users | ||
console.log( "user count:", await ComfyDB.Count() ); | ||
// Get user1 | ||
console.log( "getting user1..." ); | ||
let user1 = await ComfyDB.Get( "user1" ); | ||
console.log( user1 ); | ||
// Delete user4 | ||
console.log( "deleting user4..." ); | ||
let user4 = await ComfyDB.Get( "user4" ); | ||
console.log( user4 ); | ||
await ComfyDB.Delete( "user4" ); | ||
// Get all users | ||
console.log( "getting all users..." ); | ||
let allUsers = await ComfyDB.Search(); | ||
console.table( allUsers ); | ||
// Get all users with "fluff" in the username | ||
console.log( "getting users with 'fluff' in the username, sorted by username..." ); | ||
let fluffUsers = await ComfyDB.Search( { sortBy: "username", sort: "asc", where: { username: { contains: "fluff" } } } ); | ||
console.table( fluffUsers ); | ||
// Delete all users with "chef" in the profile | ||
console.log( "deleting users starting with 'comfiest' in the profile" ); | ||
await ComfyDB.DeleteAll( { where: { profile: { startsWith: "comfiest" } } } ); | ||
// Get all remaining users sorted by the key descending | ||
console.log( "getting remaining users..." ); | ||
let remainingUsers = await ComfyDB.Search( { sortBy: "key", sort: "desc" } ); | ||
console.table( remainingUsers ); | ||
// Add stat counters | ||
console.log( "saving stats..."); | ||
ComfyDB.DeleteAll( null, "game-stats" ); | ||
await ComfyDB.Store( "teapup", { hitpoints: 100, skittles: 0 }, "game-stats" ); | ||
await ComfyDB.Store( "catastrophe", { hitpoints: 100 }, "game-stats" ); | ||
// Get all stats | ||
console.log( "getting all stats..." ); | ||
let allStats = await ComfyDB.Search( null, "game-stats" ); | ||
console.table( allStats ); | ||
// Increment Teapup skittles by 10 | ||
console.log( "incrementing Teapup's skittles by 10" ); | ||
await ComfyDB.Increment( "skittles", { by: 10, where: { key: { equals: "teapup" } } }, "game-stats" ); | ||
// Decrement all hitpoints by 30 | ||
console.log( "decrementing all hitpoints by 30" ); | ||
await ComfyDB.Decrement( "hitpoints", { by: 30 }, "game-stats" ); | ||
// Decrement Catastrophe hitpoints by 50 | ||
console.log( "decrementing Not-Teapup's hitpoints by 50" ); | ||
await ComfyDB.Decrement( "hitpoints", { by: 50, where: { key: { "!": "teapup" } } }, "game-stats" ); | ||
// Get final stats | ||
console.log( "getting final stats..." ); | ||
let finalStats = await ComfyDB.Search( null, "game-stats" ); | ||
console.table( finalStats ); | ||
} | ||
catch( ex ) { | ||
console.log( ex ); | ||
} | ||
finally { | ||
console.log( "closing..." ); | ||
ComfyDB.Close(); | ||
} | ||
} | ||
// (async () => { | ||
// testComfy(); | ||
// })(); | ||
testComfy(); | ||
// Run a self-contained MongoDB using ComfyMongoDB to run the example | ||
const ComfyMongo = require( "comfy-mongo" )(); | ||
ComfyMongo.on( "output", ( data ) => { | ||
// console.log( data ); | ||
}); | ||
ComfyMongo.on( "error", ( err ) => { | ||
// console.log( err ); | ||
}); | ||
ComfyMongo.on( "ready", async () => { | ||
console.log( "[ComfyDB] Ready..." ); | ||
await testComfy(); | ||
ComfyMongo.shutdown(); | ||
}); | ||
ComfyMongo.on( "exit", ( code ) => { | ||
console.log( "[ComfyDB] Exit:", code ); | ||
}); |
707
index.js
@@ -1,473 +0,270 @@ | ||
const MongoClient = require('mongodb').MongoClient; | ||
// const assert = require('assert'); | ||
const MongoClient = require( "mongodb" ).MongoClient; | ||
// // Connection URL | ||
// const url = 'mongodb://localhost:27017'; | ||
let comfyDB = { | ||
_mongo: null, | ||
_DB: null, | ||
Connect: function( { url = "mongodb://localhost:27017", dbname = "comfyDB" } = {} ) { | ||
return new Promise( async ( resolve, reject ) => { | ||
try { | ||
comfyDB._mongo = await MongoClient.connect( url, { useNewUrlParser: true } ); | ||
comfyDB._DB = comfyDB._mongo.db( dbname ); | ||
resolve(); | ||
} | ||
catch( err ) { | ||
reject( err ); | ||
} | ||
}); | ||
}, | ||
IsConnected: function() { | ||
return !!comfyDB._mongo; | ||
}, | ||
Close: function() { | ||
if( comfyDB._mongo ) { | ||
comfyDB._mongo.close(); | ||
comfyDB._mongo = null; | ||
comfyDB._DB = null; | ||
} | ||
}, | ||
Collections: async function() { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return comfyDB._DB.listCollections().toArray().then( list => list.map( x => x.name ) ); | ||
}, | ||
HasCollection: async function( collection ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return ( await comfyDB.Collections() ).includes( collection ); | ||
}, | ||
DeleteCollection: function( collection ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return comfyDB._DB.collection( collection ).drop(); | ||
}, | ||
Store: async function( key, data, collection = "ComfyDefault" ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
// Create an index on the key field for performance | ||
if( ! await comfyDB.HasCollection( collection ) ) { | ||
await comfyDB._DB.createCollection( collection ).then( x => comfyDB._DB.collection( collection ).createIndex( { "key": 1 }, { unique: true } ) ); | ||
} | ||
// // Database Name | ||
// const dbName = 'myproject'; | ||
const set = comfyDB._DB.collection( collection ); | ||
let values = []; | ||
if( typeof key === "string" || key instanceof String ) { | ||
values = [ { ...data, key: key } ]; | ||
key = [ key ]; | ||
} | ||
else if( Array.isArray( key ) && Array.isArray( data ) ) { | ||
if( key.length !== data.length ) { | ||
throw new Error( "Key-Data Length Mismatch" ); | ||
} | ||
values = data.map( ( x, i ) => ({ ...x, key: key[ i ] }) ); | ||
} | ||
else { | ||
throw new Error( "Invalid Argument Type" ); | ||
} | ||
values = values.map( x => ({ ...x, updatedAt: new Date() } ) ); | ||
// // Use connect method to connect to the Server | ||
// MongoClient.connect(url, function(err, client) { | ||
// assert.equal(null, err); | ||
// console.log("Connected successfully to server"); | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
key.forEach( ( k, i ) => bulkOp.find( { key: k } ).upsert().updateOne( { | ||
$set: values[ i ], | ||
$setOnInsert: { | ||
createdAt: new Date() | ||
} | ||
}, { upsert: true } ) ); | ||
return bulkOp.execute(); | ||
}, | ||
Get: async function( key, collection = "ComfyDefault" ) { | ||
let result = await comfyDB.Search( { key: key }, collection ); | ||
// console.log( result ); | ||
return result ? result[ 0 ] : null; | ||
}, | ||
Search: function( options = null, collection = "ComfyDefault" ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
const set = comfyDB._DB.collection( collection ); | ||
let search = {}; | ||
let sort = null; | ||
// const db = client.db(dbName); | ||
// Assign options with defaults | ||
options = Object.assign( { | ||
sortBy: "createdAt", | ||
sort: "asc", | ||
limit: 100, | ||
start: 0, | ||
where: null, | ||
}, options ); | ||
// // insertDocuments(db, function() { | ||
// // client.close(); | ||
// // }); | ||
// client.close(); | ||
// }); | ||
// Aliases | ||
if( options.orderBy ) { | ||
options.sortBy = options.orderBy; | ||
} | ||
if( options.count ) { | ||
options.limit = options.count; | ||
} | ||
// const insertDocuments = function(db, callback) { | ||
// // Get the documents collection | ||
// const collection = db.collection('documents'); | ||
// // Insert some documents | ||
// collection.insertMany([ | ||
// {a : 1}, {a : 2}, {a : 3} | ||
// ], function(err, result) { | ||
// assert.equal(err, null); | ||
// assert.equal(3, result.result.n); | ||
// assert.equal(3, result.ops.length); | ||
// console.log("Inserted 3 documents into the collection"); | ||
// callback(result); | ||
// }); | ||
// } | ||
// Sorting | ||
sort = { [options.sortBy]: options.sort.toLowerCase().startsWith( "asc" ) ? 1 : -1 }; | ||
const COMPARE = { | ||
Equal: "=", | ||
NotEqual: "!", | ||
LessThan: "<", | ||
LessThanOrEqual: "<=", | ||
GreaterThan: ">", | ||
GreaterThanOrEqual: ">=", | ||
StartsWith: "^", | ||
EndsWith: "$", | ||
Contains: "_", | ||
True: "1", | ||
False: "0" | ||
}; | ||
if( options.where ) { | ||
search = generateMongoSearchFromObject( options.where ); | ||
} | ||
if( options.key ) { | ||
search[ "key" ] = options.key; | ||
} | ||
// console.log( search ); | ||
/** | ||
* @typedef InitOptions | ||
* @prop {string} [url] (Currently unsued) MongoDB URL to connect to. Defaults to `"mongodb://localhost:27017"`. | ||
* @prop {string} [name] Name of the database to use. Defaults to `"comfyDB"`. | ||
*/ | ||
let query = set.find( search ); | ||
query = query.skip( options.start ); | ||
query = query.limit( options.limit ); | ||
query = query.sort( sort ); | ||
return query.toArray(); | ||
}, | ||
Delete: function( key, collection = "ComfyDefault" ) { | ||
return comfyDB.DeleteAll( { key: key }, collection ); | ||
}, | ||
DeleteAll: function( options = {}, collection = "ComfyDefault" ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
// check if key is a single string or an array for batch update | ||
const set = comfyDB._DB.collection( collection ); | ||
let search = {}; | ||
if( options.where ) { | ||
search = generateMongoSearchFromObject( options.where ); | ||
} | ||
if( options.key ) { | ||
search[ "key" ] = options.key; | ||
} | ||
/** | ||
* @typedef FindOptions | ||
* @prop {string} [id] Find by the `_id` field. | ||
* @prop {string} [key] Find by the `key` field. | ||
* @prop {boolean} [count] Limit the amount of results. | ||
* @prop {string} [sortBy] A field to sort results by. Must also set | ||
* `isOrderDescending`. | ||
* @prop {boolean} [isOrderDescending] If `sortBy` is set, choose to sort by | ||
* descending (`true`) or ascending (`false`). | ||
* @prop {string} [field] A field to compare against. Must also set `compare` and `value`. | ||
* @prop {"=" | "!" | "<" | "<=" | ">" | ">=" | "^" | "$" | "_" | "1" | "0"} compare | ||
* Compare the set `field` against a `value`. Must also set `value` and `field`. | ||
* - Equal: "=", | ||
* - Not equal: "!", | ||
* - Less than: "<", | ||
* - Less than or equal: "<=", | ||
* - Greater than: ">", | ||
* - Greater than or equal: ">=", | ||
* - Starts with: "^", | ||
* - Ends with: "$", | ||
* - Contains: "_", | ||
* - True: "1", | ||
* - False: "0" | ||
* @prop {*} [value] A value to `compare` against at `field`. Must also set `compare` and `field`. | ||
*/ | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
bulkOp.find( search ).remove(); | ||
return bulkOp.execute(); | ||
}, | ||
Increment: function( field, options = null, collection = "ComfyDefault" ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
const set = comfyDB._DB.collection( collection ); | ||
let search = {}; | ||
let comfyDBRunning = false; | ||
// Assign options with defaults | ||
options = Object.assign( { | ||
by: 1, | ||
where: null, | ||
}, options ); | ||
let comfyDB = { | ||
_mongo: null, | ||
/** @type {import('mongodb').MongoClient} */ | ||
_client: null, | ||
/** @type {import('mongodb').Db} */ | ||
_DB: null, | ||
/** | ||
* Initialize the connection. | ||
* | ||
* @param {InitOptions} options | ||
* @returns {Promise} | ||
*/ | ||
Init: function( { url = "mongodb://localhost:27017", name = "comfyDB" } = {} ) { | ||
return new Promise( async ( resolve, reject ) => { | ||
try { | ||
comfyDBRunning = false; | ||
// TODO: If we're not depending on the default local instance of mongo, skip booting it up | ||
comfyDB._mongo = require( "comfy-mongo" )(); | ||
comfyDB._mongo.on( "output", ( data ) => { | ||
// console.log( data ); | ||
}); | ||
comfyDB._mongo.on( "error", ( err ) => { | ||
// console.log( err ); | ||
if( comfyDBRunning ) { | ||
} | ||
else { | ||
reject( err ); | ||
} | ||
}); | ||
comfyDB._mongo.on( "ready", async () => { | ||
console.log( "[ComfyDB] Ready..." ); | ||
comfyDBRunning = true; | ||
comfyDB._client = await MongoClient.connect( url, { useNewUrlParser: true } ); | ||
comfyDB._DB = comfyDB._client.db( name ); | ||
resolve(); | ||
}); | ||
comfyDB._mongo.on( "exit", ( code ) => { | ||
console.log( "[ComfyDB] Exit:", code ); | ||
comfyDB.Close(); | ||
comfyDBRunning = false; | ||
}); | ||
} | ||
catch( err ) { | ||
reject( err ); | ||
} | ||
}); | ||
}, | ||
/** | ||
* Check if the database is running. | ||
* | ||
* @returns {boolean} | ||
*/ | ||
IsRunning: function() { | ||
return comfyDBRunning; | ||
}, | ||
/** | ||
* Close the connection and shutdown the server. | ||
* | ||
* @param {boolean} [shouldExit] Whether or not to end the process. Defauls to true. | ||
*/ | ||
Close: function( shouldExit = true ) { | ||
if( comfyDB._client ) { | ||
comfyDB._client.close(); | ||
comfyDB._client = null; | ||
comfyDB._DB = null; | ||
} | ||
if( comfyDB._mongo ) { | ||
comfyDB._mongo.shutdown(); | ||
} | ||
if( shouldExit ) { | ||
process.exit(); | ||
} | ||
}, | ||
/** | ||
* Create a backup of the database. (TODO) | ||
*/ | ||
Backup: function() { | ||
// TODO: Backup Database | ||
}, | ||
/** | ||
* Restore from a backup of the database. (TODO) | ||
*/ | ||
Restore: function() { | ||
// TODO: Restore Database | ||
}, | ||
Collections: { | ||
/** | ||
* Create a collection in the database. | ||
* | ||
* @param {string} name Name of the collection to create. | ||
* @returns {Promise<string>} | ||
*/ | ||
Create: async function( name ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return comfyDB._DB.createCollection( name ).then( x => comfyDB._DB.collection( name ).createIndex( { key: 1 }, { unique: true } ) ); | ||
}, | ||
/** | ||
* List the collections in the database. | ||
* | ||
* @return {Promise<string[]>} | ||
*/ | ||
List: async function() { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return comfyDB._DB.listCollections().toArray().then( list => list.map( x => x.name ) ); | ||
}, | ||
/** | ||
* Delete/drop a collection in the database. | ||
* | ||
* @param {string} name Name of the collection to delete. | ||
* @return {Promise} | ||
*/ | ||
Delete: async function( name ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
return comfyDB._DB.collection( name ).drop(); | ||
}, | ||
}, | ||
Is: COMPARE, | ||
Data: { | ||
/** | ||
* (TODO) | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {*} options | ||
*/ | ||
Set: function( collection, { options } ) { | ||
// check if key is a single string or an array for batch update | ||
// TODO: Add Objects, tagging with timestamp for create/update | ||
}, | ||
/** | ||
* Set data in the database by a key name. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string | string[]} key Key or array of keys to save data to. | ||
* @param {* | []} data Data to save to the key. If `key` is an array, | ||
* data must also be an array that matches the length of the `key` array. | ||
* @param {*} [overwrite] Whether or not to overwrite existing data in the | ||
* database. Defaults to true. | ||
* @returns {Promise<import("mongodb").BulkWriteResult | import("mongodb").InsertWriteOpResult>} | ||
*/ | ||
SetByKey: function( collection, key, data, overwrite = true ) { | ||
// check if key is a single string or an array for batch update | ||
// TODO: Add timestamp for create/update | ||
const set = comfyDB._DB.collection( collection ); | ||
let values = []; | ||
if( typeof key === "string" || key instanceof String ) { | ||
values = [ { ...data, key: key } ]; | ||
key = [ key ]; | ||
} | ||
else if( Array.isArray( key ) && Array.isArray( data ) ) { | ||
if( key.length !== data.length ) { | ||
throw new Error( "Key-Data Length Mismatch" ); | ||
} | ||
values = data.map( ( x, i ) => ({ ...x, key: key[ i ] }) ); | ||
} | ||
else { | ||
throw new Error( "Invalid Argument Type" ); | ||
} | ||
values = values.map( x => ({ ...x, updatedAt: new Date() } ) ); | ||
// Aliases | ||
if( options.where ) { | ||
search = generateMongoSearchFromObject( options.where ); | ||
} | ||
if( options.key ) { | ||
search[ "key" ] = options.key; | ||
} | ||
// console.log( search ); | ||
if( overwrite ) { | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
key.forEach( ( k, i ) => bulkOp.find( { key: k } ).upsert().updateOne( { | ||
$set: values[ i ], | ||
$setOnInsert: { | ||
createdAt: new Date() | ||
} | ||
}, { upsert: true } ) ); | ||
return bulkOp.execute(); | ||
} | ||
else { | ||
return set.insertMany( values ); | ||
} | ||
}, | ||
/** | ||
* (TODO) | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {*} options | ||
*/ | ||
Delete: function( collection, { options } ) { | ||
// check if key is a single string or an array for batch update | ||
// TODO: Delete objects | ||
}, | ||
/** | ||
* Delete data in the database by key name. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string | string[]} key Key or array of keys to delete data at. | ||
* @returns {Promise<import("mongodb").BulkWriteResult>} | ||
*/ | ||
DeleteByKey: function( collection, key ) { | ||
// check if key is a single string or an array for batch update | ||
const set = comfyDB._DB.collection( collection ); | ||
if( typeof key === "string" || key instanceof String ) { | ||
key = [ key ]; | ||
} | ||
else if( Array.isArray( key ) ) { | ||
} | ||
else { | ||
throw new Error( "Invalid Argument Type" ); | ||
} | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
bulkOp.find( search ).upsert().update( { | ||
$inc: { [field]: options.by }, | ||
$set: { | ||
updatedAt: new Date() | ||
}, | ||
$setOnInsert: { | ||
createdAt: new Date() | ||
} | ||
} );//, { upsert: true, multi: true } ); | ||
return bulkOp.execute(); | ||
}, | ||
Decrement: function( field, options = null, collection = "ComfyDefault" ) { | ||
options = Object.assign( { | ||
by: 1, | ||
where: null, | ||
}, options ); | ||
options.by = -options.by; | ||
return comfyDB.Increment( field, options, collection ); | ||
}, | ||
Count: function( options = null, collection = "ComfyDefault" ) { | ||
if( !comfyDB._DB ) { throw new Error( "No Connection" ); } | ||
const set = comfyDB._DB.collection( collection ); | ||
let search = {}; | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
key.forEach( ( k, i ) => bulkOp.find( { key: k } ).remove() ); | ||
return bulkOp.execute(); | ||
}, | ||
/** | ||
* Increment a value at field of key in the database. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string | string[]} key Key or array of keys to increment. | ||
* @param {string} field Field of key to increment. | ||
* @param {number} amount Amount to increment field. | ||
* @returns {Promise<import("mongodb").BulkWriteResult>} | ||
*/ | ||
Increment: function( collection, key, field, amount ) { | ||
const set = comfyDB._DB.collection( collection ); | ||
let values = []; | ||
if( typeof key === "string" || key instanceof String ) { | ||
values = [ { key: key } ]; | ||
key = [ key ]; | ||
} | ||
else if( Array.isArray( key ) ) { | ||
values = key.map( ( x, i ) => ({ key: x }) ); | ||
} | ||
else { | ||
throw new Error( "Invalid Argument Type" ); | ||
} | ||
values = values.map( x => ({ ...x, updatedAt: new Date() } ) ); | ||
// Assign options with defaults | ||
options = Object.assign( { | ||
where: null, | ||
}, options ); | ||
let bulkOp = set.initializeUnorderedBulkOp(); | ||
key.forEach( ( k, i ) => bulkOp.find( { key: k } ).upsert().updateOne( { | ||
$set: values[ i ], | ||
$inc: { [field]: amount }, | ||
$setOnInsert: { | ||
createdAt: new Date() | ||
} | ||
}, { upsert: true } ) ); | ||
return bulkOp.execute(); | ||
}, | ||
/** | ||
* Decrement a value at field of key in the database. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string | string[]} key Key or array of keys to decrement. | ||
* @param {string} field Field of key to decrement. | ||
* @param {number} amount Amount to decrement field. | ||
* @returns {Promise<import("mongodb").BulkWriteResult>} | ||
*/ | ||
Decrement: function( collection, key, field, amount ) { | ||
return comfyDB.Data.Increment( collection, key, field, -amount ); | ||
}, | ||
/** | ||
* Query data in the database by some options. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {FindOptions} options Options for the find query. | ||
* @returns {Promise<any[]>} | ||
*/ | ||
Find: function( collection, options ) { | ||
// TODO: Add time query for how long it took | ||
const set = comfyDB._DB.collection( collection ); | ||
let search = {}; | ||
let sort = null; | ||
if( options.id ) { | ||
search[ "_id" ] = options.id; | ||
} | ||
if( options.key ) { | ||
search[ "key" ] = options.key; | ||
} | ||
if( options.sortBy ) { | ||
if( typeof options.isOrderDescending !== undefined ) { | ||
sort = { [options.sortBy]: options.isOrderDescending ? -1 : 1 }; | ||
} | ||
} | ||
if( options.field ) { | ||
switch( options.compare ) { | ||
case COMPARE.Equal: | ||
search[ options.field ] = { $eq: options.value }; | ||
break; | ||
case COMPARE.NotEqual: | ||
search[ options.field ] = { $ne: options.value }; | ||
break; | ||
case COMPARE.LessThan: | ||
search[ options.field ] = { $lt: options.value }; | ||
break; | ||
case COMPARE.LessThanOrEqual: | ||
search[ options.field ] = { $lte: options.value }; | ||
break; | ||
case COMPARE.GreaterThan: | ||
search[ options.field ] = { $gt: options.value }; | ||
break; | ||
case COMPARE.GreaterThanOrEqual: | ||
search[ options.field ] = { $gte: options.value }; | ||
break; | ||
// TODO: Add the remaining comparisons! | ||
// StartsWith: "^", | ||
// EndsWith: "$", | ||
// Contains: "_", | ||
// True: "1", | ||
// False: "0" | ||
} | ||
} | ||
// Aliases | ||
if( options.where ) { | ||
search = generateMongoSearchFromObject( options.where ); | ||
} | ||
if( options.key ) { | ||
search[ "key" ] = options.key; | ||
} | ||
// console.log( search ); | ||
// console.log( search ); | ||
let query = set.find( search ); | ||
if( options.count ) { | ||
query = query.limit( options.count ); | ||
} | ||
if( sort ) { | ||
query = query.sort( sort ); | ||
} | ||
return query.toArray(); | ||
}, | ||
/** | ||
* Query data in the database by its document `_id`. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string} id A document _id value. | ||
* @returns {Promise<any[]>} | ||
*/ | ||
FindById: function( collection, id ) { | ||
return comfyDB.Data.Find( collection, { id } ); | ||
}, | ||
/** | ||
* Query data in the database by its document `_id`. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string} id A document _id value. | ||
* @returns {Promise<any[]>} | ||
*/ | ||
FindByKey: function( collection, key ) { | ||
return comfyDB.Data.Find( collection, { key } ); | ||
}, | ||
/** | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string} field Name of the field to find by.() | ||
* @param {"=" | "!" | "<" | "<=" | ">" | ">=" | "^" | "$" | "_" | "1" | "0"} compare Compare the set `field` against a `value`. See Find. Defaults to True. | ||
* @param {*} [value] Value to find by when comparing. Defaults to "". | ||
* @param {number} [count] Limit the amount of results. Defaults to 100. | ||
* @param {boolean} [descending] If the results should be sorted by | ||
* descending order. Defaults to true. | ||
* @returns {Promise<any[]>} | ||
*/ | ||
FindByField: function( collection, field, compare = COMPARE.True, value = "", count = 100, descending = true ) { | ||
// TODO: Support an array of fields and comparisons | ||
return comfyDB.Data.Find( collection, { | ||
field, | ||
compare, | ||
value, | ||
count, | ||
sortBy: field, | ||
isOrderDescending: descending | ||
}); | ||
}, | ||
/** | ||
* Get the last `count` items in a collection. | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {number} [count] Limit the amount of results in the response. | ||
* Defaults to 100. | ||
* @returns {Promise<any[]>} | ||
*/ | ||
FindLatest: function( collection, count = 100 ) { | ||
return comfyDB.Data.Find( collection, { | ||
sortBy: "updatedAt", | ||
isOrderDescending: true, | ||
count, | ||
} ); | ||
}, | ||
/** | ||
* | ||
* @param {string} collection Name of the collection in the database. | ||
* @param {string} field Name of the field to count. | ||
* @param {string} [compare] Compare operator. See Find. Defaults to True. | ||
* @param {*} [value] Value to compare against. Defaults to "". | ||
* @returns {Promise<number>} | ||
*/ | ||
Count: function( collection, field, compare = COMPARE.True, value = "" ) { | ||
// TODO: Optimize!!! | ||
return comfyDB.Data.FindByField( collection, field, compare, value ).length; | ||
}, | ||
}, | ||
let query = set.find( search ); | ||
query = query.skip( options.start ); | ||
query = query.limit( options.limit ); | ||
return query.count(); | ||
}, | ||
}; | ||
function generateMongoSearchFromObject( where ) { | ||
let search = {}; | ||
Object.keys( where ).forEach( field => { | ||
// Check on the first key of field for the operator | ||
if( Object.keys( where[ field ] ).length === 0 ) { | ||
throw new Error( "Missing Search Op for Field:", field ); | ||
} | ||
let searchOp = Object.keys( where[ field ] )[ 0 ]; | ||
switch( searchOp.toLowerCase() ) { | ||
case "eq": | ||
case "equals": | ||
case "equal": | ||
case "is": | ||
case "isequal": | ||
case "isequalto": | ||
case "=": | ||
search[ field ] = { $eq: where[ field ][ searchOp ] }; | ||
break; | ||
case "ne": | ||
case "not": | ||
case "notequals": | ||
case "notequal": | ||
case "doesntequal": | ||
case "isnot": | ||
case "isnt": | ||
case "isnotequal": | ||
case "isnotequalto": | ||
case "!": | ||
case "!=": | ||
case "<>": | ||
search[ field ] = { $ne: where[ field ][ searchOp ] }; | ||
break; | ||
case "before": | ||
case "lessthan": | ||
case "lt": | ||
case "<": | ||
search[ field ] = { $lt: where[ field ][ searchOp ] }; | ||
break; | ||
case "after": | ||
case "greaterthan": | ||
case "gt": | ||
case ">": | ||
search[ field ] = { $gt: where[ field ][ searchOp ] }; | ||
break; | ||
case "contains": | ||
case "includes": | ||
search[ field ] = RegExp( `.*${where[ field ][ searchOp ]}.*`, "i" ); | ||
break; | ||
case "starts": | ||
case "startswith": | ||
case "begins": | ||
case "beginswith": | ||
case "prefix": | ||
search[ field ] = RegExp( `^${where[ field ][ searchOp ]}`, "i" ); | ||
break; | ||
case "ends": | ||
case "endswith": | ||
case "suffix": | ||
search[ field ] = RegExp( `${where[ field ][ searchOp ]}$`, "i" ); | ||
break; | ||
default: | ||
throw new Error( "Unsupported Search Op:", searchOp ); | ||
} | ||
}); | ||
return search; | ||
} | ||
module.exports = comfyDB; |
{ | ||
"name": "comfydb", | ||
"version": "1.0.5", | ||
"version": "2.0.0", | ||
"description": "Comfiest Way To Use A Database!", | ||
@@ -8,3 +8,3 @@ "main": "index.js", | ||
"start": "node index.js", | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"test": "jest" | ||
}, | ||
@@ -26,5 +26,8 @@ "repository": { | ||
"dependencies": { | ||
"mongodb": "^3.5.5" | ||
}, | ||
"devDependencies": { | ||
"comfy-mongo": "^1.0.7", | ||
"mongodb": "^3.2.7" | ||
"jest": "^25.2.4" | ||
} | ||
} |
183
README.md
@@ -1,3 +0,180 @@ | ||
ComfyDB README | ||
# ComfyDB | ||
The Comfiest Way to Use a Database | ||
**ComfyDB** is a ***SUPER EASY AND INTUITIVE*** interface to a database *(Currently supports MongoDB)* so you don't have to keep looking up the documentation. | ||
ComfyDB abstracts away the specific syntax for working with databases so that it's intuitive and easy to use for common scenarios while benefitting from search, performance, redudancy of a full-fledged database. | ||
It also takes care of the mundane tasks like setting and maintaining `createdAt` and `updatedAt` fields for each object entry. | ||
```javascript | ||
const ComfyDB = require( "comfydb" ); | ||
await ComfyDB.Connect(); | ||
await ComfyDB.Store( "user1", { username: "Instafluff", profile: "Comfiest Coder and Mug Chef!", cakes: 0 } ); | ||
await ComfyDB.Store( "user2", { username: "Fluffington", profile: "Fluffy, Yellow, Hamsterbear.", cakes: 0 } ); | ||
let user1 = await ComfyDB.Get( "user1" ); | ||
await ComfyDB.Increment( "cakes", { by: 2, where: { username: { equals: "instafluff" } } } ); | ||
let users = await ComfyDB.Search( { sortBy: "createdAt", where: { username: { contains: "fluff" } } } ); | ||
ComfyDB.Close(); | ||
``` | ||
## Instafluff ## | ||
> *Like these projects? The best way to support my open-source projects is by becoming a Comfy Sponsor on GitHub!* | ||
> https://github.com/sponsors/instafluff | ||
> *Come and hang out with us at the Comfiest Corner on Twitch!* | ||
> https://twitch.tv/instafluff | ||
#### ComfyDB Requirements | ||
ComfyDB requires a MongoDB instance to connect to. | ||
Here are three ways for you to setup an instance of MongoDB: | ||
1. Run a self-contained instance running on NodeJS via [ComfyMongoDB](https://www.github.com/instafluff/ComfyMongoDB) *(Recommended for developers)* | ||
2. Install [MongoDB Community Edition](https://docs.mongodb.com/manual/administration/install-community/) as a service on your computer *(Recommended for beginners)* | ||
3. Use a cloud-hosted MongoDB instance with [MongoDB Atlas](https://www.mongodb.com/cloud/atlas) *(Recommended for companies and production)* | ||
## Instructions ## | ||
1. Install `comfydb` | ||
``` | ||
npm install comfydb --save | ||
``` | ||
2. Connect to a MongoDB database and store, retrieve, search data | ||
```javascript | ||
const ComfyDB = require( "comfydb" ); | ||
(async () => { | ||
try { | ||
await ComfyDB.Connect( { url: "mongodb://localhost:27017", dbname: "ComfyDB" } ); | ||
// Sample users | ||
await ComfyDB.Store( "user1", { username: "Instafluff", website: "https://www.instafluff.tv", profile: "Comfiest Coder and Mug Chef!" } ); | ||
await ComfyDB.Store( "user2", { username: "Fluffington", profile: "Fluffy, Yellow, Hamsterbear." } ); | ||
await ComfyDB.Store( "user3", { username: "Teapup", profile: "Semi-licensed rainbow skittle tricycle pupper. I'm the greatest!" } ); | ||
// Get user3 | ||
let user3 = await ComfyDB.Get( "user3" ); | ||
console.log( user3 ); | ||
// Get all users with "fluff" in the username | ||
let fluffUsers = await ComfyDB.Search( { sortBy: "username", sort: "asc", where: { username: { contains: "fluff" } } } ); | ||
console.table( fluffUsers ); | ||
} | ||
catch( err ) { | ||
console.error( err ); | ||
} | ||
})(); | ||
``` | ||
## Functions ## | ||
```javascript | ||
// --- Configuration --- | ||
// Connect to a database on MongoDB | ||
await ComfyDB.Connect( options = { url: "mongodb://localhost:27017", dbname: "comfyDB" } ); | ||
// Check if we are connected to a database | ||
ComfyDB.IsConnected(); | ||
// Close connection to the database | ||
ComfyDB.Close(); | ||
// --- Data --- | ||
// Insert/Update JSON object entry by key (e.g. userID, username, timestamp, ... ) | ||
await ComfyDB.Store( key, data, collection = "ComfyDefault" ); | ||
// Retrieve object entry by key | ||
await ComfyDB.Get( key, collection = "ComfyDefault" ); | ||
// Retrieve array of object entries based on search options | ||
await ComfyDB.Search( options = { sortBy: "createdAt", sort: "asc", limit: 100, start: 0, where: null, key: null }, collection = "ComfyDefault" ); | ||
// Delete object entry by key | ||
await ComfyDB.Delete( key ); | ||
// Delete object entries based on search options | ||
await ComfyDB.DeleteAll( options = { where: null, key: null }, collection = "ComfyDefault" ); | ||
// Increment a field in object entries matching search options | ||
await ComfyDB.Increment( field, options = { where: null, key: null }, collection = "ComfyDefault" ); | ||
// Decrement a field in object entries matching search options | ||
await ComfyDB.Decrement( field, options = { where: null, key: null }, collection = "ComfyDefault" ); | ||
// Count the number of object entries matching search options | ||
await ComfyDB.Count( options = { where: null, key: null }, collection = "ComfyDefault" ); | ||
// --- Collections --- | ||
// Get a full list of collections | ||
await ComfyDB.Collections(); | ||
// Check if a collection exists | ||
await ComfyDB.HasCollection( collection ); | ||
// Delete a collection | ||
await ComfyDB.DeleteCollection( collection ); | ||
``` | ||
## How to Use Search Options ## | ||
Defining the search conditions for ComfyDB functions is done by setting up a configuration object. | ||
#### Search Options Fields #### | ||
- **sortBy** (orderBy) *(Only in **ComfyDB.Search**)* | ||
- The name of the stored data object entry's field to use for the sort. Defaults to `createdAt`. | ||
- **sort** *(Only in **ComfyDB.Search**)* | ||
- `asc` or `desc` for ascending or descending sort based on the `sortBy` field. Defaults to `asc`. | ||
- **limit** (count) *(Only in **ComfyDB.Search**)* | ||
- Maximum number of results, used for pagination. Defaults to `100`. | ||
- **start** *(Only in **ComfyDB.Search**)* | ||
- Starting index for the results, used for pagination. Defaults to `0`. | ||
- **by** *(Only in **ComfyDB.Increment** and **ComfyDB.Decrement**)* | ||
- Amount to increment or decrement the field's value by for all matching data object entries. Defaults to `1`. | ||
- **where** | ||
- Refines the search based on values inside the stored data object entries. Defaults to `null`. | ||
#### Where Object #### | ||
The inner `where` object helps fine-tune the search based on aspects of the stored data object entries. | ||
All keys inside this object adds to the search conditions (e.g. `username`) using a search operator (e.g. `equals`). Keys are case-sensitive. Operators are not case-sensitive. | ||
**Operators** | ||
- **equals** (eq, equal, equals, is, isEqual, isequalto, =) | ||
- **not** (ne, notequals, notequal, doesntequal, isnot, isnt, isnotequal, isnotequalto, !, !=, <>) | ||
- **before** (lessthan, lt, <) | ||
- **after** (after, greaterthan, gt, >) | ||
- **contains** (includes) | ||
- **startsWith** (starts, begins, beginswith, prefix) | ||
- **endsWith** (ends, suffix) | ||
#### Search Option Examples #### | ||
```javascript | ||
// Retrieving the most recent 5 entries where the score is higher than 50 points | ||
let scores = await ComfyDB.Search( { sortBy: "updatedAt", sort: "desc", limit: 5, start: 0, where: { score: { ">": 50 } } } ); | ||
// Decrementing hitpoints for all of party A's members in the Game-Stats collection | ||
await ComfyDB.Decrement( "hitpoints", { by: 30, where: { party: { equals: "A" } } }, "game-stats" ); | ||
// Deleting all enemies with HP < 0 in the Game-Stats collection | ||
await ComfyDB.DeleteAll( { where: { isEnemy: { is: true }, hitpoints: { "<": 0 } } }, "game-stats" ); | ||
``` | ||
## Credits ## | ||
Thank you too all the participants of this project! | ||
**DutchGamer46, Instafluff, Instafriend, ChatTranslator, SourBeers, zivivi1, That_MS_Gamer, jjanders85, simrose4u, Kyoslilmonster, sparky_pugwash, i_am_from_mars, julieee22, codingkatie, jellydance, LilyHazel, lewdmelon, Luxadin_, FuriousFur, sethorizer, Ella_Fint, DEAD_P1XL, Psychosys82, malfunct, UppahahAA, Stay_Hydrated_Bot, holloway87, RIKACHET, SullyGnome, Gyrojett, roberttables, ReaperofTerror, BigShoe, GoonPontoon, dot_commie, retro_crt, rhonin52, Clarkio, mholloway24, ObsidianTalon, jFeliWeb, Maayainsane, LuckyFeathersGames, WolvesGamingDen, adamisbest1231, otistav, AccordingToBo, TheHugoDahl, CodeRushed, grassgrow, EndlessMoonfall, AntiViGames, nopogo_tv, smilesandtea, Clearest_Sky, tinybolt9889, falco_online, DrJavaSaurus, MsSaltyGiggles, poppybox, infinitepharaoh_, merkurrz, jawibae, EvilCanadian, cmjchrisjones, theLeprosyy, Alca, Schattenheld0u0, codephobia, Zuulmofo, Granah, Cj_crew, donaldwm, Jwh1o1, ThatNerdViolet, phrakberg, phoenixfirewingz, Aririal, BungalowGlow** | ||
@@ -16,1 +193,5 @@ | ||
**MacABearMan, Instafriend, LilyHazel, Instafluff, Gilokk0, ChatTranslator, That_MS_Gamer, fydo, wietlol, simrose4u, ArliGames, Dreadoosa, roberttables, donaldwm, sethorizer, csharpfritz, TheHugoDahl, ancientcoder, theMichaelJolley, mordzuber, mholloway24, Stelzi79, Thrennenne, AndresAmaris, sorskoot, Grid21, SvavaBlount, nallaj, itsDeke, pipskidoodle, DutchGamer46, violettepanda, sparky_pugwash, FuriousFur, MerlinLeWizard, Bjwhite211, bachner, DevMerlin, senatorcalder, julieee22, KitAnnLIVE, MatthewDGroves, BungalowGlow, smilesandtea, Undinen, Underground_Violet_Doggo, DEAD_P1XL, ShadowNeverSeen, LANiD, Kyoslilmonster, pookiepew, gekophetweb, Kevin_C_Melsop, FulltimeDreamer_, Ob_stealth1, KassidyKW18, Cold_Earth_, theluckyseven17, julian992, MinoGozzo, imjustafnagirl, twdjesuslover, phoenixfirewingz, the7goonies, quqco, Emily_is_PogChamp, silverpocket51, SleepyMia, thementalcobra, kev40k, MLGBlackbeard, apoketo, Tomcii, JonGood, Miffyasha, SIeepyMia, KageNoTsuma, dragonhyperking, TheHungerService, ExactlyMay, adamisbest1231, Alca, WolvesGamingDen, harugawada, Luxadin_, swolemaz, CorrelR, hug3fan14, Jwh1o1, CriticalKnit, malfunct, mofumoku, gamemodeon232, bscolaro, HologramDream, EnesOzdemirTV, lewdmelon, Xalent, Maayainsane, Lander03xD, rotaidar, Cloudhun, Rosuav, SoG_Cuicui, GlitterholicDreamz, fikapaus, shadowcraft5, TheJollyMercenaryArt, superandi23, holloway87, AllanJLA, SodarCZ, HeyOhKei, TheSkiDragon, DarrnyH, shinageeexpress, AP4TV, Chibigirl24** | ||
Thank you to all the friends who helped redesign ComfyDB into its v2 form! | ||
**TODO: Need to Get Chatters List from Instafluff Stream March 31, 2020** |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 3 instances in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
50022
1
9
0
197
2
504
4
- Removedcomfy-mongo@^1.0.7
- Removedcomfy-mongo@1.1.1(transitive)
- Removedtree-kill@1.2.2(transitive)
Updatedmongodb@^3.5.5