nedb-shelter
v2.0.10
Published
This library promisifies NeDB methods to make easier to work with front end and app databases using javascript
Downloads
75
Maintainers
Readme
NeDB Shelter
If you have any suggestion, you can use nedb-shelter's git hub repository to open an issue
This plugin aims to be an Promisified wrapper to NeDB adding some quality of life changes. The point of this lib is to make things even easier on front end side.
About NeDB
NeDB is an opensource javascript document database developed by Louis Chatriot. It was developed to be pretty damn similar to MongoDB in many aspects, like querying and deleting documents with the known MongoDB syntax.
However, as the operations runs we have to deal with responses manually, setting callbacks ourselves. This plugin aims to preserve the asynchronous behavior with promises
About this project
This was an abandoned project. As I needed to get back to work, dealing with this package was becoming hard. In the past I've made many "quality of life" functions that only have hidden the NeDB's api and to make my life easier. As I matured as a developer I've returned to this code and decided to make an simpler approach. Only wrap NeDB's functions with promises to make it more readable.
The objective of NeDB is to be MongoDB compliant at a simpler level to attend most of browser/front-end workflow. Keep in mind that this library break the MongoDB compatibility created in NeDB to offer a simpler API to use in the Front-End.
I've made many changes in the API, so I'm changing the active branch to 2.0.0 version.
In other words, it's just a wrapper with syntatic sugar
API
This API aims to follow the same NeDB's style.
- Installation
- Usage
- Creating database
- Defaults
- Setting new defaults
- Methods
- Insert
- Query
- Update
- Remove
Installation
npm install 'nedb-shelter' --save
Usage
You have can import or require
import database from 'nedb-shelter'
Creating database
This is a minimal boilerplate to create a browser/app database using NeDB Shelter
await database.createDatabase({ dbName: 'planets', timestampData: true, autoload: true })
If you set autoload
to true
you don't have to call js loadDatabase()
. Otherwise, you must load the database before in order to the queries work. All methods will be available after loading the database
Methods
Select
Select is a method that allows you to choose the database based on it's name. This choice was made to maintain some control over what is passing where in the scope of the project. For instance: I don't need to import the object referencing the database everywhere. I just import "nedb's shelter" and select the database from within and it's there. This allows you to use various databases. Remember to always select your database before querying
database.select('planets')
Inserting
Insert
Insert is straightforward. Select your database and send the object to create a new entry in the database.
With objects
let response = database.select('planets').insert( { planet: "Earth", satellites: ["Moon"], species: "humans" } )
console.log(response) // [{ planet: "Earth", satellites: ["Moon"], species: "humans" }]
With Arrays ( bulk insert ) You can bulk insert data using an array of objects
await database.select('planets').insert([
{ planet: "Jupiter", satellites: ["Deimos, Titan"], species: "vogons" },
{ planet: "Earth", satellites: ["Moon"], species: "humans" }
])
Querying
By default, an query without any object argument, will return all items inside the specified schema. The query section has many utilities that are, most of the time, only wrappers to make life easier. When you use .find
you can pass any modifier that is documented in NeDB's documentation and will just work.
Find
This method will return all entries that match the object sent. It accepts 3 values.
query
: This is the object you're filtering. If you're used to MongoDB, you know that every object that has that entry in it will be hit and returned
options
: This is an object for pagination, skipping and limiting a query. More info below
projection
: This will permit you to select the exact fields to return using an object notation
let planets = await database.select('planets').query({ inhabited: true })
// returns all entries that has inhabited true
console.log(planets)
Pagination, Limiting and Sorting
Pagination method works a little different from other methods. It needs a object to execute the query. It happens because NeDB's implementation of the methods above use a chain-like syntax. You can define every one of the four expected props: query
, skip
, limit
and sort
. Not defining one means that it will use a default value.
query
: Expects aobject
.Means the search terms used to query database. For instance{ planet: "Mars"}
. Default is query all items into databaseskip
: Expects anumber
. It will skip the results from first to last item. Defaults to 0, wich means it will NOT skip any resultslimit
: Expects anumber
. It will put a limit to the number of results from database. Defaults to 0, wich means it will NOT limit any resultssort
: Expects anumber
. It will order the results based on a field and a number. Can be reversed. Defaults to 1. Acceptable values are1
for ascending and-1
for descending
let queryOptions = {
skip: 1,
limit: 5,
sort: { planet: 1 } // 1 means natural order, using -1 means it will reverse the order
}
await database.select('planets').find({}, finder) // limited, skipped and sorted results
Projecting
You can project values, removing or keeping fields manually. This is useful when you need to hide data in someway:
await database.select('planets').find({}, null, { _id: 0, createdAt: 0, updatedAt: 0 })
// the returned object will NOT have _id, createdAt and updatedAt entries
Counting
Count
This method will return a Number
counting all objects that a schema already have
await database.select('planets').count() // returns is 7
Count from a query
You can filter the results for your count. Using a query, the result returning will be a count of the actual query results
await database.select('planets').count( { system: "solar" } )// returns 6 because one system is futurama
Updating
You can update your documents in a MongoDB fashion using NeDB shelter api. This method always will return an object with the fields: numAffected, affectedDocuments, upsert. Of course this is not the best usage if you're interested in performance. So you can pass a third parameter to the update method, wich is a object, setting affectedDocuments to false. For more information on update methods and operators, you can refer to NeDB
options
is an object with two possible parameters
multi
(defaults to false) which allows the modification of several documents if set to true
upsert
(defaults to false) if you want to insert a new document corresponding to the update rules if your query doesn't match anything. If your update is a simple object with no modifiers, it is the inserted document. In the other case, the query is stripped from all operator recursively, and the update is applied to it.
returnUpdatedDocs
(defaults to true ) if set to true and update is not an upsert, will return the array of documents matched by the find query and updated. Updated documents will be returned even if the update did not actually modify them.
await database.select('planets').update({ planet: "Earth" } , { $set: { planet: "Nova Earth" } })
You have to be cautious as the update method, when not using the $set
keyword will REPLACE your object entirely. This is pretty common in most databases, but I always try to alert to this behavior, as it is not intuitive
Example replacing an entry
await database.select('planets').update({ planet: "Earth" } , { planet: "Nova Earth" })
// this will swap the Earth object entirely, removing all the subfields as well, leaving only the specified object into the former place
You can set a subfield using dot notation
await database.select('planets').update( { planet: "Earth"}, { $set: { "position.horizontal": "650" }} )
Removing
Remove
Remove is pretty simple. Pass the query in the first argument and it removes the object from database for you
await database.select('planet').remove({ planet: "Earth" })
It is important to note that you can delete many objects with a single query, so, to prevent undesired deletions, I've put an lock into the method. To delete more than one object, you must pass the second argument multi as true. This will allow your query to delete more than one document
await database.select('planet').remove({ planet: "Earth" }, { multi: true })
If you need the object back after you delete it, you can pass the returnDeletedData
option in the third parameter. This is not native in NeDB library and if you want maximum performance, you should keep this off as it queries data before deleting it
await database.select('planet').remove({ planet: "Earth" }, { multi: true }, { returnDeletedData: true }) // returns what you have deleted
Droping databases
I've added two new Methods to the stack. One of them removes the database, the other flushes the data. Those are not compliant with MongoDB standard nor NeDB API. I've written those methods because, more than one time, I saw myself trying to flush data or deleting an entire entry into IndexedDB manually as NeDB doesn't offer this method:
Drop database
This will delete the database entirely. If you use this method, you can't reuse the old database to insert data. You will have to re-create the database. This works removing a row from IndexedDB thus making the entry unavailable.
await database.select('planets').drop()
Flushing collection
This will flush all data from an database. When you use this method, the database is still there, but all data is gone. Using this method, you can use the old database without having the need to re-create the database.
await database.select('planets').flush()
Indexing Data
Index
You can index data to make the database reads faster. Keep in mind that this will work for simple queries and queries using the modifiers $in
, $lt
, $lte
, $gt
and $gte
.
The _id
field is auto indexed, so you do not need to index this field unless you add this field manually.
From NeDB's page, those are the parameter list:
fieldName
(required): name of the field to index. Use the dot notation to index a field in a nested document.
unique
(optional, defaults to false): enforce field uniqueness. Note that a unique index will raise an error if you try to index two documents for which the field is not defined.
sparse
(optional, defaults to false): don't index documents for which the field is not defined. Use this option along with "unique" if you want to accept multiple documents for which it is not defined.
expireAfterSeconds
(number of seconds, optional): if set, the created index is a TTL (time to live) index, that will automatically remove documents when the system date becomes larger than the date on the indexed field plus expireAfterSeconds. Documents where the indexed field is not specified or not a Date object are ignored
Indexing an field and adding a unique constraint
await database.select('planets').index({ fieldName: 'planet', unique: true })
When you add the unique constriaint to the field, if you try to insert duplicate, it will raise an error and will not insert the data into the database. So use with caution
Acessing RAW NeDB Object
I've added an entry to the base object, so you can access NeDB directly without having to make magic. If you call database._nedb
you will access the raw NeDB instance. Keep in mind that, if you're using this too much, it is probably better for you to use the library itself
database._nedb // returns the actual nedb library
TO-DO list
I'm still working in tests for this package and analizing if it will need more methods. For now, the package is ready to use. But if you see a flaw or something I missed or have a sugestion as well, open an issue on github :)