Embedded document not saved to its collection - node.js

It seems as if my embedded documents are not being saved to their respective collections. Here's my model:
var County = new Schema({
_id : Schema.ObjectId,
name : String,
biggestCity : String
});
var Country = new Schema({
_id : Schema.ObjectId,
name : String,
counties : {type: [County], ref: "County"}
});
var Continent = new Schema({
_id : Schema.ObjectId,
countries : {type: [Country], ref: "Country"},
});
...and here's the code that I use to save them to MongoDB:
var continentModel = mongoose.model("Continent");
var continent = new continentModel();
country.name = name;
var countryModel = mongoose.model("Country");
var countyModel = mongoose.model("County");
for (var i = 0; i < req.body.countries.length; i++) {
var country = new countryModel();
country.name = req.body.countries[i].name;
for (var j = 0; j < req.body.countries[i].counties.length; j++) {
var county = new countyModel();
county.name = req.body.countries[i].counties[j].name;
county.biggestCity = req.body.countries[i].counties[j].biggestCity;
countries.counties.push(county);
}
continent.countries.push(country;
}
continent.save();
If I do a db.continents.find(), a document comes back with all the properties (including country and county) populated.
But if I do a db.counties.find() or a db.countries.find(), nothing comes back. So it seems as if the County and Country documents are not being saved to the DB to their respective collections, but rather saved to the Continent collection as regular properties instead (not embedded documents).
What am I doing wrong?

This may be too simple, but you are only calling continent.save() and never calling county.save() or country.save() at the end of the for loops. Is that just an omission or does that fix the problem. If it is an omission, please see my note about posting the output.

Related

how to query mongoDb with array of boolean fields?

My mongoose model schema looks like -
{
email: { type: String},
Date: {type: Date},
isOnboarded: {type: Boolean},
isVip: {type: Boolean},
isAdult: {type: Boolean}
}
In my frontend I have 3 checkboxes for "isVip", "isOnboarded" and "isAdult" options. If they are checked I'm adding them to an array, which I'll pass to the server. Let's say if "isVip" and "isAdult" are checked, I will pass [isVip, isAdult] in post api to server. Now how can I write a query to get all the documents with the fields in array as true i.e in above example how can I retrieve all docs with {isVip: true, isAdult:true}
I'm having trouble because the array values keep changing, it can be only one field or 3 fields. I couldn't find a way to give condition inside mongoose query.
User.find(
{ [req.query.array]: true},
{ projection: { _id: 0 } }
)
User is my mongoose model.
I want something like this (documents with the value 'true' for the fields given in the array) and 'req.query.array' is the array with field names I passed from frontend.
You have to create your object in JS and pass then to mongo in this way:
var query = {}
if(isVip) query["isVip"] = true;
if(isOnboarded) query["isOnboarded"] = true;
if(isAdult) query["isAdult"] = true;
And then you can use the mongoose method you want, for example:
var found = await model.find(query)
And this will return the document that matches the elements.
Also, note that this is to create and object to be read by the query, so you can use this into an aggregation pipeline or whatever you vant
Check the output:
var query = {}
query["isVip"] = true;
query["isOnboarded"] = true;
query["isAdult"] = true;
console.log(query)
Is the same object that is used to do the query here
{
"isVip": true,
"isOnboarded": true,
"isAdult": true
}
Also, to know if your post contains "isVip", "isOnboarded" or "isAdult" is a javascript question, not a mongo one, but you can use something like this (I assume you pass a string array):
var apiArray = ["isVip","isAdult"]
var isVip = apiArray.includes("isVip")
var isAdult = apiArray.includes("isAdult")
var isOnboarded = apiArray.includes("isOnboarded")
console.log("isVip: "+isVip)
console.log("isAdult: "+isAdult)
console.log("isOnboarded: "+isOnboarded)

String value from mongoose document weird behaviour adding in a variable

I have a mongo document called New which has a sub document called hashtags which has th value name which is a string. I am trying to add all the hashtags belongs to a new in a variable but is adding extra characters (seems that the name string value is not well decoded from the bson or something like that).
New document:
var newSchema = new Schema({
...
hashtags: [{
type : mongoose.Schema.ObjectId,
ref: 'Hashtag'
}]
});
Hashtag document:
var mongoose = require('mongoose')
var Schema = mongoose.Schema
var hashtagSchema = new Schema({
color: {
type: String,
default: '#000000'
},
name: {
type: String
}
});
var hashtag = mongoose.model('Hashtag', hashtagSchema )
module.exports = hashtag
Snniped testing code:
docs.forEach(noticia => {
if(noticia.hashtags.length > 0){
for(i in noticia.hashtags){
if(noticia.hashtags[i] && noticia.hashtags[i].name){
text += '#' + noticia.hashtags[i].name.replace(/\s/g,'') + ' '
}
}
}
})
console.log(text)
Console output:
#Lula #toBSON #_cast #_markModified #_registerAtomic #$__getAtomics #hasAtomics #_mapCast #push #nonAtomicPush #$pop #pop #$shift #shift #pull #splice #unshift #sort #addToSet #set #toObject #inspect #indexOf #pull
I have tried to apply noticia.hashtags[i].name.replace(/\s/g,'').toString() :
docs.forEach(noticia => {
if(noticia.hashtags.length > 0){
for(i in noticia.hashtags){
if(noticia.hashtags[i] && noticia.hashtags[i].name){
text += noticia.hashtags[i].name.toString() + ' '
}
}
}
})
Console output
LulatoBSON_cast_markModified_registerAtomic$__getAtomicshasAtomics_mapCastpushnonAtomicPush$poppop$shiftshiftpullspliceunshiftsortaddToSetsettoObjectinspectindexOfpull
How I have to decode this string value?
The for...in statement iterates over all non-Symbol, enumerable properties of an object.
var string1 = "";
var object1 = {a: 1, b: 2, c: 3};
for (var property1 in object1) {
string1 += object1[property1];
}
console.log(string1);
// expected output: "123"
You are iterating over the properties of noticia.hashtags

MongoDB creating, finding from and changing an array

Here's what I want to achieve in mongodb but as a javascript example.
var array = [];
//Initiating an array
for(var i = 0; i < 30; i++) {
array[i] = 0;
}
//Changing a value at an index
array[14] = 1;
//Getting a value at an index
console.log(array[4]);
//Swapping Two Variables.
var temp = array[14];
array[14] = array[12];
array[12] = temp;
So far I can set a schema
var schema = new mongoose.Schema({
array: {Number: Number, Value: Number}
});
I can Initiate it later on
schema.statics.name = function(cb) {
var new = new Array({
{Number: 1, Value: 0},
{Number: 2, Value: 0},
...
{Number: 30, Value: 0}
});
new.save();
I can loop through them all
c.user.array.forEach(function (element) {
console.log(element.Number);
console.log(element.Value);
})
But I get stuck with finding a single value or setting/switching them.
It seems overly complicated for something so simple in a programming language; ive been trying for the last few hours and there so many {} and $'s that its making my head hurt.
From what I can find I should be using .find() but then there no examples of how the stuff in the schema is laid out.
Schema:
array: [{value: Number}]
Can be initialised as many times as you want:
backpack: [{value: 0}, {value:0}, {value:0}]
and can be accessed like
.array[1].value
more info on sub docs and children
Thanks to Tomalak

Sub-document saving is not updating parent

I'm trying to learn some relationship mechanics in mongoose. I have two models, a parent and a child:
var childrenSchema = new Schema({
name : String,
date : {type : Date, default: Date.now},
attribute1 : String,
attribute2 : String,
})
var parentSchema = new Schema({
name: String,
children: [childrenSchema]
})
exports.parent = mongoose.model('Parent', parentSchema);
exports.children = mongoose.model('Person', childrenSchema);
I will create a parent object in an initial call, and send an asynchronous call to an api which fetches children information based on the child's name. While that async call is out, I return the parent as is, because the user doesn't need to see the children's information immediately.
var Parent = require('schema.js').parent;
var Child= require('schema.js').children;
function addParent(p){
var parent = new Parent();
parent.name = p.result.name;
var child = new Child();
child.name = p.result.childname;
parent.children.push(child);
getChildDetails(child); // Async function to get children info..
parent.save(); //Save the parent so the information we return is persisted.
return parent; //Children probably not fully populated here. Not a problem.
}
function getChildDetails(child){
var promiseapi = require('mypromiseapi');
promiseapi.fetch('childinfo',child.name).then(function(result){
child.attribute1 = result.attribute1;
child.attribute2 = result.attribute2;
}).then( function(){
child.save(); // I expect the parent's information to be updated.
});
}
However, I am now in a little bit of a desync issue. Parent object has the single child on it, but only name and some mongoose specific information is populated (objectId). A child table is created also, with the child information fully populated, and it has the same objectID as the child that's affixed to the parent.
Why does the object that is affixed to the parent not get updated when I save it independently elsewhere in my code?
I solved it by using Populations (http://mongoosejs.com/docs/populate.html) instead of the pure schemas. The new schema models look like this:
var childrenSchema = new Schema({
name : String,
date : {type : Date, default: Date.now},
attribute1 : String,
attribute2 : String,
})
var parentSchema = new Schema({
name: String,
children: [{type: Schema.Types.ObjectId, ref:'Child'}]
})
And the new addParent method looks like this:
function addParent(p){
var parent = new Parent();
parent.name = p.result.name;
var child = new Child();
child.name = p.result.childname;
parent.children.push(child._id);
child.save();
getChildDetails(child); // Async function to get children info..
parent.save(function(err,result){
Parent.populate(result,{path:'children'},function(err,resultparent){
return(result);
});
}); //Save the parent so the information we return is persisted.
}

Mongoose - embedded documents are saved as string

In my node app I have designation model with following Schema.
var DesignationSchema = new Schema({
_id : ObjectId,
designation : String
});
And this is embedded in users Schema.
var UserSchema = new Schema({
fname:String,
lname :String,
email :String,
password :String,
designations : [DesignationSchema]
});
User can select one / many designations from select list. Below is my jade implementation for creating select list;
select(name="designations")
- for(var i = 0; i < designations.length; i++) {
option(value="#{designations[i]}", name="designations[#{i}]") #{designation.designation}
- }
But these values are stored as string array instead of DesignationSchema array:
{
designations : ["{designation:'Some Value' , _id : __DESIGNATION__ID}", "{designation:'Some Value' , _id : __DESIGNATION__ID}"]
}
UPDATE
I am pushing designations in following way in expressjs :
var newUser = new User(user.info);
if(user.designations && user.designations.length){
for(var i =0; i < user.designations.length; i ++) {
newUser.designations.push(user.designations[i]);
//I have tried JSON.parse(user.designations[i]) but it raises error that says : 'Unexpected token d'
//user.designations[i] object is something like : {designation:'Some Content', _id : __DESIGNATION__ID}
//IMPORTANT : the __DESIGNATION__ID is not inside single (') or double (") quotes. And I think this is the reason why JSON.parse is raising the error.
}
}
You can ommit the _id from DesignationSchema as Mongoose will add that for you itself. Make sure you JSON.stringify the data you POST/PUT/etc so that you are left with something you can parse.
You may also need to specify the content-type as application/json when you send the data back. This should allow you to avoid JSON.parse in your endpoint.

Resources