Coder Social home page Coder Social logo

mongoose-autopopulate's Introduction

mongoose-autopopulate

Always populate() certain fields in your mongoose schemas

Build Status Coverage Status

Read the docs here.

Note: population is a powerful feature, but it has limitations and helps you get away with poor schema design. In particular, it is usually bad MongoDB schema design to include arrays that grow without bound in your documents. Do not include a constantly-growing array of ObjectIds in your schema - your data will become unwieldy as the array grows and you will eventually hit the 16 MB document size limit. In general, think carefully when designing your schemas.

Usage

The mongoose-autopopulate module exposes a single function that you can pass to Mongoose schema's plugin() function.

const schema = new mongoose.Schema({
  populatedField: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'ForeignModel',
    // The below option tells this plugin to always call `populate()` on
    // `populatedField`
    autopopulate: true
  }
});
schema.plugin(require('mongoose-autopopulate'));

Only apply this plugin to top-level schemas. Don't apply this plugin to child schemas.

// Don't do `nestedSchema.plugin(require('mongoose-autopopulate'))`.
// You only need to add mongoose-autopopulate to top-level schemas.
const nestedSchema = mongoose.Schema({
  child: { type: Number, ref: 'Child', autopopulate: true }
});
const topSchema = mongoose.Schema({ nested: nestedSchema });
topSchema.plugin(require('mongoose-autopopulate'));

mongoose-autopopulate's People

Contributors

botv avatar danchoys avatar durran avatar greenkeeper[bot] avatar homeyer avatar islandrhythms avatar jmikrut avatar lukasfri avatar psperber avatar rerthal avatar siboulet avatar thevaan avatar vkarpov15 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  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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mongoose-autopopulate's Issues

does it have any option to limit json depth??

json structure is :

[ { "name": "name1", "children": [ { "name": "name2" }, { "name": "name2" }, { "name": "name3", "children":[ { "name": "name4" }, { "name": "name5" } ] } ] } ]

when it keep goes like this, does it have any option to limit depth??

An in-range update of mongoose is breaking the build 🚨

Version 4.7.6 of mongoose just got published.

Branch Build failing 🚨
Dependency mongoose
Current Version 4.7.5
Type peerDependency

This version is covered by your current version range and after updating it in your project the build failed.

As mongoose is β€œonly” a peerDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ


Status Details
  • ❌ coverage/coveralls Coverage pending from Coveralls.io Details

  • ❌ continuous-integration/travis-ci/push The Travis CI build failed Details

Commits

The new version differs by 22 commits .

  • faf2c6a chore: release 4.7.6
  • 175ad20 fix(query): don't call error handler if passRawResult is true and no error occurred
  • d1492ce test(query): repro #4836
  • 22552c5 docs(populate): remove implicit Model.populate() example
  • 62c8b08 fix(populate): use base model name if no discriminator for backwards compat
  • f0aa82d test(populate): repro #4843
  • 8f39e1b fix: handle refs correctly even if using browser driver
  • deaa95b fix(model): allow passing non-array to insertMany
  • a5150d6 fix(document): don't skip pointCut if save not defined (like in browser doc)
  • a8d3694 Merge pull request #4838 from billouboq/benchmarks
  • b98b132 Merge pull request #4842 from arciisine/patch-1
  • ecc6cfe Merge pull request #4837 from billouboq/micro-optimisations
  • f5de529 Allowing callback to be optional
  • 90878d3 chore: now working on 4.7.6
  • 4b47b5d fix insertedId error and change benchmark output

There are 22 commits in total. See the full diff.

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

autopopulate doesn't work with on inherited schemas

Hi, I'm using a discriminatorKey for inherited schema. The root schema contains

{
...
    tags:[{item:{type: ObjectId, ref: 'tags',autopopulate:true},comment:String}],
...
}

the child schema have a field that refs to the same collection.

{
...
    custom_tags:[{item:{type: ObjectId, ref: 'tags',autopopulate:true},comment:String}],
...
}

The second populate doesn't work

Autopopulate after save

I'm currently working on an interesting PR for this plugin which allows autopoulation to be executed after the document is saved, but before i do it i wanted to have some feedback on what the correct behaviour should be.

  • Since mongoose doesnt allow populated docs to be assigned to new docs its rather confusing to save the document and then not having that value populated as shown
/**
 * The user schema has
 *   credit_card: { type: ObjectId, ref: 'CreditCard', autopopulate: true }
 */
var user = new User();
user.credit_card = card; // in this case card is a mongoose doc
console.log(user.credit_card) // outputs the id
user.save(function (err) {
  console.log(user.credit_card) // also outputs the id
})

So that means the value should be populated after the save

  • Repopulating already populated docs could potentially result in a lot of overhead, since mongoose allows to assign docs to reference values maybe users should always do that and only populate docs if theyre new.
  • It seems that enabling this feature absolutely breaks my current project if values are constantly being populated after saving no matter what, so maybe we should be careful here

document.save after setting an autopopulated field

Mongoose: 4.4.14
mongoose-autopopulate: 0.4.0

consider following schema:

var schema = new mongoose.Schema({
  company:{
   type: Schema.Types.ObjectId,
   ref: 'companyProfile',
   autopopulate: { select: 'name _id  admin'},
  }
});

now, when i find a document using this schema and set the company:

doc.set('company',someObjectId);
and then,

doc.save(function(err){
// logs error
});

I get an error for schema validation failed. how should I set the autopopulated field and save the document?

Autopopulate Causes Recursion Issue

Do you want to request a feature or report a bug?
Bug.

My code worked until the update to 5.8.2, and now does not.

What is the current behavior?
Autopopulate with a recursive subdocument causes recursion limit to be hit with find().

If the current behavior is a bug, please provide the steps to reproduce.

