Supports MongoDB 2.2 <= 3.0
This is a MongoDB skunkworks project to enable queryable views within the shell. Views are like virtual collections, that can be queried as regular collections.
They support:
- Criteria
- Projections
- Joins
- Nesting
Why might you want this? Well lets say you want to save a query for regular reuse. Say you have an employees
collection:
db.employees.insert(
[
{name: "John", dob: new Date(1980, 1, 2)},
{name: "Paul", manager: true, dob: new Date(1983, 7, 10), uid: 3},
{name: "Mary", dob: new Date(1985, 5, 12), uid: 20},
{name: "Aimee", manager: true, dob: new Date(1945, 2, 20), uid: 50}
]
)
and we want all managers from an employee
collection. Then you could create a view via:
db.employees.createView("managers", { manager: true })
and query/sort/limit it as though it was a collection via
db._managers.find().sort({ name: -1 }).pretty()
/* yields =>
{
"_id": ObjectId("54f9e58e1d8a2ac246213516"),
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z"),
"uid": 3
}
{
"_id": ObjectId("54f9e58e1d8a2ac246213518"),
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z"),
"uid": 50
}
*/
it's virtual, so if you add to the underlying collection(s)
db.employees.insert( {name: "Ian", manager: true, dob: new Date(1995, 1, 20), uid: 99 })
then the same view query yields:
db._managers.find().sort({ name: -1 }).pretty();
/* yields =>
{
"_id": ObjectId("54f9e58e1d8a2ac246213516"),
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z"),
"uid": 3
}
{
"_id": ObjectId("54f9e5b41d8a2ac24621351a"),
"name": "Ian",
"manager": true,
"dob": ISODate("1995-02-20T05:00:00Z"),
"uid": 99
}
{
"_id": ObjectId("54f9e58e1d8a2ac246213518"),
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z"),
"uid": 50
}
*/
you can of course add criteria to the find()
db._managers.find({ name: /Paul/ }).sort({ name: -1 }).pretty();
/* yields =>
{
"_id": ObjectId("54f9e58e1d8a2ac246213516"),
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z"),
"uid": 3
}
*/
you can then create nested views just as easily
db._managers.createView("senior_managers", { dob: {$lt: new Date(1990, 0 , 1) } })
db._senior_managers.find()
/* yields =>
{
"_id": ObjectId("54f9d8b3f088c1c44badce68"),
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z")
}
{
"_id": ObjectId("54f9d8b3f088c1c44badce6a"),
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z")
}
*/
We can see all our views so far via
show views
/* yields =>
managers
senior_managers
*/
Maybe we don't want senior managers to show the _id
field, then we use a projection
// remove view first
db._senior_managers.drop();
db._managers.createView("senior_managers", { dob: {$lt: new Date(1990, 0 , 1)} }, { _id: 0 })
db._senior_managers.find()
/* yields =>
{
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z"),
"uid": 3
}
{
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z"),
"uid": 50
}
*/
we can even combine projections as in
db._senior_managers.find({}, {uid: 0, manager: 0})
/* yields =>
{
"name": "Paul",
"dob": ISODate("1983-08-10T04:00:00Z")
}
{
"name": "Aimee",
"dob": ISODate("1945-03-20T04:00:00Z")
}
*/
it's just a cursor, so we can sort and limit as expected:
db._senior_managers.find().sort({ dob: 1 }).limit(1)
/* yields =>
{
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z")
}
*/
Now what about joins ? Easy. Join to the users
.
// add users
db.users.insert([
{ id: 99, email: "[email protected]" },
{ id: 50, email: "[email protected]" },
{ id: 20, email: "[email protected]" },
{ id: 3, email: "[email protected]"}
])
db.employees.createView('employees_with_email', {}, {}, { target: db.users, from: "uid", to: "id"})
db._employees_with_email.find().sort({name: 1})
/* yields =>
{
"_id": {
"from": ObjectId("54f9ebb1257b0c8dc73be97a"),
"to": ObjectId("54f9f1c4067bf2a2b99c53b1")
},
"name": "Aimee",
"manager": true,
"dob": ISODate("1945-03-20T04:00:00Z"),
"uid": 50,
"id": 50,
"email": "[email protected]"
}
{
"_id": {
"from": ObjectId("54f9ec10461b20c42cabc3d4"),
"to": ObjectId("54f9f1c4067bf2a2b99c53b0")
},
"name": "Ian",
"manager": true,
"dob": ISODate("1995-02-20T05:00:00Z"),
"uid": 99,
"id": 99,
"email": "[email protected]"
}
{
"_id": {
"from": ObjectId("54f9ebb1257b0c8dc73be979"),
"to": ObjectId("54f9f1c4067bf2a2b99c53b2")
},
"name": "Mary",
"dob": ISODate("1985-06-12T04:00:00Z"),
"uid": 20,
"id": 20,
"email": "[email protected]"
}
{
"_id": {
"from": ObjectId("54f9ebb1257b0c8dc73be978"),
"to": ObjectId("54f9f1c4067bf2a2b99c53b3")
},
"name": "Paul",
"manager": true,
"dob": ISODate("1983-08-10T04:00:00Z"),
"uid": 3,
"id": 3,
"email": "[email protected]"
}
*/
It's all a cursor, so guess what? You can even join a view to another view!
Want to see what's inside your view? Inspect it!
db._employees_with_email.inspect()
/* yields =>
{
"name": "employees_with_email",
"target": "employees",
"query": {
},
"projection": {
},
"join": {
"target": "users",
"from": "uid",
"to": "id"
}
}
*/
Moreover, these views persist. Both when you switch DBs via use [db]
or by restarting the shell.
Views are virtual, and only save the state of the query used to create them. This means that each time a query is performed on a view, the latest collection data is fetched.
-
In POSIX environments, run
make
-
In WinX environments, please add
mongorc.js
to your MongoDB installation folder (if it doesn't exist), runnpm run build
to generate the browserify bundle, and finally copy the contents ofdist/bundle.js
into ``mongorc.js`.
Create
db.[collection|view].createView(
name:String,
criteria:Object,
projection:Object,
join: {
target: [collection|view]
from: String,
to: String
})
See all views in DB
show views
Inspect a view
db._[view].inspect()
Query
db._[view].find(criteria:Object, projection:Object):DBQuery
Drop
db._[view].drop()
- Under the hood, views composed criteria using
$and
operators. So allcriteria
parameters in the view, along with any find criteria in thefind
call, will be condensed into a singlecriteria
object.
ie. in the above example,
db.employees.createView("managers", { manager: true });
db._managers.find({ name: /Jane/ });
Will yield
db.employees.find({ $and: [{ manager: true }, { name: /Jane/ }] });
-
MongoDB allows for projections in the
find
function. Fields can be enabled or disabled, either as whitelists or blacklists see MongoDB docs. -
In order to properly combine projections, we must combine the two sets in certain ways:
- For matched fields in both the view and the find projection, we bitwise AND them (meaning that unless they are both true, the field is off)
- For fields enabled in the base projection, only those enabled in the find projection will remain.
- For fields disabled in the base projection, all of those disabled in the find projection will be added.
Egs.
Case 1:
db.employees.createView("managers", { manager: true }, { name: 1, _id: 1 });
db._managers.find({ }, { _id: 0 });
// yields =>
db.employees.find({ ... }, { name: 1, _id: 0 }); // id set to 0 from 1~0
Case 2:
db.employees.createView("managers", { manager: true }, { name: 1, id: 1 });
db._managers.find({ }, { name: 1 });
// yields =>
db.employees.find({ ... }, { name: 1 }); // id removed as not in find() projection
Case 3:
db.employees.createView("managers", { manager: true }, { id: 0 });
db._managers.find({ }, { email: 0 });
// yields =>
db.employees.find({ ... }, { id: 0, email: 0 }); // id removed as not in find() projection
Currently supports a single join to another collection or view.
Naming conflicts are solved by prefixing the fields with collection or view name and an underscore.
_id
field is a compound key of from
and to
_ids
API:
join: {
target: [collection|view],
from: String, // foreign key in this collection or view
to: String // unique key in target collection or view
}
-
Views are scoped to the DB level
-
View names must be unique, and cannot match any given collection name in that DB
-
Views based on dropped collections or views will be removed automatically
-
Joins are performed in-memory, and may take a long time for large collections
First time grab deps: npm install
Then run npm test