Giter Club home page Giter Club logo

mongoose-fill's Introduction

mongoose-fill

mongoose-fill is mongoose.js add-on that adds simple api for virtual async fields.

why?

This just gives you a simple and easy to use API for:

  • implementing db joins without keeping refs to related objects (with refs you can use populate)
  • joining mongoose object with any kind of data (with any async service - other db or web service)

api use cases - learn by example

basic use case fills single filed

// import of 'mongoose-fill' patches mongoose and returns mongoose object, so you can do:
var mongoose = require('mongoose-fill')
 ...
// note you should set virtual properties options 
// on you schema to get it mongoose-fill work
var myParentSchema = new Schema({
    ....
}, {
  toObject: {
    virtuals: true
  },
  toJSON: {
    virtuals: true 
  }
}

myParentSchema.fill('children', function(callback){
    this.db.model('Child')
        .find({parent: this.id})
        .select('name age')
        .order('-age')
        .exec(callback)
})
...
Parent.findById(1).fill('children').exec().then(function(parent){
    //parent.children <- will be array of children with fields name and age ordered by age
})

filling property using single query for multiple objects

myParentSchema.fill('childrenCount').value(function(callback){
    // `this` is current (found) instance
    this.db.model('Child')
        .count({parent: this.id})
        .exec(callback)
    // multi is used for queries with multiple fields
    }).multi(function(docs, ids, callback){     
    // query childrenCount for all found parents with single db query
    this.db.model('Child')
        .aggregate([{
            $group: {
                _id: '$parent',
                childrenCount: {$sum: 1}
            }},
            {$match: {'_id': {$in: ids}}}
        ], callback)
})
...

// you can place property name that should be filled in `select` option
Parent.find({}).select('name childrenCount').exec().then(function(parents){
    //parent.childrenCount <- will contain count of children
})

using fill options with default values

myParentSchema.fill('children', function(select, order, callback){
    this.db.model('Child')
        .find({parent: this.id})
        .select(select)
        .order(order)
        .exec(callback)
}).options('', '-age')
...

// fill children with only `name age` properties ordered by `age`
Parent.findById(1).fill('children', 'name age', 'age').exec().then(function(parent){
    //parent.children <- will be array of children with fields name and age ordered by age
})

Also check the code of test for more use cases

how does it work

  • adds fill method to mongoose schema object
  • adds fill and filled prototype methods to mongoose model
  • patches mongoose query exec method extending query api with fill method
  • implemented using mongoose virtual setters/getters (actual value is stored in __propName property)

Installation

npm install mongoose-fill

Run tests

npm test

mongoose-fill's People

Contributors

sibelius avatar wclr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

mongoose-fill's Issues

Support fill propagation to field models

Use case: a Post that has Comment that has a Like

I want to use mongoose-fill like this:

post.find().fill('comment.like', user)

Right now, I need to define this propagation step on my schemas

Post.fill('comment.like', function(user, cb) {
  this.comment.fill('like', user, cb);
})

Comment.fill('like', function(user, cb) {
  Like.find({owner: this._id, user: user}).exec(function (err, like) {
    cb(err, like.length > 0);
  });
})

this.id or this._id are undefined

Trying to count objects from other collection which have a reference to the original object (similar to example given where parent: this.id)
This.id is undefined:

ParentSchema.fill('childCount', (callback) => { console.log(this.id); Child.count({ parent: this.id }).exec(callback); });

Exec works only with Promise

Hi
When I tried to write this:

Art.find().fill('likes').exec(function (err, data) {
  res.send(data);
});

I didn't got response. But with this:

Art.find().fill('likesCount').exec().then(function (data) {
  res.send(data);
});

all works fine.
And, can you explain how to catch errors with this? Will this work?

Art.find().fill('likesCount').exec().then(cbOk,  cbError);

Thanks

Any reason why the callback parameter comes last?

I was having an issue with the following code:

feed.find().select(visibleFields)                
                .fill('moderatedPosts', {limit: 100})
                .fill('postCount')
                .exec();

It failed. Gave no error, no nothing. Just failed. However, this version:

feed.find().select(visibleFields)
                .fill('moderatedPosts postCount', {limit: 100})
                .exec();

worked fine.

Then I realized that if the optional parameters aren't supplied, the first one becomes the callback, which wasn't what my code was expecting. This seems to me like a very strange behavior. Wouldn't it be better to have the callback always come first and have all the optional parameters come after it? Any specific reason why this was done?

Does not work with ES6 arrow functions.

Hello, I've changed my fill function to arrow function. It didn't work. After I changed to previous one, it worked.

TicketSchema.fill("kioskid", function (callback) {}); // working
TicketSchema.fill("kioskid", (callback) => {}); // not working

Current version on NPM fails

The current version available on NPM fails due to a require('mongoose-fill') in the index.js file where it should be require('mongoose')

I don't see this in the git history on the repo anywhere, so I'm not sure how it happened.

Fill property in array of subdocs?

Is it possible to fill a property in an array of subdocs?

var mongoose = require('mongoose-fill');
mongoose.connect('mongodb://localhost/ps2test');

var Vehicle = new mongoose.Schema({
    vehicle_id: Number,
});

Vehicle.fill('vehicle', function(callback){
    callback(null, "flash");
});

var Stats = new mongoose.Schema({,
    vehicles: [Vehicle],
});

var Stat = mongoose.model('CharStat', Stats);

var stat = {
    vehicles:[{
            vehicle_id: 17
        }, {
            vehicle_id: 8
    }]
};

Stat.create(stat, function (err, doc) {
    if (err) console.log(err);
    console.log(doc);

    Stat.findOne({_id: doc._id}).fill('vehicles.vehicle').exec(function (err, event) {
        if (err) console.log(err);
        console.log(event.toObject({virtuals:true}));
    })
});

Results in:

{ _id: 571d119eedf9251039a5277d,
  __v: 0,
  vehicles: 
   [ { vehicle_id: 17,
       _id: 571d119eedf9251039a5277f,
       vehicle: undefined,
       id: '571d119eedf9251039a5277f' },
     { vehicle_id: 8,
       _id: 571d119eedf9251039a5277e,
       vehicle: undefined,
       id: '571d119eedf9251039a5277e' } ],
  id: '571d119eedf9251039a5277d' }

The fill function is returning undefined, though it should be "flash".
Am I doing something wrong?
I tried to emulate the test at https://github.com/whitecolor/mongoose-fill/blob/master/test.js#L182

Cannot read property 'model' of undefined

I have an array of favorites and wanted to set a virtual field "followers" in this way:

userSchema.fill('followers', function(callback){
  var somefavs = { "favorites.innerArray.0": { $exists: true } };
  this.db.model('User').find(somefavs, function (err, favs) {
    callback(null, parseInt(Math.random() * 100));
  });
});

However, it throws the following error:

1) controllers/favorite.js favorite.ids() can aggregate followers:
     Uncaught TypeError: Cannot read property 'model' of undefined
      at Object.<anonymous> (models/users.js:9:1733)
      at fillDoc (node_modules/mongoose-fill/index.js:60:23)
      at node_modules/mongoose-fill/index.js:297:17
      at node_modules/mongoose-fill/node_modules/async/lib/async.js:356:13
      at async.forEachOf.async.eachOf (node_modules/mongoose-fill/node_modules/async/lib/async.js:233:13)
      at _asyncMap (node_modules/mongoose-fill/node_modules/async/lib/async.js:355:9)
      at Object.map (node_modules/mongoose-fill/node_modules/async/lib/async.js:337:20)
      at node_modules/mongoose-fill/index.js:296:21
      at node_modules/mongoose-fill/node_modules/async/lib/async.js:356:13
      at async.forEachOf.async.eachOf (node_modules/mongoose-fill/node_modules/async/lib/async.js:233:13)
      at _asyncMap (node_modules/mongoose-fill/node_modules/async/lib/async.js:355:9)
      at Object.map (node_modules/mongoose-fill/node_modules/async/lib/async.js:337:20)
      at node_modules/mongoose-fill/index.js:202:15
      at node_modules/mongoose-fill/node_modules/async/lib/async.js:356:13
      at iterate (node_modules/mongoose-fill/node_modules/async/lib/async.js:262:13)
      at async.forEachOfSeries.async.eachOfSeries (node_modules/mongoose-fill/node_modules/async/lib/async.js:281:9)
      at _asyncMap (node_modules/mongoose-fill/node_modules/async/lib/async.js:355:9)
      at Object.mapSeries (node_modules/mongoose-fill/node_modules/async/lib/async.js:347:20)
      at node_modules/mongoose-fill/index.js:200:13
      at Query.<anonymous> (node_modules/mongoose/lib/query.js:2181:28)
      at node_modules/kareem/index.js:259:21
      at node_modules/kareem/index.js:127:16
      at _combinedTickCallback (internal/process/next_tick.js:67:7)
      at process._tickDomainCallback (internal/process/next_tick.js:122:9)

