Mongoose Find $in Query Not Working - node.js

I have some code that looks like this
return courseModel.findQ({ autoReview: true}).then(function(autoCourses){
autoCourses = _.pluck(autoCourses, '_id');
console.log("autoCourses", autoCourses);
return userCoursesModel.findQ({
activityId: {
$in: autoCourses
}
}).then(function(ucs) {
var saves = [];
console.log("ucs", ucs);
When i log "autoCourses" i see a few documents printed, and the lodash pluck should pluck out the _id's of each one. I confirmed this is working correctly.
I am using mongoose-q:
https://github.com/iolo/mongoose-q
And i tried the normal "find" as well, but no luck.
When I do a findQ({}) and find all the docs, I can see ones that match the activityId. For example:
autoCourses [ 55d3b57e395d0105a4828f18,
55d4e0b8df5d4cdc23c916e2,
55d23654642c1d05124c1ace,
55d238b0642c1d05124c1ad2,
55d23bb1ecce9f4b14f79c7b,
55d257cf70721efc16db1c24,
55d2682532eace3118376a95,
55d2694a32eace3118376aad,
Also, when I log each user course activityId with a findAll, I can see that there are matches:
ucs.activityId 55de1dedc7c10dfc3bf5d9ae
ucs.activityId 55d3a91d46206dde7a46ed94
ucs.activityId 55df9974d8dfddb877a372f9
ucs.activityId 55d3b57e395d0105a4828f18
What is happening here? Why isn't the $in query working?
UPDATE:
As requested this is what the user course schema looks like:
var UserCourseSchema = new Schema({
activityId: {
type: Schema.Types.ObjectId,
ref: 'courses',
required: true
},
and the course
var CourseSchema = new Schema({
autoReview: {
type: Boolean,
default: false
},
Creating the userCourse document:
var newUserCourse = {
userId : userId,
activityId : activity._id,
steps : []
}
// copy steps from activity
activity.steps.forEach(function(step){
newUserCourse.steps.push({partId: step._id});
})
return UserCourses.createQ(newUserCourse);

I contacted MongoDB support regarding this and I found that mongoose-q was using an older version of mongoose (< version 4) while my top level dependency was 4.0.5, this was causing bizarre behavior. As soon as I updated mongoose-q to 0.1.0 and mongoose to 4.1.6 and the find worked again.

Related

How to insert Array of objects in mongoDB?

I am very new to MONGO DB so please bear with me.I am having a problem my array of objects is not working properly .
Here is my schema
const playerSchema = new mongoose.Schema({
name: String,
stats :{
wins:Number,
losses:Number,
xp:Number
},
achievement:[
{
name:String,
date: String
}
] });
Here is my document
const fluffy = new playerModel({
"name":"nic raboy",
"stats":{
"wins":5,
"losses":10,
"xp":300
},
"achievements":[
{"name":"Massive XP","date" :"25-08-21"},
{"name":"instant loss","date":"24-08-21"}
]
});
however in mongodb atlas its only showing array...and i cant see the objects inside...
SCREENSHOT
Your schema is correct, it seems your input is wrong,
In schema definition you named it achievement, whereas in input document it is achievements. Correct this everything will work as you expected.
Explanation
The schema is expecting achievement and you inserted achievements, that is why it is shown as an empty array in the database. To avoids this kind of typos in the future, use the required flag.
const playerSchema = new mongoose.Schema({
name: String,
stats: {
wins: Number,
losses: Number,
xp: Number
},
achievements: [
{
name: {
type: String,
required : true,
},
date: {
type: String,
required : true, // required informs for missing fields
}
}
]
})
Refer this link for more on validation
You can use insertMany see the doc here.
Of course, a while loop should work find calling multiple times insertOne, though I advise you to use the insertMany() method.
If you're new to MongoDB, I strongly encourage you to have a look at MongoDB University's MongoDB basics course as well as the MongoDB for JavaScript Developers course.

Mongodb Many to Many relationship

I am working on a webapp built on mean.js and part of the app is projects that have tags, similar to a blog site that has tags for each blog entry.
I have searched everywhere for a good example/tutorial explaining the best way to create a many to many relationship using mongodb/mongoose, but I can't seem to find anything.
Each project can have multiple tags and I want the users to be able to find all projects with a specific tag.
I would really appreciate any suggestions/examples on the correct way to achieve this. TIA
Keep an array of id's in both collections. For example:
coll1:
{
_id: ObjectId("56784ac717e12e59d600688a"),
coll2_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
coll2:
{
_id: ObjectId("56784ac717e12e59d600677b"),
coll1_ids: [ObjectId("..."), ObjectId("..."), ObjectId("..."), ...]
}
The advantage of this approach is that when you have a document from either collection, you can immediately pull all associated documents from the opposite collection simply by doing
obj = db.coll1.findOne({})
db.coll2.find({_id: {$in: obj['coll2_ids']}}) # returns all associated documents
and vice-versa.
For many-to-many relationship, I have found mongoose-relationship really useful.
Here's a sample how you would solve it using mongoose-relationship module:
// In TagsModel.js file ----------------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var relationship = require("mongoose-relationship");
var tagSchema = new schema({
projects: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Project',
childPath: 'tags'
}],
....
});
tagSchema.plugin(relationship, {
relationshipPathName: 'projects'
});
var tagModel = mongoose.model('Tag', tagSchema);
--------------------------------------------------------------------
// In ProjectModel.js file -----------------------------------------
var mongoose = require('mongoose');
var schema = mongoose.Schema;
var Projects = new Schema({
tags : [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag'
}],
...
});
var tagModel = mongoose.model('Project', tagSchema);
With this model structure, you will be able to find projects by specific tag and tags by project.
It seems you just want to have an Array of tags within your Project schema.
var Tags = new Schema({
name : String
});
var Projects = new Schema({
tags : [String] // When you save a Tag just save the Name of it here
});
// This way you could easily look it up
// Assuming "name" was passed in as a param to express
Projects.find({ 'tags' : req.params.name }) // voila!
There are other ways as well (such as saving ObjectIds).
This method would be easier if you think Tag names could change often but once again if a Tag is "deleted" you'd have to go look through all Projects to remove that ObjectId
tags : [{ type: Schema.ObjectId, ref : 'Tags' }]
Basically the gist of this is that you are saving the a String or ObjectId reference (of the name) in an Array of "tags" within your Project model.
Just remember when you're going to Edit Tag names / Delete a tag / etc, you'll want to go through and update (if they are saved as Strings) / remove those tags from any Projects that have it in their array.

Mongoose find with default value

I have a mongoose model: (With a field that has a default)
var MySchema= new mongoose.Schema({
name: {
type: String,
required: true
},
isClever: {
type: Boolean,
default: false
}
});
I can save a model of this type by just saving a name and in mongoDB, only name can be seen in the document (and not isClever field). That's fine because defaults happen at the mongoose level. (?)
The problem I am having then is, when trying to retrieve only people called john and isClever = false:
MySchema.find({
'name' : 'john',
'isClever': false
}).exec( function(err, person) {
// person is always null
});
It always returns null. Is this something related to how defaults work with mongoose? We can't match on a defaulted value?
According to Mongoose docs, default values are applied when the document skeleton is constructed.
When you execute a find query, it is passed to Mongo when no document is constructed yet. Mongo is not aware about defaults, so since there are no documents where isClever is explicitly true, that results in empty output.
To get your example working, it should be:
MySchema.find({
'name' : 'john',
'isClever': {
$ne: true
}
})

MongoDB Update Statement Set Value From Other Collection

I have a background in SQL server and I'm migrating some data over to MongoDB for a proof of concept project using Mongoose as the driver. I want to add a DB ref to another collection. A basic example of what I'm trying to do here:
Project Schema:
{
projectNo: String,
projectTitle: String
}
Document Schema:
{
projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'project'},
projectNo: String,
documentTitle: String
}
I want to add a new key to the Document schema that references the _id of the project collection. If I was doing this in SQL Server I'd do this:
UPDATE document
SET projectId = project._id
FROM project
WHERE document.projectNo = project.projectNo
Can anyone point me in the right direction? Thank you!
Kevin
Ok I had to take off my SQL Server hat and think about it in JS terms. For each project in project I want to update these collections with the project _id. So I came up with this:
var proj = db.proj.find(); //load all the projects
proj.forEach(function(proj){ //loop through each one and update matching records in the collection
db.forecast.update(
{
"projectNo" : proj.projectNo,
"$isolated" : true
},{
$set: {
projId : proj._id
}
}, {
"multi" : true
});
});
I'm sure there is another way to do this, I'm very open to suggestions.

Mongoose Changing Schema Format

We're rapidly developing an application that's using Mongoose, and our schema's are changing often. I can't seem to figure out the proper way to update a schema for existing documents, without blowing them away and completely re-recreating them from scratch.
I came across http://mongoosejs.com/docs/api.html#schema_Schema-add, which looks to be right. There's little to no documentation on how to actually implement this, making it very hard for someone who is new to MongoDB.
I simply want to add a new field called enabled. My schema definition is:
var sweepstakesSchema = new Schema({
client_id: {
type: Schema.Types.ObjectId,
ref: 'Client',
index: true
},
name: {
type: String,
default: 'Sweepstakes',
},
design: {
images: {
type: [],
default: []
},
elements: {
type: [],
default: []
}
},
enabled: {
type: Boolean,
default: false
},
schedule: {
start: {
type: Date,
default: Date.now
},
end: {
type: Date,
default: Date.now
}
},
submissions: {
type: Number,
default: 0
}
});
Considering your Mongoose model name as sweepstakesModel,
this code would add enabled field with boolean value false to all the pre-existing documents in your collection:
db.sweepstakesModel.find( { enabled : { $exists : false } } ).forEach(
function (doc) {
doc.enabled = false;
db.sweepstakesModel.save(doc);
}
)
There's nothing built into Mongoose regarding migrating existing documents to comply with a schema change. You need to do that in your own code, as needed. In a case like the new enabled field, it's probably cleanest to write your code so that it treats a missing enabled field as if it was set to false so you don't have to touch the existing docs.
As far as the schema change itself, you just update your Schema definition as you've shown, but changes like new fields with default values will only affect new documents going forward.
I was also searching for something like migrations, but didn't find it. As an alternative you could use defaults. If a key has a default and the key doesn't exist, it will use the default.
Mongoose Defaults
Default values are applied when the document skeleton is constructed. This means that if you create a new document (new MyModel) or if you find an existing document (MyModel.findById), both will have defaults provided that a certain key is missing.
I had the exact same issue, and found that using findOneAndUpdate() rather than calling save allowed us to update the schema file, without having to delete all the old documents first.
I can post a code snippet if requested.
You might use mongo shell to update the existing documents in a specific collection
db.SweeptakesModel.update({}, {$set: {"enabled": false}}, {upsert:false, multi:true})
I had a similar requirement of having to add to an existing schema when building an app with Node, and only found this (long ago posted) query to help.
The schema I added to by introducing the line in the original description of the schema and then running something similar to the following line, just the once, to update existing records:
myModelObject.updateMany( { enabled : { $exists : false } }, { enabled : false } )
'updateMany' being the function I wanted to mention here.
just addition to what Vickar was suggesting, here Mongoose Example written on Javascript (Nodejs):
const mongoose = require('mongoose');
const SweeptakesModel = mongoose.model(Constants.SWEEPTAKES,sweepstakesSchema);
SweeptakesModel.find( { enabled : { $exists : false } }).then(
function(doc){
doc.enabled = false;
doc.save();
}
)

Resources