Model

const mongoose = require( 'mongoose' ),
  autopopulate = require( 'mongoose-autopopulate' ),
  Schema = mongoose.Schema;

const Model = new Schema( {
  children: {
    type: [ Schema.Types.ObjectId ],
    ref: 'Model',
    autopopulate: true
  }
} );

Model.plugin( autopopulate );
module.exports = mongoose.model( 'Model', Model );

Controller [partial, using Koa]

exports.main = async ctx => {
  let models = await Model.find( { 'children._id': ctx.params.id } );
  ctx.body = models;
}

What is the expected behavior?
Before the update of Mongoose to 5.8.2, this would retrieve all Models that had a child with the given ID, i.e. it would retrieve all parents of the desired Model. Now, it appears because of autopopulate, not only is find() searching direct children, but all descendants, recursively, causing the call to hang.

If I set autopoulate: false everything works as expected.

What are the versions of Node.js, Mongoose and MongoDB you are using? Note that "latest" is not a version.

I'm using Koa 2.11.0, Mongoose 5.8.2, and mongoose-autopopulate 0.9.1

Sort autopopulate

Hi I was wondering if I could sort the people array people with a parameter or something?

var Town = new Schema({
country: {type: ObjectId, ref: 'Country'},
name: String,
people: {type: [{type: ObjectId, ref: 'People'}], autopopulate: true}
}, { collection: 'town'});

Allow mongoose 5.x

npm WARN [email protected] requires a peer of [email protected] but none is installed. You must install peer dependencies yourself.

I've been using it with mongoose 5.0.0-rc2 and haven't had any issues. Maybe it's time to update the peerDependency field?

autopopulate not working on findOneAndUpdate

The similar issue was already fixed in #42. But it looks it appears again.

I am using mongoose-autopopulate 0.9.1

My schema:

const selectedProtocolSchema = new mongoose.Schema({
  protocol: {
    type: mongoose.Schema.Types.ObjectId,
    ref: 'UserProtocol',
    autopopulate: true
  }
});

In such code

let obj = await Model.findOneAndUpdate({}, {protocol: id});
// the obj.protocol is null here
protocol = await Model.findOne();
// the obj.protocol is populated here as expected

Out of memory crash due to infinite loop

Hello,

I've encountered a bug due to an infinite loop.
I was trying to autopopulate an array of users included in my user collection.
And for example, a user A had a user B in his array, and user B had user A in his array as well => infinite loop.
I managed to solve the issue by using select options in autopopulate to exclude the array of the population, and break the loop.

