Gathering detailed insights and metrics for forerunnerdb
Gathering detailed insights and metrics for forerunnerdb
Gathering detailed insights and metrics for forerunnerdb
Gathering detailed insights and metrics for forerunnerdb
forerunnerdb-core
ForerunnerDB's core module exported - see ForerunnerDB proper for more information.
@irrelon/forerunnerdb-core
ForerunnerDB core utilities for operating on JSON data.
@fohletex/forerunnerdb-debugger
A tiny library to quickly fetch and debug the database contents of ForerunnerDB
morefun-forerunnerdb
A NoSQL document store database for browsers and Node.js.
npm install forerunnerdb
Typescript
Module System
Min. Node Version
Node Version
NPM Version
60.9
Supply Chain
89.8
Quality
72.8
Maintenance
25
Vulnerability
98.6
License
JavaScript (78.02%)
HTML (19.2%)
SCSS (1.45%)
CSS (1.33%)
Less (0.01%)
Total Downloads
980,197
Last Day
348
Last Week
1,743
Last Month
4,684
Last Year
68,267
721 Stars
2,509 Commits
72 Forks
29 Watching
49 Branches
11 Contributors
Minified
Minified + Gzipped
Latest Version
2.0.24
Package Id
forerunnerdb@2.0.24
Unpacked Size
51.92 MB
Size
12.92 MB
File Count
650
NPM Version
8.1.2
Node Version
16.13.1
Publised On
28 Mar 2023
Cumulative downloads
Total Downloads
Last day
45.6%
348
Compared to previous day
Last week
-12.3%
1,743
Compared to previous week
Last month
215%
4,684
Compared to previous month
Last year
76.2%
68,267
Compared to previous year
Many of you use ForerunnerDB in your work and have given lots of feedback to me about getting some new features / functionality into the next version. You can see the active work on this endeavour over at https://github.com/Irrelon/forerunnerdb-core
ForerunnerDB 3.0 is being built in a completely modular fashion with extensibility at heart. Things like better persistent storage, exotic index support, different query language support etc are all going to be much simpler. It's nowhere near ready for prime time right now but if you feel like checking out the core functionality, head over to that repo above and check out what's there. With ❤ from Rob.
ForerunnerDB is developed with ❤ love by Irrelon Software Limited, a UK registered company.
ForerunnerDB is used in live projects that serve millions of users a day, is production ready and battle tested in real-world applications.
ForerunnerDB receives no funding or third-party backing except from patrons like yourself. If you love ForerunnerDB and want to support its development, or if you use it in your own products please consider becoming a patron: https://www.patreon.com/user?u=4443427
Community Support: https://github.com/Irrelon/ForerunnerDB/issues Commercial Support: forerunnerdb@irrelon.com
Master | Dev |
---|---|
ForerunnerDB is a NoSQL JavaScript JSON database with a query language based on MongoDB (with some differences) and runs on browsers and Node.js. It is in use in many large production web applications and is transparently used by over 6 million clients. ForerunnerDB is the most advanced, battle-tested and production ready browser-based JSON database system available today.
ForerunnerDB was created primarily to allow web (and mobile web / hybrid) application developers to easily store, query and manipulate JSON data in the browser / mobile app via a simple query language, making handling JSON data significantly easier.
ForerunnerDB supports data persistence on both the client (via LocalForage) and in Node.js (by saving and loading JSON data files).
If you build advanced web applications with AngularJS or perhaps your own framework or if you are looking to build a server application / API that needs a fast queryable in-memory store with file-based data persistence and a very easy setup (simple installation via NPM and no requirements except Node.js) you will also find ForerunnerDB very useful.
An example hybrid application that runs on iOS, Android and Windows Mobile via Ionic (AngularJS + Cordova with some nice extensions) is available in this repository under the ionicExampleClient folder. See here for more details.
If you are using Node.js (or have it installed) you can use NPM to download ForerunnerDB via:
1npm install forerunnerdb
You can also install the development version which usually includes new features that are considered either unstable or untested. To install the development version you can ask NPM for the dev tag:
1npm install forerunnerdb --tag dev
You can also install ForerunnerDB via the bower package manager:
1bower install forerunnerdb
If you are still a package manager hold-out or you would prefer a more traditional download, please click here.
fdb-all.min.js is the entire ForerunnerDB with all the added extras. If you prefer only the core database functionality (just collections, no views etc) you can use fdb-core.min.js instead. A list of the different builds is available for you to select the best build for your purposes.
Include the fdb-all.min.js file in your HTML (change path to the location you put forerunner):
1<script src="./js/dist/fdb-all.min.js" type="text/javascript"></script>
After installing via npm (see above) you can require ForerunnerDB in your code:
1var ForerunnerDB = require("forerunnerdb"); 2var fdb = new ForerunnerDB();
1var db = fdb.db("myDatabaseName");
If you do not specify a database name a randomly generated one is provided instead.
Data Binding: Enabled
To create or get a reference to a collection object, call db.collection (where collectionName is the name of your collection):
1var collection = db.collection("collectionName");
In our examples we will use a collection called "item" which will store some fictitious items for sale:
1var itemCollection = db.collection("item");
When you request a collection that does not yet exist it is automatically created. If it already exists you are given the reference to the existing collection. If you want ForerunnerDB to throw an error if a collection is requested that does not already exist you can pass an option to the collection() method instead:
1var collection = db.collection("collectionName", {autoCreate: false});
If no primary key is specified ForerunnerDB uses "_id" by default.
On requesting a collection you can specify a primary key that the collection should be using. For instance to use a property called "name" as the primary key field:
1var collection = db.collection("collectionName", {primaryKey: "name"});
You can also read or specify a primary key after instantiation via the primaryKey() method.
Occasionally it is useful to create a collection that will store a finite number of records. When that number is reached, any further documents inserted into the collection will cause the oldest inserted document to be removed from the collection on a first-in-first-out rule (FIFO).
In this example we create a capped collection with a document limit of 5:
1var collection = db.collection("collectionName", {capped: true, size: 5});
If you do not specify a value for the primary key, one will be automatically generated for any documents inserted into a collection. Auto-generated primary keys are pseudo-random 16 character strings.
PLEASE NOTE: When doing an insert into a collection, ForerunnerDB will automatically split the insert up into smaller chunks (usually of 100 documents) at a time to ensure the main processing thread remains unblocked. If you wish to be informed when the insert operation is complete you can pass a callback method to the insert call. Alternatively you can turn off this behaviour by calling yourCollection.deferredCalls(false);
You can either insert a single document object:
1itemCollection.insert({ 2 _id: 3, 3 price: 400, 4 name: "Fish Bones" 5});
or pass an array of documents:
1itemCollection.insert([{ 2 _id: 4, 3 price: 267, 4 name:"Scooby Snacks" 5}, { 6 _id: 5, 7 price: 234, 8 name: "Chicken Yum Yum" 9}]);
When inserting large amounts of documents ForerunnerDB may break your insert operation into multiple smaller operations (usually of 100 documents at a time) in order to avoid blocking the main processing thread of your browser / Node.js application. You can find out when an insert has completed either by passing a callback to the insert call or by switching off async behaviour.
Passing a callback:
1itemCollection.insert([{ 2 _id: 4, 3 price: 267, 4 name:"Scooby Snacks" 5}, { 6 _id: 5, 7 price: 234, 8 name: "Chicken Yum Yum" 9}], function (result) { 10 // The result object will contain two arrays (inserted and failed) 11 // which represent the documents that did get inserted and those 12 // that didn't for some reason (usually index violation). Failed 13 // items also contain a reason. Inspect the failed array for further 14 // information. 15});
If you wish to switch off async behaviour you can do so on a per-collection basis via:
1db.collection('myCollectionName').deferredCalls(false);
After async behaviour (deferred calls) has been disabled, you can insert records and be sure that they will all have inserted before the next statement is processed by the application's main thread.
JSON has limitations on the types of objects it will serialise and de-serialise back to an object. Two very good examples of this are the Date() and RegExp() objects. Both can be serialised via JSON.stringify() but when calling JSON.parse() on the serialised version neither type will be "re-materialised" back to their object representations.
For example:
1var a = { 2 dt: new Date() 3}; 4 5a.dt instanceof Date; // true 6 7var b = JSON.stringify(a); // "{"dt":"2016-02-11T09:52:49.170Z"}" 8 9var c = JSON.parse(b); // {dt: "2016-02-11T09:52:49.170Z"} 10 11c.dt instanceof Date; // false
As you can see, parsing the JSON string works but the dt key no longer contains a Date instance and only holds the string representation of the date. This is a fundamental drawback of using JSON.stringify() and JSON.parse() in their native form.
If you want ForerunnerDB to serialise / de-serialise your object instances you must use this format instead:
1var a = { 2 dt: fdb.make(new Date()) 3};
By wrapping the new Date() in fdb.make() we allow ForerunnerDB to provide the Date() object with a custom .toJSON() method that serialises it differently to the native implementation.
For convenience the make() method is also available on all ForerunnerDB class instances e.g. db, collection, view etc. For instance you can access make via:
1var fdb = new ForerunnerDB(), 2 db = fdb.db('test'), 3 coll = db.collection('testCollection'), 4 date = new Date(); 5 6// All of these calls will do the same thing: 7date = fdb.make(date); 8date = db.make(date); 9date = coll.make(date);
You can read more about how ForerunnerDB's serialiser works here.
1var a = { 2 dt: fdb.make(new Date()) 3};
1var a = { 2 re: fdb.make(new RegExp(".*", "i")) 3};
or
1var a = { 2 re: fdb.make(/.*/i)) 3};
ForerunnerDB's serialisation system allows for custom type handling so that you can expand JSON serialisation to your own custom class instances.
This can be a complex topic so it has been broken out into the Wiki section for further reading here.
PLEASE NOTE While we have tried to remain as close to MongoDB's query language as possible, small differences are present in the query matching logic. The main difference is described here: Find behaves differently from MongoDB
See the Special Considerations section for details about how names of keys / properties in a query object can affect a query's operation.
Much like MongoDB, searching for data in a collection is done using the find() method, which supports many of the same operators starting with a $ that MongoDB supports. For instance, finding documents in the collection where the price is greater than 90 but less than 150, would look like this:
1itemCollection.find({ 2 price: { 3 "$gt": 90, 4 "$lt": 150 5 } 6});
And would return an array with all matching documents. If no documents match your search, an empty array is returned.
Searches support regular expressions for advanced text-based queries. Simply pass the regular expression object as the value for the key you wish to search, just like when using regular expressions with MongoDB.
Insert a document:
1collection.insert([{ 2 "foo": "hello" 3}]);
Search by regular expression:
1collection.find({ 2 "foo": /el/ 3});
You can also use the RegExp object instead:
1var myRegExp = new RegExp("el"); 2 3collection.find({ 4 "foo": myRegExp 5});
ForerunnerDB supports many of the same query operators that MongoDB does, and adds some that are not available in MongoDB but which can help in browser-centric applications.
Selects those documents where the value of the field is greater than (i.e. >) the specified value.
1{ field: {$gt: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $gt: 1 19 } 20});
Result is:
1[{ 2 _id: 2, 3 val: 2 4}, { 5 _id: 3, 6 val: 3 7}]
Selects the documents where the value of the field is greater than or equal to (i.e. >=) the specified value.
1{ field: {$gte: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $gte: 1 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 2, 6 val: 2 7}, { 8 _id: 3, 9 val: 3 10}]
Selects the documents where the value of the field is less than (i.e. <) the specified value.
1{ field: { $lt: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $lt: 2 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}]
Selects the documents where the value of the field is less than or equal to (i.e. <=) the specified value.
1{ field: { $lte: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $lte: 2 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 2, 6 val: 2 7}] 8 ``` 9 10#### $eq 11Selects the documents where the value of the field is equal (i.e. ==) to the specified value. 12 13```js 14{field: {$eq: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $eq: 2 19 } 20});
Result is:
1[{ 2 _id: 2, 3 val: 2 4}]
Selects the documents where the value of the field is strict equal (i.e. ===) to the specified value. This allows for strict equality checks for instance zero will not be seen as false because 0 !== false and comparing a string with a number of the same value will also return false e.g. ('2' == 2) is true but ('2' === 2) is false.
1{field: {$eeq: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: "2" 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: "2" 14}]); 15 16result = coll.find({ 17 val: { 18 $eeq: 2 19 } 20});
Result is:
1[{ 2 _id: 2, 3 val: 2 4}]
Selects the documents where the value of the field is not equal (i.e. !=) to the specified value. This includes documents that do not contain the field.
1{field: {$ne: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $ne: 2 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 3, 6 val: 3 7}]
Selects the documents where the value of the field is not equal equal (i.e. !==) to the specified value. This allows for strict equality checks for instance zero will not be seen as false because 0 !== false and comparing a string with a number of the same value will also return false e.g. ('2' != 2) is false but ('2' !== 2) is true. This includes documents that do not contain the field.
1{field: {$nee: value} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $nee: 2 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 3, 6 val: 3 7}]
Selects the documents where the result of the query inside the $not operator do not match the query object.
1{$not: query}
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert({ 6 _id: 1, 7 name: 'John Doe', 8 group: [{ 9 name: 'groupOne' 10 }, { 11 name: 'groupTwo' 12 }] 13}); 14 15coll.insert({ 16 _id: 2, 17 name: 'Jane Doe', 18 group: [{ 19 name: 'groupTwo'} 20 ] 21}); 22 23result = coll.find({ 24 $not: { 25 group: { 26 name: 'groupOne' 27 } 28 } 29});
Result is:
1[{ 2 _id: 2, 3 name: 'Jane Doe', 4 group: [{ 5 name: 'groupTwo'} 6 ] 7}]
If your field is a string or number and your array of values are also either strings or numbers you can utilise $fastIn which is an optimised $in query that uses indexOf() to identify matching values instead of looping over all items in the array of values and running a new matching process against each one. If your array of values include sub-queries or other complex logic you should use $in, not $fastIn.
Selects documents where the value of a field equals any value in the specified array.
1{ field: { $in: [<value1>, <value2>, ... <valueN> ] } }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $in: [1, 3] 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 3, 6 val: 3 7}]
You can use $fastIn instead of $in when your field contains a string or number and your array of values contains only strings or numbers. $fastIn utilises indexOf() to speed up performance of the query. This means that the array of values is not evaluated for sub-queries, other operators like $gt etc, and it is assumed that the array of values is a completely flat array, filled only with strings or numbers.
Selects documents where the string or number value of a field equals any string or number value in the specified array.
The array of values MUST be a flat array and contain only strings or numbers.
1{ field: { $fastIn: [<value1>, <value2>, ... <valueN> ] } }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $fastIn: [1, 3] 19 } 20});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 3, 6 val: 3 7}]
Selects documents where the value of a field does not equal any value in the specified array.
1{ field: { $nin: [ <value1>, <value2> ... <valueN> ]} }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 val: { 18 $nin: [1, 3] 19 } 20});
Result is:
1[{ 2 _id: 2, 3 val: 2 4}]
Selects the first document matching a value of the specified field. If any further documents have the same value for the specified field they will not be returned.
1{ $distinct: { field: 1 } }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 1 11}, { 12 _id: 3, 13 val: 1 14}, { 15 _id: 4, 16 val: 2 17}]); 18 19result = coll.find({ 20 $distinct: { 21 val: 1 22 } 23});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 4, 6 val: 2 7}]
Version >= 1.3.326
This is equivalent to MongoDB's $size operator but please see below for usage.
Selects documents based on the length (count) of items in an array inside a document.
1{ $count: { field: <value> } }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 arr: [] 8}, { 9 _id: 2, 10 arr: [{ 11 val: 1 12 }] 13}, { 14 _id: 3, 15 arr: [{ 16 val: 1 17 }, { 18 val: 2 19 }] 20}]); 21 22result = coll.find({ 23 $count: { 24 arr: 1 25 } 26});
Result is:
1[{ 2 _id: 2, 3 arr: [{ 4 val: 1 5 }] 6}]
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 arr: [] 8}, { 9 _id: 2, 10 arr: [{ 11 val: 1 12 }] 13}, { 14 _id: 3, 15 arr: [{ 16 val: 1 17 }, { 18 val: 2 19 }] 20}]); 21 22result = coll.find({ 23 $count: { 24 arr: { 25 $gt: 1 26 } 27 } 28});
Result is:
1[{ 2 _id: 3, 3 arr: [{ 4 val: 1 5 }, { 6 val: 2 7 }] 8}]
The $or operator performs a logical OR operation on an array of two or more
1{ $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 $or: [{ 18 val: 1 19 }, { 20 val: { 21 $gte: 3 22 } 23 }] 24});
Result is:
1[{ 2 _id: 1, 3 val: 1 4}, { 5 _id: 3, 6 val: 3 7}]
Performs a logical AND operation on an array of two or more expressions (e.g.
1{ $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({ 17 $and: [{ 18 _id: 3 19 }, { 20 val: { 21 $gte: 3 22 } 23 }] 24});
Result is:
1[{ 2 _id: 3, 3 val: 3 4}]
When
1{ field: { $exists: <boolean> } }
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2, 11 moo: "hello" 12}, { 13 _id: 3, 14 val: 3 15}]); 16 17result = coll.find({ 18 moo: { 19 $exists: true 20 } 21});
Result is:
1[{ 2 _id: 2, 3 val: 2, 4 moo: "hello" 5}]
The $elemMatch operator limits the contents of an array field from the query results to contain only the first element matching the $elemMatch condition.
The $elemMatch operator is specified in the options object of the find call rather than the query object.
MongoDB $elemMatch Documentation
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert({ 6 names: [{ 7 _id: 1, 8 text: "Jim" 9 }, { 10 _id: 2, 11 text: "Bob" 12 }, { 13 _id: 3, 14 text: "Bob" 15 }, { 16 _id: 4, 17 text: "Anne" 18 }, { 19 _id: 5, 20 text: "Simon" 21 }, { 22 _id: 6, 23 text: "Uber" 24 }] 25}); 26 27result = coll.find({}, { 28 $elemMatch: { 29 names: { 30 text: "Bob" 31 } 32 } 33});
Result is:
1{ 2 names: [{ 3 _id: 2, 4 text: "Bob" 5 }] 6}
Notice that only the FIRST item matching the $elemMatch clause is returned in the names array. If you require multiple matches use the ForerunnerDB-specific $elemsMatch operator instead.
The $elemsMatch operator limits the contents of an array field from the query results to contain only the elements matching the $elemMatch condition.
The $elemsMatch operator is specified in the options object of the find call rather than the query object.
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert({ 6 names: [{ 7 _id: 1, 8 text: "Jim" 9 }, { 10 _id: 2, 11 text: "Bob" 12 }, { 13 _id: 3, 14 text: "Bob" 15 }, { 16 _id: 4, 17 text: "Anne" 18 }, { 19 _id: 5, 20 text: "Simon" 21 }, { 22 _id: 6, 23 text: "Uber" 24 }] 25}); 26 27result = coll.find({}, { 28 $elemsMatch: { 29 names: { 30 text: "Bob" 31 } 32 } 33});
Result is:
1{ 2 names: [{ 3 _id: 2, 4 text: "Bob" 5 }, { 6 _id: 3, 7 text: "Bob" 8 }] 9}
Notice that all items matching the $elemsMatch clause are returned in the names array. If you require match on ONLY the first item use the MongoDB-compliant $elemMatch operator instead.
Coverts an array of documents into an array of values that are derived from a key or path in the documents. This is very useful when combined with the $find operator to run sub-queries and return arrays of values from the results.
1{ $aggregate: path}
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 val: 1 8}, { 9 _id: 2, 10 val: 2 11}, { 12 _id: 3, 13 val: 3 14}]); 15 16result = coll.find({}, { 17 $aggregate: "val" 18});
Result is:
1[1, 2, 3]
PLEASE NOTE: BETA STATUS - PASSES UNIT TESTING BUT MAY BE UNSTABLE
Finds other documents whose co-ordinates based on a 2d index are within the specified distance from the specified centre point. Co-ordinates must be presented in latitude / longitude for $near to work.
1{ 2 field: { 3 $near: { 4 $point: [<latitude number>, <longitude number>], 5 $maxDistance: <number>, 6 $distanceUnits: <units string> 7 } 8 } 9}
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 latLng: [51.50722, -0.12750], 7 name: 'Central London' 8}, { 9 latLng: [51.525745, -0.167550], // 2.18 miles 10 name: 'Marylebone, London' 11}, { 12 latLng: [51.576981, -0.335091], // 10.54 miles 13 name: 'Harrow, London' 14}, { 15 latLng: [51.769451, 0.086509], // 20.33 miles 16 name: 'Harlow, Essex' 17}]); 18 19// Create a 2d index on the lngLat field 20coll.ensureIndex({ 21 latLng: 1 22}, { 23 type: '2d' 24}); 25 26// Query index by distance 27// $near queries are sorted by distance from centre point by default 28result = coll.find({ 29 latLng: { 30 $near: { 31 $point: [51.50722, -0.12750], 32 $maxDistance: 3, 33 $distanceUnits: 'miles' 34 } 35 } 36});
Result is:
1[{ 2 "lngLat": [51.50722, -0.1275], 3 "name": "Central London", 4 "_id": "1f56c0b5885de40" 5}, { 6 "lngLat": [51.525745, -0.16755], 7 "name": "Marylebone, London", 8 "_id": "372a34d9f17fbe0" 9}]
You can specify an $orderBy option along with the find call to order/sort your results. This uses the same syntax as MongoDB:
1itemCollection.find({ 2 price: { 3 "$gt": 90, 4 "$lt": 150 5 } 6}, { 7 $orderBy: { 8 price: 1 // Sort ascending or -1 for descending 9 } 10});
Version >= 1.3.757
You can specify a $groupBy option along with the find call to group your results:
1myColl = db.collection('myColl'); 2 3myColl.insert([{ 4 "price": "100", 5 "category": "dogFood" 6}, { 7 "price": "60", 8 "category": "catFood" 9}, { 10 "price": "70", 11 "category": "catFood" 12}, { 13 "price": "65", 14 "category": "catFood" 15}, { 16 "price": "35", 17 "category": "dogFood" 18}]); 19 20myColl.find({}, { 21 $groupBy: { 22 "category": 1 // Group using the "category" field. Path's are also allowed e.g. "category.name" 23 } 24});
Result is:
1{ 2 "dogFood": [{ 3 "price": "100", 4 "category": "dogFood" 5 }, { 6 "price": "35", 7 "category": "dogFood" 8 }], 9 "catFood": [{ 10 "price": "60", 11 "category": "catFood" 12 }, { 13 "price": "70", 14 "category": "catFood" 15 }, { 16 "price": "65", 17 "category": "catFood" 18 }], 19}
You can specify which fields are included in the return data for a query by adding them in the options object. This returns a partial document for each matching document in your query.
This follows the same rules specified by MongoDB here:
Please note that the primary key field will always be returned unless explicitly excluded from the results via "_id: 0".
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"); 4 5coll.insert([{ 6 _id: 1, 7 text: "Jim", 8 val: 2131232, 9 arr: [ 10 "foo", 11 "bar", 12 "you" 13 ] 14}]);
Now query for only the "text" field of each document:
1result = coll.find({}, { 2 text: 1 3});
Result is:
1[{ 2 _id: 1, 3 text: "Jim" 4}]
Notice the _id field is ALWAYS included in the results unless you explicitly exclude it:
1result = coll.find({}, { 2 _id: 0, 3 text: 1 4});
Result is:
1[{ 2 text: "Jim" 3}]
Version >= 1.3.55
It is often useful to limit the number of results and then page through the results one page at a time. ForerunnerDB supports an easy pagination system via the $page and $limit query options combination.
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test"), 4 data = [], 5 count = 100, 6 result, 7 i; 8 9// Generate random data 10for (i = 0; i < count; i++) { 11 data.push({ 12 _id: String(i), 13 val: i 14 }); 15} 16 17coll.insert(data); 18 19// Query the first 10 records (page indexes are zero-based 20// so the first page is page 0 not page 1) 21result = coll.find({}, { 22 $page: 0, 23 $limit: 10 24}); 25 26// Query the next 10 records 27result = coll.find({}, { 28 $page: 1, 29 $limit: 10 30});
Version >= 1.3.55
You can skip records at the beginning of a query result by providing the $skip query option. This operates in a similar fashion to the MongoDB skip() method.
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test").truncate(), 4 data = [], 5 count = 100, 6 result, 7 i; 8 9// Generate random data 10for (i = 0; i < count; i++) { 11 data.push({ 12 _id: String(i), 13 val: i 14 }); 15} 16 17coll.insert(data); 18result = coll.find({}, { 19 $skip: 50 20});
When you have documents that contain arrays of sub-documents it can be useful to search and extract them. Consider this data structure:
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 coll = db.collection("test").truncate(), 4 result, 5 i; 6 7coll.insert({ 8 _id: "1", 9 arr: [{ 10 _id: "332", 11 val: 20, 12 on: true 13 }, { 14 _id: "337", 15 val: 15, 16 on: false 17 }] 18}); 19 20/** 21 * Finds sub-documents from the collection's documents. 22 * @param {Object} match The query object to use when matching parent documents 23 * from which the sub-documents are queried. 24 * @param {String} path The path string used to identify the key in which 25 * sub-documents are stored in parent documents. 26 * @param {Object=} subDocQuery The query to use when matching which sub-documents 27 * to return. 28 * @param {Object=} subDocOptions The options object to use when querying for 29 * sub-documents. 30 * @returns {*} 31 */ 32result = coll.findSub({ 33 _id: "1" 34}, "arr", { 35 on: false 36}, { 37 //$stats: true, 38 //$split: true 39});
The result of this query is an array containing the sub-documents that matched the query parameters:
1[{ 2 _id: "337", 3 val: 15, 4 on: false 5}]
The result of findSub never returns a parent document's data, only data from the matching sub-document(s)
The fourth parameter (options object) allows you to specify if you wish to have stats and if you wish to split your results into separate arrays for each matching parent document.
Version >= 1.3.469
Subqueries are ForerunnerDB specific and do not work in MongoDB
A subquery is a query object within another query object.
Subqueries are useful when the query you wish to run is reliant on data inside another collection or view and you do not want to run a separate query first to retrieve that data.
Subqueries in ForerunnerDB are specified using the $find operator inside your query.
Take the following example data:
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 users = db.collection("users"), 4 admins = db.collection("admins"); 5 6users.insert([{ 7 _id: 1, 8 name: "Jim" 9}, { 10 _id: 2, 11 name: "Bob" 12}, { 13 _id: 3, 14 name: "Bob" 15}, { 16 _id: 4, 17 name: "Anne" 18}, { 19 _id: 5, 20 name: "Simon" 21}]); 22 23admins.insert([{ 24 _id: 2, 25 enabled: true 26}, { 27 _id: 4, 28 enabled: true 29}, { 30 _id: 5, 31 enabled: false 32}]); 33 34result = users.find({ 35 _id: { 36 $in: { 37 $find: { 38 $from: "admins", 39 $query: { 40 enabled: true 41 }, 42 $options: { 43 $aggregate: "_id" 44 } 45 } 46 } 47 } 48});
When this query is executed the $find sub-query object is replaced with the results from the sub-query so that the final query with (aggregated)[#$aggregate] _id field looks like this:
1result = users.find({ 2 _id: { 3 $in: [3, 4] 4 } 5});
The result of the query after execution is:
1[{ 2 "_id": 3, 3 "name": "Bob" 4}, { 5 "_id": 4, 6 "name": "Anne" 7}]
This is one of the areas where ForerunnerDB and MongoDB are different. By default ForerunnerDB updates only the keys you specify in your update document, rather than outright replacing the matching documents like MongoDB does. In this sense ForerunnerDB behaves more like MySQL. In the call below, the update will find all documents where the price is greater than 90 and less than 150 and then update the documents' key "moo" with the value true.
1collection.update({ 2 price: { 3 "$gt": 90, 4 "$lt": 150 5 } 6}, { 7 moo: true 8});
If you wish to fully replace a document with another one you can do so using the $replace operator described in the Update Operators section below.
If you want to replace a key's value you can use the $overwrite operator described in the Update Operators section below.
You can target individual documents for update by their id (primary key) via a quick helper method:
1collection.updateById(1, {price: 180});
This will update the document with the _id field of 1 to a new price of 180.
Adds an item into an array only if the item does not already exist in the array.
ForerunnerDB supports the $addToSet operator as detailed in the MongoDB documentation. Unlike MongoDB, ForerunnerDB also allows you to specify a matching field / path to check uniqueness against by using the $key property.
In the following example $addToSet is used to check uniqueness against the whole document being added:
1// Create a collection document 2db.collection("test").insert({ 3 _id: "1", 4 arr: [] 5}); 6 7// Update the document by adding an object to the "arr" array 8db.collection("test").update({ 9 _id: "1" 10}, { 11 $addToSet: { 12 arr: { 13 name: "Fufu", 14 test: "1" 15 } 16 } 17}); 18 19// Try and do it again... this will fail because a 20// matching item already exists in the array 21db.collection("test").update({ 22 _id: "1" 23}, { 24 $addToSet: { 25 arr: { 26 name: "Fufu", 27 test: "1" 28 } 29 } 30});
Now in the example below we specify which key to test uniqueness against:
1// Create a collection document 2db.collection("test").insert({ 3 _id: "1", 4 arr: [] 5}); 6 7// Update the document by adding an object to the "arr" array 8db.collection("test").update({ 9 _id: "1" 10}, { 11 $addToSet: { 12 arr: { 13 name: "Fufu", 14 test: "1" 15 } 16 } 17}); 18 19// Try and do it again... this will work because the 20// key "test" is different for the existing and new objects 21db.collection("test").update({ 22 _id: "1" 23}, { 24 $addToSet: { 25 arr: { 26 $key: "test", 27 name: "Fufu", 28 test: "2" 29 } 30 } 31});
You can also specify the key to check uniqueness against as an object path such as 'moo.foo'.
Version >= 1.3.34
The $cast operator allows you to change a property's type within a document. If used to cast a property to an array or object the property is set to a new blank array or object respectively.
This example changes the type of the "val" property from a string to a number:
1db.collection("test").insert({ 2 val: "1.2" 3}); 4 5db.collection("test").update({}, { 6 $cast: { 7 val: "number" 8 } 9}); 10 11JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "1d6fbf16e080de0", 3 "val": 1.2 4}]
You can also use cast to ensure that an array or object exists on a property without overwriting that property if one already exists:
1db.collection("test").insert({ 2 _id: "moo", 3 arr: [{ 4 test: true 5 }] 6}); 7 8db.collection("test").update({ 9 _id: "moo" 10}, { 11 $cast: { 12 arr: "array" 13 } 14}); 15 16JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "moo", 3 "arr": [{ 4 "test": true 5 }] 6}]
Should you wish to initialise an array or object with specific data if the property is not currently of that type rather than initialising as a blank array / object, you can specify the data to use by including a $data property in your $cast operator object:
1db.collection("test").insert({ 2 _id: "moo" 3}); 4 5db.collection("test").update({ 6 _id: "moo" 7}, { 8 $cast: { 9 orders: "array", 10 $data: [{ 11 initial: true 12 }] 13 } 14}); 15 16JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "moo", 3 "orders":[{ 4 "initial": true 5 }] 6}]
Version >= 1.3.34
$each allows you to iterate through multiple update operations on the same query result. Use $each when you wish to execute update operations in sequence or on the same query. Using $each is slightly more performant than running each update operation one after the other calling update().
Consider the following sequence of update calls that define a couple of nested arrays and then push a value to the inner-nested array:
1db.collection("test").insert({ 2 _id: "445324", 3 count: 5 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $cast: { 10 arr: "array", 11 $data: [{}] 12 } 13}); 14 15db.collection("test").update({ 16 _id: "445324" 17}, { 18 arr: { 19 $cast: { 20 secondArr: "array" 21 } 22 } 23}); 24 25db.collection("test").update({ 26 _id: "445324" 27}, { 28 arr: { 29 $push: { 30 secondArr: "moo" 31 } 32 } 33}); 34 35JSON.stringify(db.collection("test").find());
Result:
1[ 2 { 3 "_id": "445324", 4 "count": 5, 5 "arr": [{"secondArr": ["moo"]}] 6 } 7]
These calls a wasteful because each update() call must query the collection for matching documents before running the update against them. With $each you can pass a sequence of update operations and they will be executed in order:
1db.collection("test").insert({ 2 _id: "445324", 3 count: 5 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $each: [{ 10 $cast: { 11 arr: "array", 12 $data: [{}] 13 } 14 }, { 15 arr: { 16 $cast: { 17 secondArr: "array" 18 } 19 } 20 }, { 21 arr: { 22 $push: { 23 secondArr: "moo" 24 } 25 } 26 }] 27}); 28 29JSON.stringify(db.collection("test").find());
Result:
1[ 2 { 3 "_id": "445324", 4 "count": 5, 5 "arr": [{"secondArr": ["moo"]}] 6 } 7]
As you can see the single sequenced call produces the same output as the multiple update() calls but will run slightly faster and use fewer resources.
The $inc operator increments / decrements a field value by the given number.
1db.collection("test").update({ 2 <query> 3}, { 4 $inc: { 5 <field>: <value> 6 } 7});
In the following example, the "count" field is decremented by 1 in the document that matches the id "445324":
1db.collection("test").insert({ 2 _id: "445324", 3 count: 5 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $inc: { 10 count: -1 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324", 3 "count": 4 4}]
Using a positive number will increment, using a negative number will decrement.
The $move operator moves an item that exists inside a document's array from one index to another.
1db.collection("test").update({ 2 <query> 3}, { 4 $move: { 5 <arrayField>: <value|query>, 6 $index: <index> 7 } 8});
The following example moves "Milk" in the "shoppingList" array to index 1 in the document with the id "23231":
1db.users.update({ 2 _id: "23231" 3}, { 4 $move: { 5 shoppingList: "Milk" 6 $index: 1 7 } 8});
The $mul operator multiplies a field value by the given number and sets the result as the field's new value.
1db.collection("test").update({ 2 <query> 3}, { 4 $mul: { 5 <field>: <value> 6 } 7});
In the following example, the "value" field is multiplied by 2 in the document that matches the id "445324":
1db.collection("test").insert({ 2 _id: "445324", 3 value: 5 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $mul: { 10 value: 2 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324", 3 "value": 10 4}]
The $overwrite operator replaces a key's value with the one passed, overwriting it completely. This operates the same way that MongoDB's default update behaviour works without using the $set operator.
If you wish to fully replace a document with another one you can do so using the $replace operator instead.
The $overwrite operator is most useful when updating an array field to a new type such as an object. By default ForerunnerDB will detect an array and step into the array objects one at a time and apply the update to each object. When you use $overwrite you can replace the array instead of stepping into it.
1db.collection("test").update({ 2 <query> 3}, { 4 $overwrite: { 5 <field>: <value>, 6 <field>: <value>, 7 <field>: <value> 8 } 9});
In the following example the "arr" field (initially an array) is replaced by an object:
1db.collection("test").insert({ 2 _id: "445324", 3 arr: [{ 4 foo: 1 5 }] 6}); 7 8db.collection("test").update({ 9 _id: "445324" 10}, { 11 $overwrite: { 12 arr: { 13 moo: 1 14 } 15 } 16}); 17 18JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324", 3 "arr": { 4 "moo": 1 5 } 6}]
The $push operator appends a specified value to an array.
1db.collection("test").update({ 2 <query> 3}, { 4 $push: { 5 <field>: <value> 6 } 7});
The following example appends "Milk" to the "shoppingList" array in the document with the id "23231":
1db.collection("test").insert({ 2 _id: "23231", 3 shoppingList: [] 4}); 5 6db.collection("test").update({ 7 _id: "23231" 8}, { 9 $push: { 10 shoppingList: "Milk" 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "23231", 3 "shoppingList": [ 4 "Milk" 5 ] 6}]
The $pull operator removes a specified value or values that match an input query.
1db.collection("test").update({ 2 <query> 3}, { 4 $pull: { 5 <arrayField>: <value|query> 6 } 7});
The following example removes the "Milk" entry from the "shoppingList" array:
1db.users.update({ 2 _id: "23231" 3}, { 4 $pull: { 5 shoppingList: "Milk" 6 } 7});
If an array element is an embedded document (JavaScript object), the $pull operator applies its specified query to the element as though it were a top-level object.
The $pullAll operator removes all values / array entries that match an input query from the target array field.
1db.collection("test").update({ 2 <query> 3}, { 4 $pullAll: { 5 <arrayField>: <value|query> 6 } 7});
The following example removes all instances of "Milk" and "Toast from the "items" array:
1db.users.update({ 2 _id: "23231" 3}, { 4 $pullAll: { 5 items: ["Milk", "Toast"] 6 } 7});
If an array element is an embedded document (JavaScript object), the $pullAll operator applies its specified query to the element as though it were a top-level object.
The $pop operator removes an element from an array at the beginning or end. If you wish to remove an element from the end of the array pass 1 in your value. If you wish to remove an element from the beginning of an array pass -1 in your value.
1db.collection("test").update({ 2 <query> 3}, { 4 $pop: { 5 <field>: <value> 6 } 7});
The following example pops the item from the beginning of the "shoppingList" array:
1db.collection("test").insert({ 2 _id: "23231", 3 shoppingList: [{ 4 _id: 1, 5 name: "One" 6 }, { 7 _id: 2, 8 name: "Two" 9 }, { 10 _id: 3, 11 name: "Three" 12 }] 13}); 14 15db.collection("test").update({ 16 _id: "23231" 17}, { 18 $pop: { 19 shoppingList: -1 // -1 pops from the beginning, 1 pops from the end 20 } 21}); 22 23JSON.stringify(db.collection("test").find());
Result:
1[{ 2 _id: "23231", 3 shoppingList: [{ 4 _id: 2, 5 name: "Two" 6 }, { 7 _id: 3, 8 name: "Three" 9 }] 10}]
Renames a field in any documents that match the query with a new name.
1db.collection("test").update({ 2 <query> 3}, { 4 $rename: { 5 <field>: <newName> 6 } 7});
The following example renames the "action" field to "jelly":
1db.collection("test").insert({ 2 _id: "23231", 3 action: "Foo" 4}); 5 6db.collection("test").update({ 7 _id: "23231" 8}, { 9 $rename: { 10 action: "jelly" 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 _id: "23231", 3 jelly: "Foo" 4 }]
PLEASE NOTE: $replace can only be used on the top-level. Nested $replace operators are not currently supported and may cause unexpected behaviour.
The $replace operator will take the passed object and overwrite the target document with the object's keys and values. If a key exists in the existing document but not in the passed object, ForerunnerDB will remove the key from the document.
The $replace operator is equivalent to calling MongoDB's update without using a MongoDB $set operator.
When using $replace the primary key field will NEVER be replaced even if it is specified. If you wish to change a record's primary key id, remove the document and insert a new one with your desired id.
1db.collection("test").update({ 2 <query> 3}, { 4 $replace: { 5 <field>: <value>, 6 <field>: <value>, 7 <field>: <value> 8 } 9});
In the following example the existing document is outright replaced by a new one:
1db.collection("test").insert({ 2 _id: "445324", 3 name: "Jill", 4 age: 15 5}); 6 7db.collection("test").update({ 8 _id: "445324" 9}, { 10 $replace: { 11 job: "Frog Catcher" 12 } 13}); 14 15JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324", 3 "job": "Frog Catcher" 4}]
The $splicePush operator adds an item into an array at a specified index.
1db.collection("test").update({ 2 <query> 3}, { 4 $splicePush: { 5 <field>: <value> 6 $index: <index> 7 } 8});
The following example inserts "Milk" to the "shoppingList" array at index 1 in the document with the id "23231":
1db.collection("test").insert({ 2 _id: "23231", 3 shoppingList: [ 4 "Sugar", 5 "Tea", 6 "Coffee" 7 ] 8}); 9 10db.collection("test").update({ 11 _id: "23231" 12}, { 13 $splicePush: { 14 shoppingList: "Milk", 15 $index: 1 16 } 17}); 18 19JSON.stringify(db.collection("test").find());
Result:
1[ 2 { 3 "_id": "23231", 4 "shoppingList": [ 5 "Sugar", 6 "Milk", 7 "Tea", 8 "Coffee" 9 ] 10 } 11]
The $splicePull operator removes an item (or items) from an array at a specified index. If you specify a $count operator the splicePull operation will remove from the $index to the number of items you specify. $count defaults to 1 if it is not specified.
1db.collection("test").update({ 2 <query> 3}, { 4 $splicePull: { 5 <field>: { 6 $index: <index>, 7 $count: <integer> 8 } 9 } 10});
The following example inserts "Milk" to the "shoppingList" array at index 1 in the document with the id "23231":
1db.collection("test").insert({ 2 _id: "23231", 3 shoppingList: [ 4 "Sugar", 5 "Tea", 6 "Coffee" 7 ] 8}); 9 10db.collection("test").update({ 11 _id: "23231" 12}, { 13 $splicePull: { 14 shoppingList: { 15 $index: 1 16 } 17 } 18}); 19 20JSON.stringify(db.collection("test").find());
Result:
1[ 2 { 3 "_id": "23231", 4 "shoppingList": [ 5 "Sugar", 6 "Milk", 7 "Tea", 8 "Coffee" 9 ] 10 } 11]
The $toggle operator inverts the value of a field with a boolean. If the value is true before toggling, after toggling it will be false and vice versa.
1db.collection("test").update({ 2 <query> 3}, { 4 $toggle: { 5 <field>: 1 6 } 7});
In the following example, the "running" field is toggled from true to false:
1db.collection("test").insert({ 2 _id: "445324", 3 running: true 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $toggle: { 10 running: 1 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324", 3 "running": false 4}]
The $unset operator removes a field from a document.
1db.collection("test").update({ 2 <query> 3}, { 4 $unset: { 5 <field>: 1 6 } 7});
In the following example, the "count" field is remove from the document that matches the id "445324":
1db.collection("test").insert({ 2 _id: "445324", 3 count: 5 4}); 5 6db.collection("test").update({ 7 _id: "445324" 8}, { 9 $unset: { 10 count: 1 11 } 12}); 13 14JSON.stringify(db.collection("test").find());
Result:
1[{ 2 "_id": "445324" 3}]
Often you want to update a sub-document stored inside an array. You can use the array positional operator to tell ForerunnerDB that you wish to update a sub-document that matches your query clause.
The following example updates the sub-document in the array "arr" with the _id "foo" so that the "name" property is set to "John":
1db.collection("test").insert({ 2 _id: "2", 3 arr: [{ 4 _id: "foo", 5 name: "Jim" 6 }] 7}); 8 9var result = db.collection("test").update({ 10 _id: "2", 11 "arr": { 12 "_id": "foo" 13 } 14}, { 15 "arr.$": { 16 name: "John" 17 } 18});
Internally this operation checks the update for property's ending in ".$" and then looks at the query part of the call to see if a corresponding clause exists for it. In the example above the "arr.$" property in the update part has a corresponding "arr" in the query part which determines which sub-documents are to be updated based on if they match or not.
Upserts are operations that automatically decide if the database should run an insert or an update operation based on the data you provide.
Using upsert() is effectively the same as using insert(). You pass an object or array of objects to the upsert() method and they are processed.
1// This will execute an insert operation because a document with the _id "1" does not 2// currently exist in the database. 3db.collection("test").upsert({ 4 "_id": "1", 5 "test": true 6}); 7 8db.collection("test").find(); // [{"_id": "1", "test": true}] 9 10// We now perform an upsert and change "test" to false. This will perform an update operation 11// since a document with the _id "1" now exists. 12db.collection("test").upsert({ 13 "_id": "1", 14 "test": false 15}); 16 17db.collection("test").find(); // [{"_id": "1", "test": false}]
One of the restrictions of upsert() is that you cannot use any update operators in your document because the operation could be an insert. For this reason, upserts should only contain data and no $ operators like $push, $unset etc.
An upsert operation both returns an array of results and accepts a callback that will receive the same array data on what operations were done for each document passed, as well as the result of that operation. See the [https://forerunnerdb.com/source/doc/Collection.html#upsert](upsert documentation) for more details.
The count() method is useful when you want to get a count of the number of documents in a collection or a count of documents that match a specified query.
1// Cound all documents in the "test" collection 2var num = db.collection("test").count();
1// Get all documents whos myField property has the value of 1 2var num = db.collection("test").count({ 3 myField: 1 4});
JavaScript objects are passed around as references to the same object. By default when you query ForerunnerDB it will "decouple" the results from the internal objects stored in the collection. If you would prefer to get the reference instead of decoupled object you can specify this in the query options like so:
1var result = db.collection("item").find({}, { 2 $decouple: false 3});
If you do not specify a decouple option, ForerunnerDB will default to true and return decoupled objects.
Keep in mind that if you switch off decoupling for a query and then modify any object returned, it will also modify the internal object held in ForerunnerDB, which could result in incorrect index data as well as other anomalies.
If your data uses different primary key fields from the default "_id" then you need to tell the collection. Simply call the primaryKey() method with the name of the field your primary key is stored in:
1collection.primaryKey("itemId");
When you change the primary key field name, methods like updateById will use this field automatically instead of the default one "_id".
Removing is as simple as doing a normal find() call, but with the search for docs you want to remove. Remove all documents where the price is greater than or equal to 100:
1collection.remove({ 2 price: { 3 "$gte": 100 4 } 5});
Sometimes you want to join two or more collections when running a query and return a single document with all the data you need from those multiple collections. ForerunnerDB supports collection joins via a simple options key "$join". For instance, let's setup a second collection called "purchase" in which we will store some details about users who have ordered items from the "item" collection we initialised above:
1var fdb = new ForerunnerDB(), 2 db = fdb.db("test"), 3 itemCollection = db.collection("item"), 4 purchaseCollection = db.collection("purchase"); 5 6itemCollection.insert([{ 7 _id: 1, 8 name: "Cat Litter", 9 price: 200 10}, { 11 _id: 2, 12 name: "Dog Food", 13 price: 100 14}, { 15 _id: 3, 16 price: 400, 17 name: "Fish Bones" 18}, { 19 _id: 4, 20 price: 267, 21 name:"Scooby Snacks" 22}, { 23 _id: 5, 24 price: 234, 25 name: "Chicken Yum Yum" 26}]); 27 28purchaseCollection.insert([{ 29 itemId: 4, 30 user: "Fred Bloggs", 31 quantity: 2 32}, { 33 itemId: 4, 34 user: "Jim Jones", 35 quantity: 1 36}]);
Now, when we find data from the "item" collection we can grab all the users that ordered that item as well and store them in a key called "purchasedBy":
1itemCollection.find({}, { 2 "$join": [{ 3 "purchase": { 4 "itemId": "_id", 5 "$as": "purchasedBy", 6 "$require": false, 7 "$multi": true 8 } 9 }] 10});
The "$join" key holds an array of joins to perform, each join object has a key which denotes the collection name to pull data from, then matching criteria which in this case is to match purchase.itemId with the item._id. The three other keys are special operations (start with $) and indicate:
The result of the call above is:
1[{ 2 "_id":1, 3 "name":"Cat Litter", 4 "price":200, 5 "purchasedBy":[] 6},{ 7 "_id":2, 8 "name":"Dog Food", 9 "price":100, 10 "purchasedBy":[] 11},{ 12 "_id":3, 13 "price":400, 14 "name":"Fish Bones", 15 "purchasedBy":[] 16},{ 17 "_id":4, 18 "price":267, 19 "name":"Scooby Snacks", 20 "purchasedBy": [{ 21 "itemId":4, 22 "user":"Fred Bloggs", 23 "quantity":2 24 }, { 25 "itemId":4, 26 "user":"Jim Jones", 27 "quantity":1 28 }] 29},{ 30 "_id":5, 31 "price":234, 32 "name":"Chicken Yum Yum", 33 "purchasedBy":[] 34}]
Version => 1.3.455
If your join has more advanced requirements than matching against foreign keys alone, you can specify a custom query that will match data from the foreign collection using the $where clause in your $join.
For instance, to achieve the same results as the join in the above example, you can specify matching data in the foreign collection using the $$ back-reference operator:
1itemCollection.find({}, { 2 "$join": [{ 3 "purchase": { 4 "$where": { 5 "$query": { 6 "itemId": "$$._id" 7 } 8 }, 9 "$as": "purchasedBy", 10 "$require": false, 11 "$multi": true 12 } 13 }] 14});
The $$ back-reference operator allows you to reference key/value data from the document currently being evaluated by the join operation. In the example above the query in the $where operator is being run against the purchase collection and the back-reference will lookup the current _id in the itemCollection for the document currently undergoing the join.
Suppose we have two collections "a" and "b" and we run a find() on "a" and join against "b".
$root tells the join system to place the data from "b" into the root of the source document in "a" so that it is placed as part of the return documents at root level rather than under a new key.
If you use "$as": "$root" you cannot use "$multi": true since that would simply overwrite the root keys in "a" that are copied from the foreign document over and over for each matching document in "b".
This query also copies the primary key field from matching documents in "b" to the document in "a". If you don't want this, you need to specify the fields that the query will return. You can do this by specifying an "options" section in the $where clause:
1var result = a.find({}, { 2 "$join": [{ 3 "b": { 4 "$where": { 5 "$query": { 6 "_id": "$$._id" 7 }, 8 "$options": { 9 "_id": 0 10 } 11 }, 12 "$as": "$root", 13 "$require": false, 14 "$multi": false 15 } 16 }] 17});
By providing the options object and specifying the "_id" field as zero we are telling ForerunnerDB to ignore and not return that field in the join data.
"id": 0
The options section also allows you to join b against other collections as well which means you can created nested joins.
Version >= 1.3.12
ForerunnerDB currently supports triggers for inserts and updates at both the before and after operation phases. Triggers that fire on the before phase can also optionally modify the operation data and actually cancel the operation entirely allowing you to provide database-level data validation etc.
Setting up triggers is very easy.
Here is an example of a before insert trigger that will cancel the insert operation before the data is inserted into the database:
1var fdb = new ForerunnerDB(),
2 db = fdb.db("test"),
3 collection = db.collection("test");
4
5collection.addTrigger("myTrigger", db.TYPE_INSERT, db.PHASE_BEFORE, function (operation, oldData, newData) {
6 // By returning false inside a "before" trigger we cancel the operation
7 return false;
8});
9
10collection.insert({test: true});
The trigger method passed to addTrigger() as parameter 4 should handle these arguments:
Argument | Data Type | Description |
---|---|---|
operation | object | Details about the operation being executed. In before update operations this also includes query and update objects which you can modify directly to alter the final update applied. |
oldData | object | The data before the operation is executed. In insert triggers this is always a blank object. In update triggers this will represent what the document that will be updated currently looks like. You cannot modify this object. |
newData | object | The data after the operation is executed. In insert triggers this is the new document being inserted. In update triggers this is what the document being updated will look like after the operation is run against it. You can update this object ONLY in before phase triggers. |
In this example we insert a document into the collection and then update it afterwards. When the update operation is run the before update trigger is fired and the document is modified before the update is applied. This allows you to make changes to an operation before the operation is carried out.
1var fdb = new ForerunnerDB(),
2 db = fdb.db("test"),
3 collection = db.collection("test");
4
5collection.addTrigger("myTrigger", db.TYPE_UPDATE, db.PHASE_BEFORE, function (operation, oldData, newData) {
6 newData.updated = String(new Date());
7});
8
9// Insert a document with the property "test" being true
10collection.insert({test: true});
11
12// Now update that document to set "test" to false - this
13// will fire the trigger code registered above and cause the
14// final document to have a new property "updated" which
15// contains the date/time that the update occurred on that
16// document
17collection.update({test: true}, {test: false});
18
19// Now inspect the document and it will show the "updated"
20// property that the trigger added!
21console.log(collection.find());
Please keep in mind that you can only modify a document's data during a before phase trigger. Modifications to the document during an after phase trigger will simply be ignored and will not be applied to the document. This applies to insert and update t
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
Found 1/29 approved changesets -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
license file not detected
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
project is not fuzzed
Details
Reason
102 existing vulnerabilities detected
Details
Score
Last Scanned on 2025-01-27
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More