npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

mongo-views

v0.1.4

Published

Support Views in MongoDB shell

Downloads

7

Readme

mongo-views

Build Status

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.

Installation

  • In POSIX environments, run make

  • In WinX environments, please add mongorc.js to your MongoDB installation folder (if it doesn't exist), run npm run build to generate the browserify bundle, and finally copy the contents of dist/bundle.js into ``mongorc.js`.

Basic Usage

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()

Criteria

  • Under the hood, views composed criteria using $and operators. So all criteria parameters in the view, along with any find criteria in the find call, will be condensed into a single criteria 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/ }] });

Projection

  • 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:

    1. 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)
    2. For fields enabled in the base projection, only those enabled in the find projection will remain.
    3. 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

Join

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
}

Guidelines

  • 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

Run Tests

First time grab deps: npm install

Then run npm test