But the issue is that it was really hard to find what was going on.
Node was crashing due to an Out of memory in the garbage collector, but was not knowing which part of the code was producing that... I was really lucky to find the issue (a user couldn't manage to connect anymore to my app because of that, and warn me).

Is it possible that you add in your code something to prevent this behavior ?
Maybe by watching if there is a population of population from the same collection and breaking the loop with a counter or something.

Thanks a lot.
Denis

How to select population field from query?

I want to select population field from query but still I am getting all fields.Any way to achieve this?

exports.getChatByBotUserId = (req, res) => {
var deferred = Q.defer()
console.log('\nget Chat By Bot User Id\n', req.params.botuserId)
Chat.find({ "bot_user": req.params.botuserId })
.populate({
path: 'bot_response',
model: 'Child',
select: 'plugin_id'
})
.exec((err,result) => {
if (err) {
res.status(500).send({ error: 'Something failed!' })
deferred.reject(err)
} else {
res.json(result)
deferred.resolve(result);
}
})
return deferred.promise;
}

Autopopulate Self ref

If you reference a schema from within itself autopopulate does not work. The way to do this is as follows:

var mySchema = new Schema({
    name: {type: String}
    parent: {type: String, ref: 'mySchema'}
})

Running mySchema.find({}).populate('parent').exec(function(error, docs){ ... }) works as expected, populating the document. AFAIK this is the correct way to self reference documents, weird but correct.

Adding the auto-populate key value {type: String, ref: 'mySchema', autopopulate: true} does not work, setting type: mongoose.Schema.Types.ObjectId or similar does not help or work.

I could not find any docs mentioning how to do this, all help appreciated.

Select when autopopulate with refPath

I want to autopopulate with the refPath and make a select in the populate. Since in the refPath is the referenced type, is it possible to select depending on this type? My schema:

const MentionTextSchema = new Schema(
    {
        text: String,
        mentions: [{
            index: Int32,
            length: Int32,
            type: String,
            mentionedThing: {
                type: Object,
                refPath: "mentions.type",
                autopopulate: optionsFunction,
            }
        }]
    }
);

I want to populate the mentions.mentionedThing.type and the type is in mentions.type and can be either a User or a Group. And depending on the type the select shall be different. I tried it with an options functions but I don't know how to access the refPath and if it's possible:

var optionsFunction = function() {
    if(this.GetTheType() === "User") { // how to get the refPath?
        return { select: 'nick_name' };
    }
    else if(this.GetTheType() === "Group") { // how to get the refPath?
        return { select: 'title' };
    }
};

Relax Mongoose dependencies version

Mongoose 4.1 has been released this weekend. I can't update Mongoose unless ~4.1 is added to autopopulate peer dependencies, or being relaxed to ^4.0.

autopopulate not working on findOneAndUpdate and findByIdAndUpdate.

I am using mongoose-autopopulate on my model. It works fine when calling MyModel.findById(id). It fails however when calling MyModel.findByIdAndUpdate() or MyModel.findOneAndUpdate()

My calling code looks as follow:
export const register = ({ params, user }, res, next) => {
Event.findByIdAndUpdate(
params.id,
{
$push: { registrations: { user } }
},
{ new: true }
)
// I need to "manually" populate here
//.populate('author')
// .populate('registrations.user')
.exec()
.then(notFound(res))
.then(event => {
console.log(event);
return event ? event.view() : null;
})
.then(success(res, 201))
.catch(next);

/Event.findById(params.id)
.then(notFound(res))
.then(event => {
event.registrations.push({ user });
return event.save();
})
.then(event => (event ? event.view() : null))
.then(success(res, 201))
.catch(next);
/
};

ObjectId fields set with autopopulate are empty after a Model.create()

I have a schema like:

const StoresSchema = new Schema({ 
    chainId: {type: mongoose.Schema.Types.ObjectId, ref: 'Chains', autopopulate: true, required: true},
    cityId: {type: mongoose.Schema.Types.ObjectId, ref: 'Cities', autopopulate: true, required: true},
    // . . . 
})

When I create a document and try to insert it with Stores.create(doc), the operation seems to work:

let doc = {
    chainId: new ObjectId,
    cityId: new ObjectId,
    // . . . 
}
let res = await Stores.create(doc)

However, when I perform a Stores.find({}) I get all the other fields inserted, but the autopopulate fields are null.

If I remove the autopopulate option from those schema fields, it works. But I need to autopopulate on queries. How can I create new documents set for autopopulating on a later query?

Auto-populate only selected mutiple columns from one Collection

Hi
This is not really issue but i would like to know , does autopopulate work on multiple selected column for population which i have heighlighted as below

var bandSchema = new Schema({
name: String,
lead: { type: ObjectId, ref: 'people', autopopulate: { [select: 'firstName'], [select: 'lastName' } }
});
bandSchema.plugin(autopopulate);

Turn Autopopulate off by Default

Currently, fields are autopopulated whenever I use Model.find (or any other method that calls find) and I have to set the autopopulate option to false whenever I don't want to use it. Is there any way to set autopopulate to false by default, and similarly only set the autopopulate option to true when I want to use it.

Does this support populating for multiple fields?

Not only on _id but also some other field?

const ActionSchema = new Schema({
  _id: { type: String, index: true, unique: true },
  label: { type: String },
  type: { type: String },
  order: { type: Number },
  mount: { type: String },
  mountType: { type: String },
  owner: { type: String },
  actions: [{ type: String, ref: 'action', autopopulate: { options: { sort: { order: 1 } } } }], // automatic recursion
  parts: [{ type: String, ref: 'part', autopopulate: true }], // automatic recursion
  parent: { type: String },
  parentType: { type: String },
  parents: { type: Object },
  expanded: { type: Boolean, default: true },
  date: { type: Date, default: Date.now },
  updated: { type: Date, default: Date.now },
});
ActionSchema.plugin(autopopulate); // automatic recursion

So given the above schema, I want to populate on both the _id and the mount. Mount is a foreign key.

So something like this is my use case:

actions: [{ 
    type: String,
    refs: [
      { ref: '_id' refPath: 'actions._id' }, 
      { ref: 'mount' refPath: 'lists._id' }
    ], 
     autopopulate: { options: { sort: { order: 1 } } } 
}],

So I need to pull docs for '_id' from the actions collection and docs for 'mount' from the lists collection.

Populating virtual field leaves the foreignField

When autopopulating a virtual field, through this example:

schema.virtual('searchResult', {
        ref: 'SearchResult',
        localField: 'special_Id',
        foreignField: 'special_Id',
        justOne: true,
        autopopulate: { select: 'name -_id' },
    });

With a 'find' command to the scema, I get the populated field, with his foreignField inside ({ name: 'XXXX', special_Id: 'YYYY' }). Even though it was not mentioned in the select (and, by my opnion, should only have: { name: 'YYYY' }. Is there a way I could exclude the foreignField and have only the one from the select option?

thanks!

Not preserving subdocument on push unless parent is existing

Hi,

I'm not sure if this is a bug or if this is some sort of limitation. It took me a little while to understand what was going on.

If you push() a subdoc to a parent that was just created, without explicitely reloading the parent from Mongo ie. using find(), pushed subdocuments appears as depopulated ie. the resulting subdocuments array will only contain the referring ids.

If you reload the parent document from Mongo after creating it, then call push(), pushed subdocuments preserves the original subdocuments data.

var Fruit = mongoose.model('Fruit', new mongoose.Schema({
  name: String,
}));

var saladSchema = new mongoose.Schema({
  fruits: [{
    type: mongoose.Schema.Types.ObjectId,
    ref: 'Fruit',
    autopopulate: true,
  }],
});

saladSchema.plugin(autopopulate);

var FruitSalad = mongoose.model('FruitSalad', saladSchema);

// Create a new fruit salad
function makeSalad() {
  var salad = new FruitSalad();
  return salad.save();
}

function reloadSalad(salad) {
  return FruitSalad.findById(salad.id);
}

// Add banana to salad
function addBanana(salad) {
  var fruit = new Fruit({name: 'banana'});

  return fruit.save().then(function(fruit) {
    salad.fruits.push(fruit);
    //return salad.save();
    return salad;
  });
}

// Add apple to salad
function addApple(salad) {
  var fruit = new Fruit({name: 'apple'});

  return fruit.save().then(function(fruit) {
    salad.fruits.push(fruit);
    //return salad.save();
    return salad;
  });
}

function test1() {
  return makeSalad().then(addBanana).then(addApple).then(function(salad) {
    console.log(salad);
    console.assert(!salad.fruits[0].name); // Fruits replaced by their ids
  });
}

// Reload salad from Mongo before adding fruits
function test2() {
  return makeSalad().then(reloadSalad).then(addBanana).then(addApple).then(function(salad) {
    console.log(salad);
    console.assert(salad.fruits[0].name); // Fruits preserved
  });
}

test1().then(test2);

Autopopulate arrays doesnt seem to work

So I wrote this test and its failing.

var InnerSchema = new mongoose.Schema({
  field: String
});
var Inner = mongoose.model('Inner', InnerSchema);

var MiddleSchema = new mongoose.Schema({
  inners: {
      type: [mongoose.Schema.Types.ObjectId],
      ref: 'Inner',
      autopopulate: { select: 'field' }
  }
});
MiddleSchema.plugin(plugin);
var Middle = mongoose.model('Middle', MiddleSchema);

var OuterSchema = new mongoose.Schema({
  middle: {
      type: mongoose.Schema.Types.ObjectId,
      ref: 'Middle',
      autopopulate: { select: 'inners' }
  }
});
OuterSchema.plugin(plugin);
var Outer = mongoose.model('Outer', OuterSchema);

new Inner({
  field: 'hello'
}).save(function (err, inner1) {
  new Inner({
      field: 'goodbye'
  }).save(function (err, inner2) {
      new Middle({
          inners: [inner1._id, inner2._id]
      }).save(function (err, middle) {
          new Outer({
              middle: middle._id
          }).save(function (err, outer) {
              Outer.findOne({}, function (err, outer) {
                  assert.equal(outer.middle.inners.length, 2);
                  outer.remove(function () {
                      middle.remove(done);
                  });
              })
          })
      })
  })
})

Disabling Autopopulate in Query

Hi,

Is there a way to disable autopopulate for a specific query? For example:

Model.find({ ... }, { autopopulate: false }) 

There's .lean() but that would mean I manually need to .hydrate() the result back to use any of the Mongoose model functions.

Or maybe I'm missing something?

Thanks

Not populating subdocuments of an embedded array

Hi. In my User schema I have an embedded array of schema type UserPermission. Even though the autopopulate option is set on some paths inside of UserPermission they are not being autopopulated when the User document is loaded. Both User and UserPermission have the autopopulate plugin enabled. Other autopopulated fields are populating as expected.

I suspect this is because the array is an embedded array, rather than array of refs that need to be populated, correct?

In the short term, what is the easiest way to manually trigger autopopulation of the embedded array after User is loaded?

In the medium/long term, shouldn't this situation be handled by the plugin?

Thank you.

Autopopulate - Select options argument(return all fields), issue with latest version of mongoose > 4.11.0

For the following program, getting different output with version @4.4.5 and @4.12.0. The version of [email protected]

let autopopulate = require('mongoose-autopopulate');
let mongoose = require('mongoose');
let Schema = mongoose.Schema;
let ObjectId = Schema.Types.ObjectId;

const CONFIG = {
    "DB_URL" : "mongodb://localhost/test1",
    "DB_CONNECT_OPTION" : {"useMongoClient" : true},
    "PERSON_SCHEMA" : "PERSON",
    "AREA_SCHEMA" : "AREA",
    "BAND_SCHEMA" : "BAND",
};

class AutoPopulateTest {
    static init(){
        try{
            mongoose.connect(CONFIG.DB_URL, CONFIG.DB_CONNECT_OPTION);

            AutoPopulateTest.dbSchemaInit();
        }catch(err){
            throw err;
        }
    }

    static dbSchemaInit(){
        try{
            let areaSchema = new Schema({
                "code": String,
                "name": String
            });
            mongoose.model(CONFIG.AREA_SCHEMA, areaSchema, CONFIG.AREA_SCHEMA);

            let personSchema = new Schema({
                "name": String,
                "nickName": String,
                "areaId": {"type":ObjectId, "ref":CONFIG.AREA_SCHEMA, "autopopulate":true }
            });
            personSchema.plugin(autopopulate);
            mongoose.model(CONFIG.PERSON_SCHEMA, personSchema, CONFIG.PERSON_SCHEMA);

            let bandSchema = new Schema({
                "name": String,
                "personId": {"type":ObjectId, "ref":CONFIG.PERSON_SCHEMA, "autopopulate":{"select" : "name"} },
                "areaId": {"type":ObjectId, "ref":CONFIG.AREA_SCHEMA, "autopopulate":true }
            });
            bandSchema.plugin(autopopulate);
            mongoose.model(CONFIG.BAND_SCHEMA, bandSchema, CONFIG.BAND_SCHEMA);
        }catch(err){
            throw err;
        }
    }

    static async saveRecords(){
        try {
            let People = mongoose.model(CONFIG.PERSON_SCHEMA);
            let Area = mongoose.model(CONFIG.AREA_SCHEMA);
            let Band = mongoose.model(CONFIG.BAND_SCHEMA);

            let taskList = [];
            let peopleRefId = mongoose.Types.ObjectId();
            let areaRefId = mongoose.Types.ObjectId();

            let personJson = {"_id": peopleRefId, "name": "Narendra Modi", "nickName":"Modi", "areaId": areaRefId};
            let personPojo = new People(personJson);
            taskList.push(personPojo.save());

            let areaJson = {"_id" : areaRefId, "code" : "IND", "name" : "INDIA"};
            let areaPojo = new Area(areaJson);
            taskList.push(areaPojo.save());

            let bandJson = {"name" : "Politics", "personId":peopleRefId, "areaId" : areaRefId};
            let bandPojo = new Band(bandJson);
            taskList.push(bandPojo.save());

            let output = await Promise.all(taskList);
            return output;
        }catch(err){
            return Promise.reject(err);
        }
    }

    static async fetchRecord(){
        try{
            let Band = mongoose.model(CONFIG.BAND_SCHEMA);

            let output = await Band.findOne().sort({"_id":-1}).exec();
            return output.toObject();
        }catch(err){
            return Promise.reject(err);
        }
    }

    static async main(){
        try{
            AutoPopulateTest.init();

            await AutoPopulateTest.saveRecords();

            let queryOutput = await AutoPopulateTest.fetchRecord();
            console.log(queryOutput);
        }catch(err){
            console.log(err);
        }
    }
}

AutoPopulateTest.main();

In band schema, I have specified following for people schema, autopopulate

"personId": {"type":ObjectId, "ref":CONFIG.PERSON_SCHEMA, "autopopulate":{"select" : "name"} },

Output with [email protected]

{
    _id: 5b5af8c48779e4782284ce6f,
    name: 'Politics',
    personId: {
        _id: 5b5af8c48779e4782284ce6d,
        name: 'Narendra Modi'
    },
    areaId:{
        _id: 5b5af8c48779e4782284ce6e,
        code: 'IND',
        name: 'INDIA',
        __v: 0
    },
    __v: 0
}

Output with [email protected]

{
   _id: 5b5af7fe5ba313224100848f,
    name: 'Politics',
    personId:{
        _id: 5b5af7fe5ba313224100848d,
         name: 'Narendra Modi',
         areaId:{ _id: 5b5af7fe5ba313224100848e,
            code: 'IND',
            name: 'INDIA',
            __v: 0
         }
    },
    areaId:{
        _id: 5b5af7fe5ba313224100848e,
        code: 'IND',
        name: 'INDIA',
        __v: 0
    },
  __v: 0
}

The areaId has not been specified in 'select option argument'. With [email protected] areaId is also part of output json. Can you please let me know the reason for different output. and how can i fix this. Thanks.

Projection issues with autopopulated fields

Setting autopopulate: true to a field in a schema makes it being projected automatically.
Trying to explicitly omit it with: { "field": 0 } in the query results in an error:

"Projection cannot have a mix of inclusion and exclusion."

Demo

Schema A

let SchemaA = new Schema({
  name: {
    type: String,
    default: ''
  },
  type: {
    type: String,
    ref: 'SchemaB',
    default: null,
    autopopulate: true
  }
});

Schema B

let SchemaB = new Schema({
  name: {
    type: String,
    default: ''
  }
});

Init

let b = new B({ "name": "b" });
let a = new A({ "name": "a", "type": b });

Queries

Query 1

A.findOne({ "name": "a" }, ['name'])
  .then(doc => { console.log(doc) })

Query 2

A.findOne({ "name": "a" }, { "name": 1, "type": 0 })
  .then(doc => { console.log(doc) })

Actual result

Query 1

{
  "name": "a" , 
  "type": {
    "name": "b"
  }
}

Query 2
Projection cannot have a mix of inclusion and exclusion.

Expected result

{
  "name": "a" 
}

autopopulate results to null in depth level 2

I started using mongoose-autopopulate, as I've been annoyed of manually typing the populate paths. I developed a service which stores post in a mongodb database and links to an user id stored, used for authentication too. Everything works fine, but when I try to add a comment to a post linked to an user account, too, the user in the comment is not populated (I'll get null). I had a look in my database and everything works correctly, the database is storing the right links.

.get((req, res) => {
        PostModel.findById(req.params.id, (err, post) => {
            if (err){
                res.status(500).json({
                    success: false,
                    message: 'internal server error'
                });
            } else if (!post) {
                res.status(404).json({
                    success: false, 
                    message: 'post doesn\'t exist'
                });
            } else {
                res.status(200).json({
                    success: true,
                    message: 'post succesfully queried',
                    data: post
                });
            }
        });
    })

My query that returns the populated post

Database

{"_id":{"$oid":"5b455bb3124880076a145d45"},"published":true,"comments":[{"$oid":"5b455bd2124880076a145d46"}],"title":"e","content":"e","author":{"$oid":"5b442c426bdeb91233486f0b"},"createdAt":{"$date":{"$numberLong":"1531272115219"}},"updatedAt":{"$date":{"$numberLong":"1531272146141"}},"__v":{"$numberInt":"0"}}

My post

{"_id":{"$oid":"5b455bd2124880076a145d46"},"content":"test comment","author":{"$oid":"5b442c426bdeb91233486f0b"},"createdAt":{"$date":{"$numberLong":"1531272146098"}},"updatedAt":{"$date":{"$numberLong":"1531272146098"}},"__v":{"$numberInt":"0"}}

My comment

{"_id":{"$oid":"5b442c426bdeb91233486f0b"},"permissions":{"admin":false,"blog":true},"verfied":false,"disabled":false,"username":"s","email":"[email protected]","password":"$2b$10$M5a9eQz3fMVOc/VKBV8ndOX8kpG1e1nCf7RuQ2ABs4IU3MXKUW4Uy","createdAt":{"$date":{"$numberLong":"1531194434294"}},"updatedAt":{"$date":{"$numberLong":"1531194434294"}},"__v":{"$numberInt":"0"}}

And my user.

As you can see, everything is alright in the database itself, but when I use the query with mongoose using these schemas

Schemas

const PostSchema = new Schema({
    title: {
        type: String,
        unique: true,
        trim: true,
        maxlength: 256
    },
    content: {
        type: String,
        trim: true,
        maxlength: 100000
    },
    published: {
        type: Boolean,
        default: true
    },
    author: {
        type: Schema.Types.ObjectId,
        ref: 'User',
        required: true,
        autopopulate: {select: ['username', 'email']}
    },
    comments: [{
        type: Schema.Types.ObjectId,
        ref: 'Comment',
        autopopulate: true
    }]
}, {
    timestamps: true,
    strict: true
});

PostSchema.plugin(autopopulate);

const Post = mongoose.model('Post', PostSchema);
module.exports = Post;

Post.js

const UserSchema = new Schema({
    username: {
        type: String,
        unique: true,
        required: true,
        trim: true,
        maxlength: 256
    }, 
    password: {
        type: String,
        required: true,
        trim: true,
        maxlength: 256
    },
    email: {
        type: String,
        unique: true,
        required: true,
        trim: true,
        lowercase: true,
        match: /^[a-z0-9_\-.+"]+@([a-z0-9.-])+\.([a-z0-9]{2,})$/,
        maxlength: 256
    },
    permissions: {
        admin: {
            type: Boolean,
            default: false
        },
        blog: {
            type: Boolean,
            default: false
        }
    },
    verfied: {
        type: Boolean,
        default: false
    },
    disabled: {
        type: Boolean,
        default: false
    }
},{
    timestamps: true,
    strict: true
});

const User = mongoose.model('User', UserSchema);
module.exports = User;

User.js

const CommentSchema = new Schema({
    content: {
        type: String,
        required: true,
        maxlength: 1000
    },
    author:  {
        type: Schema.Types.ObjectId,
        required: true,
        autopopulate: {select: ['username', 'email']}
    }
},{
    timestamps: true,
    strict: true
});

CommentSchema.plugin(autopopulate);

const Comment = mongoose.model('Comment', CommentSchema);
module.exports = Comment;

Comment.js

I'll get

{"success":true,"message":"post succesfully queried","data":{"published":true,"comments":[{"_id":"5b455bd2124880076a145d46","content":"test comment","author":null,"createdAt":"2018-07-11T01:22:26.098Z","updatedAt":"2018-07-11T01:22:26.098Z","__v":0}],"_id":"5b455bb3124880076a145d45","title":"e","content":"e","author":{"_id":"5b442c426bdeb91233486f0b","username":"s","email":"[email protected]"},"createdAt":"2018-07-11T01:21:55.219Z","updatedAt":"2018-07-11T01:22:26.141Z","__v":0}}

The reference to the user is null, I don't have any idea if I do something wrong or if this is a bug. Please tell me if I did something wrong.
Kjell

Populating within nested array

I have my following FeedSchema defined as follows:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const FeedSchema = new Schema({
  owner: {
    type: Schema.Types.ObjectId,
    ref: "userType"
  },
  userType: {
    type: String,
    enum: ["IndustryPartner", "User", "School"]
  },
  posts: [
    {
      author: {
        userType: {
          type: String,
          enum: ["IndustryPartner", "User", "School"]
        },
        user: {
          type: Schema.Types.ObjectId,
          refPath: "posts.author.userType", 
          autopopulate: true,
          required: true
        },
 
      },
   ...
module.exports = Feed = mongoose.model(
  "Feed",
  FeedSchema.plugin(require("mongoose-autopopulate"))
);

However, I am still getting the author.user as the ObjectId itself. What am I doing wrong?

Auto Populate works for one key in document but not in another

I have the following Schema

const ClassSchema = new Schema({
  students: {
    numOfStudents: { type: Number, default: 0 },
    studentList: [
      {
        type: Schema.Types.ObjectId,
        ref: "User",
        autopopulate: { select: "name profile profile_type account_type" }
      }
    ]
  },
  mentors: {
    numOfMentors: { type: Number, default: 0 },
    mentorList: [
      [
        {
          type: Schema.Types.ObjectId,
          ref: "User",
          autopopulate: { select: "name profile profile_type account_type" }
        }
      ]
    ]
  },
});
module.exports = Class = mongoose.model(
  "Class",
  ClassSchema.plugin(require("mongoose-autopopulate"))
);

And within the Document I am testing against I have the following:

 "students" : {
        "numOfStudents" : NumberInt(1), 
        "studentList" : [
            ObjectId("5c9b828e347bb645e0865257")
        ]
    }, 
    "mentors" : {
        "numOfMentors" : NumberInt(1), 
        "mentorList" : [
            ObjectId("5c9ba636347bb645e0865283")
        ]
    }

and when I use the following query:

const query = {
         "students.studentList": { $in: req.user._id }
       };
       Class.find(query)

I get the following response:

[
    {
        "students": {
            "numOfStudents": 1,
            "studentList": [
                {
                    "_id": "5c9b828e347bb645e0865257",
                    "name": "Student",
                    "account_type": "student",
                    "profile": {
                        "profile_picture": {
                            "url": "https://picture.jpeg",
                            "originalname": "download (2).jpeg",
                            "mimetype": "application/jpeg",
                            "blobName": "profile-picture-7eb5e4b6-a1ad-4bbf-880b-02540d709a86.jpeg",
                            "container": "stemuli",
                            "blob": "profile-picture-7eb5e4b6-a1ad-4bbf-880b-02540d709a86.jpeg",
                            "size": "6136",
                            "etag": "\"0x8D75918FB547DD0\"",
                            "createdOn": "2019-11-08T04:03:54.746Z"
                        },
                        "_id": "5c9b828e347bb645e086525a"
                    },
                    "profile_type": "StudentProfile"
                }
            ]
        },
        "mentors": {
            "numOfMentors": 1,
            "mentorList": [
                [
                    "5c9ba636347bb645e0865283"
                ]
            ]
        }
}
]

I assumed maybe I messed something up here and the user in the mentors.mentorList with the _id : 5c9ba636347bb645e0865283 didn't have the correct references. So, since I know that the student was populating, I moved copied the student into the mentors.mentorList.

So, now it would look like this:

"students" : {
        "numOfStudents" : NumberInt(1), 
        "studentList" : [
            ObjectId("5c9b828e347bb645e0865257")
        ]
    }, 
    "mentors" : {
        "numOfMentors" : NumberInt(1), 
        "mentorList" : [
            ObjectId("5c9b828e347bb645e0865257")
        ]
    }

Weirdly enough, I get the same type of response back with the mentors.mentorList not populated:

"students": {
            "numOfStudents": 1,
            "studentList": [
                {
                    "_id": "5c9b828e347bb645e0865257",
                    "name": "Student",
                    "account_type": "student",
                    "profile": {
                        "profile_picture": {
                            "url": "https://stemuli.blob.core.windows.net/stemuli/profile-picture-7eb5e4b6-a1ad-4bbf-880b-02540d709a86.jpeg",
                            "originalname": "download (2).jpeg",
                            "mimetype": "application/jpeg",
                            "blobName": "profile-picture-7eb5e4b6-a1ad-4bbf-880b-02540d709a86.jpeg",
                            "container": "stemuli",
                            "blob": "profile-picture-7eb5e4b6-a1ad-4bbf-880b-02540d709a86.jpeg",
                            "size": "6136",
                            "etag": "\"0x8D75918FB547DD0\"",
                            "createdOn": "2019-11-08T04:08:44.406Z"
                        },
                        "_id": "5c9b828e347bb645e086525a"
                    },
                    "profile_type": "StudentProfile"
                }
            ]
        },
        "mentors": {
            "numOfMentors": 1,
            "mentorList": [
                [
                    "5c9b828e347bb645e0865257"
                ]
            ]
        }

I am really not sure what could be going wrong. And it seems like it is user error, but I can't figure out why it's not working! Maybe someone can shed some light on this.

Thank you so much.

How to restrict deep population in mongoose-autopopulate

In one query I want to deep populate and in another query I want to populate till one level.
Here's my schema which is deep populating the data.

 ChildSchema = new Schema({
    plugin_id: { type: String }, 
    title: { type: String }, //card title
    text: { type: String },
    is_valid: { type: Boolean,require:true, default:  false},
    children: [{ type: Schema.Types.ObjectId, ref: 'Child', autopopulate: true }]
});

ChildSchema.plugin(autopopulate);
module.exports = mongoose.model('Child', ChildSchema);

This is Chat History schema:

ChatHistorySchema = new Schema({
    type:{ type:Number }, 
    bot_response:{ type: Schema.Types.ObjectId, ref: 'Child' } 
}); 

This is my query which in which I am trying to disable the auto-populate feature since I don't want deep population instead i want to populate Child schema only to 1 level.

    //query 
  ChatHistory.find({ "bot_user": req.params.botuserId }, {}, {autopopulate: false})
   .populate({
      path: 'bot_response',
      model: 'Child'
   })
   .exec((err,result) => {
    if (err) {
      res.status(500).send({ error: 'Something failed!' })
      deferred.reject(err)
    } else {
      res.json(result)
      deferred.resolve(result);
    }
  })

This query is still returning deeply populated data.

I am not sure how can i get control over maxDepth from query.
Any help is highly appreciated.

Stackoverflow error when a schema references itself

Code:

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.Types.ObjectId;
var autopopulate = require('mongoose-autopopulate');

mongoose.connect('mongodb://localhost/test');

var ReplySchema = new Schema();
ReplySchema.add({
  text: String,
  replies: [ReplySchema]
});

var CommentSchema = new Schema({
  text: String,
  replies: [ReplySchema]
});

var TitleSchema = new Schema({
  title: String
});
var PostSchema = new Schema({
  title: { type: ObjectId, ref: 'Title', autopopulate: true },
  comments: [CommentSchema]
}).plugin(autopopulate);

var Reply = mongoose.model('Reply', ReplySchema);
var Comment = mongoose.model('Comment', CommentSchema);
var Title = mongoose.model('Title', TitleSchema);
var Post = mongoose.model('Post', PostSchema);

Title.create({ title: 'The Title' }, function(titleErr, createdTitle) {
  if (titleErr) {
    console.log('titleErr: ', titleErr);
  }
  Post.create({
    title: createdTitle._id,
    comments: [{ text: 'comment one', replies: [] }]
  }, function(postErr, createdPost) {
    if (postErr) {
      console.log('postErr: ', postErr);
    }
    console.log('createdPost: ', createdPost);
  });
});

Output:

~/code/test $ node test.js
/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:0
(function (exports, require, module, __filename, __dirname) { var mongoose = r

RangeError: Maximum call stack size exceeded
    at Array.join (native)
    at /Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:93:20
    at Schema.eachPath (/Users/azerner/code/test/node_modules/mongoose/lib/schema.js:543:5)
    at eachPathRecursive (/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:88:10)
    at /Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:91:7
    at Schema.eachPath (/Users/azerner/code/test/node_modules/mongoose/lib/schema.js:543:5)
    at eachPathRecursive (/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:88:10)
    at /Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:91:7
    at Schema.eachPath (/Users/azerner/code/test/node_modules/mongoose/lib/schema.js:543:5)
    at eachPathRecursive (/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:88:10)
    at /Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:91:7
    at Schema.eachPath (/Users/azerner/code/test/node_modules/mongoose/lib/schema.js:543:5)
    at eachPathRecursive (/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:88:10)
    at /Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:91:7
    at Schema.eachPath (/Users/azerner/code/test/node_modules/mongoose/lib/schema.js:543:5)
    at eachPathRecursive (/Users/azerner/code/test/node_modules/mongoose-autopopulate/index.js:88:10)
~/code/test $

With autopopulate removed:

var PostSchema = new Schema({
  title: { type: ObjectId, ref: 'Title' },
  comments: [CommentSchema]
});

Output:

~/code/test $ node test.js
createdPost:  { __v: 0,
  title: 55fedada732e5c692bec3120,
  _id: 55fedada732e5c692bec3121,
  comments:
   [ { text: 'comment one',
       _id: 55fedada732e5c692bec3122,
       replies: [] } ] }

Autopopulate doesn't work on virtuals

The code below defines two mongoose schemas. The client schema has a virtual which is supposed to be autopopulated by the mongoose-autopopulate plugin. Unfortunately it is not - I need to manually add .populate('service.net.radio.accessPoint') to the find query in order to get it populated. What am I doing wrong? I didn't find any documentation related to handling virtuals in mongoose-autopopulate. There was a pull request and this feature was supposedly implemented. Is it currently broken?

const clientSchema = new mongoose.Schema({
    service: {
        net: {
            radio: {
                accessPointName: {
                    type: String,
                    required: false,
                },
                ip: {
                    match: /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/,
                    required: false,
                    type: String,
                    trim: true,
                }
            }
        }
    }
}, {
    toJSON: {
        virtuals: true,
    },
    toObject: {
        virtuals: true,
    },
});
clientSchema.virtual(`service.net.radio.accessPoint`, {
    ref: `settings.accessPoint`,
    localField: `service.net.radio.accessPointName`,
    foreignField: `name`,
    autopopulate: true
});
clientSchema.plugin(require(`mongoose-autopopulate`));
return mongoose.model(`client`, clientSchema);

/*...*/

const accessPointSchema = new mongoose.Schema({
    name: {
        required: true,
        type: String,
        trim: true,
        unique: true
    }
}, {
    collection: `settings.accessPoints`
});
return mongoose.model(`settings.accessPoint`, accessPointSchema);

Need match case

I am not able to match the auto-populated data of mongoose Schema
EG:->

var baseSchema=new Schema{
name:{type:String}
childSchema:[{type:String, ref:'ChildSchema' autopopulate:{ select :'name address'}}]
}
var ChildSchema=new Schema{
name:{type:String}
address:{type:String}
}
baseSchema.find({childShema.$.name='abc'});
                                OR

baseSchema.find({childShema.name='abc'});
RESULT Coming : NO-DATA
Result Needed : [values]

How to AutoPopulate inside a object

status: {
      type: {
        id: {
          type: ObjectId,
          ref: 'CustomerStatus',
          autopopulate: { select: 'title' },
        },
        updatedBy: {
          type: ObjectId,
          ref: 'User',
          autopopulate: { select: 'name' },
        },
        updatedAt: {
          type: Date,
        },
      },
    },

Something like this

not autopopulating in lean() query

import mongoose from "mongoose";
import autopopulate from "mongoose-autopopulate";
const FollowNotification = mongoose
  .Schema({
    sender : {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Account",
      autopopulate: { select: ["id", "name"] }
    },
    receiver: {
      type: mongoose.Schema.Types.ObjectId,
      ref: "Account",
      autopopulate: { select: ["id", "name"] }
    },
    read: { type: Boolean, default: false },
    createdDate: { type: Date, default: Date.now }
  })
  .plugin(autopopulate);

export default mongoose.model("FollowNotification", FollowNotification);

this is schema code


router.get("/", async (req, res) => {
  if (req.session.info === undefined) {
    return res.status(403).json({
      code: 0
    });
  }

  let followNotifications = await FollowNotification.find({
    receiver: req.session.info._id
  }).lean();

  for (let i = 0; i < followNotifications.length; i++) {
    followNotifications[i].type = "follow";
  }

  let likeNotifications = await LikeNotification.find({
    receiver: req.session.info._id
  }).lean();

  for (let i = 0; i < likeNotifications.length; i++) {
    likeNotifications[i].type = "like";
  }

  let commentNotifications = await CommentNotification.find({
    receiver: req.session.info._id
  }).lean();

  for (let i = 0; i < commentNotifications.length; i++) {
    commentNotifications[i].type = "comment";
  }

  let notifications = [];

  notifications = notifications.concat(followNotifications);
  notifications = notifications.concat(likeNotifications);
  notifications = notifications.concat(commentNotifications);

  notifications.sort((a, b) => {
    if (a._id < b._id) return 1;
    if (a._id > b._id) return -1;
    return 0;
  });

  return res.json(notifications);
});

ant this is node express code returning notifications array.

{_id: "5bd72e20ed72c56d73b5b606", read: true, sender: "5b16243e210080215bd45ac5", receiver: "5b16243e210080215bd45ac5", post: "5bd72b66ed72c56d73b5b5f5", …}
content: "fesf"
createdDate: "2018-10-29T15:58:24.452Z"
post: "5bd72b66ed72c56d73b5b5f5"
read: true
receiver: "5b16243e210080215bd45ac5"
sender: "5b16243e210080215bd45ac5"
__v: 0
_id: "5bd72e20ed72c56d73b5b606"
__proto__: Object

this is the result.

is this expected ??

Autopopulate in discriminated collections.

var baseSchema = new Schema({
       field: String
});
baseSchema.plugin(autopopulate);
module.exports = mongoose.model('BaseSchema', baseSchema);`    

var childSchema = new Schema({
       items: [{
             type: Schema.Types.ObjectId, ref: 'ChildData', autopopulate: true
       }]
});
childSchema.plugin(autopopulate);
module.exports = BaseSchema.discriminator('ChildSchema', childSchema);`

Autopopulate don't work if populated field are in child schema.

UPD.
Works correctly if query "based" on child schema. E.g. ChildSchema.find({}), but don't work if BaseSchema.find({})

Populate without select (when using pre hooks)

Hi,

i run in an issue when i use autopopulate together with self written pre-hooks for populating (find, finOne).

Situation: I want to autopopulate a referenced model with a certain value, so i choose the 'select' option. The referenced model/schema has pre-hooks for populating.

Result: The populating part is working but the select part isn't. The hole doc will be populated what is not the intention of using the select option.

Edit: The problem appears also when the referenced model also use this plugin.

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.