If I remove the single part calling the this.db.model() it works flawlessly:

userSchema.fill('followers', function(callback){
  callback(null, parseInt(Math.random() * 100));
});

I have no idea why this happens as it looks almost the same as the documentation. One thing to note is that the model to retrieve is the same as the schema we are working with (User). However, since the problem is that it cannot find the function .model() and not that it cannot find the model itself, I think it might be an error with mongoose-fill itself.

Mongoose hooks

Hey there, thanks for contributing for open source. I was using in your package for integrating some firebase data into something I'd need, and I wonder if it is possible to use this inside of schema.pre('find', callback) callbacks for example. Something like this:

schema.fill('user', function(callback) {
  if (this.userFirebaseUid) {
    return FirebaseAdmin
      .auth()
      .getUser(this.userFirebaseUid)
      .then(user => {
        callback(null, user.providerData[0])
      })
      .catch(err => {
        callback(err, null);
      })
  }

  callback();
});

const autoPopulateRefs = function autoPopulateRefs(next) {
  this.fill('user');
  this.populate('source');
  this.populate('category');

  next();
}

schema
  .pre('findOne', autoPopulateRefs)
  .pre('find', autoPopulateRefs);

throws and error with mongoose 4.13.7

var multipleProps = __fill.fill.props.length > 1
//args.unshift(doc)
args.push(function(err, val){
// if val is not passed, just leave it
if (arguments.length > 1 && val !== doc){
if (prop){
doc.set(prop, multipleProps ? val[prop] : val)
} else {
props.forEach(function(prop){
doc.set(prop, val[prop])
})
}

Work with aggregation

When I tried to use mongoose-fill with aggregation

  require('mongoose').model('Art').aggregate([
    {$lookup: {
      from: 'likes',
      localField: '_id',
      foreignField: 'art',
      as: 'likes'
    }},
    {$sort: {_id: -1}},
  ]).fill('likesCount').exec(callback);

I got an error

TypeError: require(...).model(...).aggregate(...).fill is not a function at router.get

When I write

 require('mongoose').model('Art').find({}).fill('likesCount').exec(callback);

all works fine
I think monggose-fill not designed for work in this scenario?

document#fill does not return a promise

This works:

Example.findOne({_id: id})
.exec()
.then(found => {
    found.fill('myRef', (err, filled) => {
        // filled.myRef is filled
    });
});

But this does not:

Example.findOne({_id: id})
.exec()
.then(found => {
    return found.fill('myRef);
})
.then(filled => {
        // filled.myRef is not filled, because the document fill method only takes the callback, it doesn't return a promise.
    });
});

And I do know I can do this:

Example.findOne({_id: id})
.fill('myRef)
.exec()
.then(filled => {
        // filled.myRef is filled
    });
});

but sometimes you have a mongoose document which you want to fill later, not at query time.

(Thanks for the great plugin though, I prefer it to mongoose's populate!)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.