How do I update an embedded document with a nested hierarchy? - javascript

I have an embedded document that needs to be updated. I have managed to get everything working except for one scenario: I am unable to get an embedded document with a nested hierarchy to get updated. Here is an example of what I'm attempting to do:
console.log('Phone type: ' + req.body.phone.type); // prints, e.g., 'Phone type: Work'
console.log('Phone #: ' + req.body.phone.number); // prints, e.g., 'Phone #: 555-555-5555'
var updateData = {
"user.$.contact_info": {
email: req.body.email,
phone: {
type: req.body.phone.type,
number: req.body.phone.number
}
}
};
Group.update(
{ "user._id" : req.params.user_id },
{ $push : updateData },
function(err,data) {
console.log('Success!'); // I see the "Success!" message every time I save a record
}
);
Schema:
var contactInfoSchema = mongoose.Schema({
created : {
type: Date,
default: Date.now
},
email : String
phone: {
type: String,
number: String
}
});
var userSchema = mongoose.Schema({
created : {
type: Date,
default: Date.now
},
contact_info : [contactInfoSchema]
});
var GroupSchema = mongoose.Schema({
created : {
type: Date,
default: Date.now
},
users : [userSchema]
});
What I find is that I can create records, but only the email address is stored, not the phone information. When inspecting the console I can see that the phone type and phone number info is being sent, but it just isn't updating in the database. What am I doing wrong?

The problem in in the "contactInfoSchema" with the use of the "type" keyword. You need this:
var contactInfoSchema = mongoose.Schema({
created : {
type: Date,
default: Date.now
},
email : String
phone: {
"type": { "type": String },
number: String
}
});
So basically mongoose is confused since you tried to call a "sub-document" field "type", and it thinks that is the data "type" for the field "phone". Declared as above all works fine.

Related

How to use a mongoose model in another model?

I have the two following models. In the user model I want to use an array of Requests and in Request Model I want to use User as an attribute(without the password). How can I do it?
var userSchema = new Schema({
cartaoCidadao: {
type: String,
required: true,
index: {
unique: true,
},
match: /[0-9]{8}/,
},
password: { type: String, required: true },
role: { type: String },
estado: { type: String, enum: ["Infetado", "Suspeito"] },
});
var requestSchema = new Schema({
encaminhado: { type: String },
pessoaRisco: { type: String },
trabalhoRisco: { type: String },
estadoPedido: { type: String },
resultado: { type: String },
});
You can use the schema you defined as a type itself:
var userSchema = new Schema({
// ...
requests: {
type: [requestSchema] // this property type is: array of requests
}
// ...
});
If both models are stored in database and you probably want to go for their association. You can reference one model from another. (see the answer of Muhammad Lahin)
Then you query your parent model and associate the children models with it (https://mongoosejs.com/docs/populate.html)
And here is an example of how you can exclude some fields during the population:
https://mongoosejs.com/docs/populate.html#query-conditions
It will be something like:
User.
find(/* some query */).
populate({
path: 'requests',
select: 'fieldToSelect1 fieldToSelect2' // You can control which fields to include
}).
exec();
you can do something like this
var userSchema = new Schema({
requests: [
{
type: Schema.Types.ObjectId,
ref: "Request",
}
]
});
var requestSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "User"
}
})
you can set the type of the user in the request model like this
type: schema.Types.ObjectID,
ref: 'users'
which users id schema of users and when you want to send the whole user data in the response, use populate and in populate you can omit the password too
now if you see the mondodb database in the request database the whole user object is not saved and only the user_id is saved so you dont have access to the user. you have to send another request to get the user data from the gived user_id which is not good. the solution is you set the schema as I said and when you are sending a response which have the data of the request use populate to add the userdata to the response
res.json({request: requests.find(//options to find a specified request).populate('users','name')})
first argument is the model which is users and the second argument is the wanted field of the users model for example this will return the name of the user. you can add more arguments to add all of them except the password
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
user: {
type: mongoose.Types.ObjectId,
ref: 'User'
}
});

Undefined Object with Fetching Schema less Data from MongoDB using NodeJS and Mongoose

I'm trying to do some models in mongoDB using mongoose as a schemaless during working saving data works fine but when i try to fetch the data there is problem
this is the Model Creation code :
const TemplateSchema = new Schema({
any: {},
blog: {
type: Schema.Types.ObjectId,
ref:'blogs'
},
user:{
type: Schema.Types.ObjectId,
ref:'users'
},
date:{
type: Date,
default: Date.now
}
},{ strict: false });
Saving Code :
let newTemplate = {
tempbody: { name: 'Laith', age: 26 },
blog: blog.id,
user: req.session.userid,
}
//console.log(inputObject);
new Template(newTemplate).save()
.then((template) => {
res.redirect(`/blogs/posts/${req.params.blogid}`);
});
Fetching Code :
Template.findOne({_id: req.params.templateid}).populate('blog').then(template =>{//To Fetch ObjectID for Blog from DB
if(template && template.blog.blogID == req.params.blogid) {
console.log(template.tempbody);
res.render('blogs/addPostTemplate', {
template: template,
blogid: req.params.blogid,
templateid: req.params.templateid
});
} else {
res.redirect(`/blogs/posts/${req.params.blogid}`);
}
});
The result suppose to be :
The Object tempbody but it gives always undefined
and if i try to fetch template._id it works fine
and if i print template as full object it also gives the result
and sometimes gives : (node:16840) [DEP0079] DeprecationWarning: Custom inspection function on Objects via .inspect() is deprecated
Any help is appreciated.

Mongodb findOneAndUpdate function is inserting new document

I just need to update serviceActiveFlag status whenever i make an update api call.After an update api call,i can see a new document with empty vehicle array is created as shown below.
_id:59c76073c11d3929148f500f
vehicle:Array
_v:0
id field will override upon every put api call.
Can some one help me to resolve this issue?
schema
var localTransportSchema = new Schema({
name: { type: String, required: false, trim: true },
contact: {
addressLine1: { type: String, required: false },
serviceActiveFlag: { type: String, required: false, enum: ['Y', 'N'] },
},
vehicle: [{
vehicleType:{ type: String, required: false, enum: ['sedan', 'hatchback', 'suv', 'mpv', 'luxury'] },
}]
});
module.exports.accomodationModel = mongoose.model(collection, localTransportSchema);
controller
var updates = {
$set: {
"contact.addressLine1": req.body['addressLine1'],
"contact.serviceActiveFlag": req.body['serviceActiveFlag']
}
};
transportModel.findOneAndUpdate({ "name": req.body['providerName']},
updates, { returnOriginal: false, upsert: false }, function (err, doc) {
if (err) {
logger.error("Error while updating record : - " + err.message);
return res.status(409).json({
"Message": "Error while updating transport details for provider " + req.body['providerName'] + " in transport details table"
});
} else if (doc === null) {
logger.error("Error while updating record in transport details : - unable to update database");
return res.status(409).json({
"Message": "Error while updating transport details for provider " + req.body['providerName'] + " due to " + err.message
});
}
});
dbModel.findOneAndUpdate() of mongoose insert a new document only when your filter query(where claus) failed to find the existing document i.e there is no matching document exist in your collection.
So mongoose will insert the new document to the collection. This works similar to upsert:true of update()
Ref :Official docs about findOneAndUpdate

why this mongoose text search doesn't work

Even though I have similar data in collection, result always comes back empty. I have ingredients in array[object]. That means qty:1 unit:spoon ing:salt and I want only ing to be searchable. I have mongoose debug enabled and it shows:
Mongoose: recipes.ensureIndex({ 'Ingridients.ing': 'text' }) { background: true, safe: undefined }
This is the code:
var RecipesSchema = new mongoose.Schema({
Name: {type: String, required: true},
Desc: String,
Ingridients: [{qty:Number, unit:String, ing:{ type: String, index: "text" } }],
created: { type: Date }
});
Recipe.find({ $text : { $search : "salt egg potato peper" } },
{ score : { $meta: "textScore" }
})
.sort({ score : { $meta : 'textScore' } })
.exec(function(err, recipe) {
// callback
if(err) console.log(err);
res.render('list', { recipes: recipe });
});
after i tried on localhost and it worked, switched on mongolab.com by dropping and recreating collection and indexes again. it was not able to index correctly
ERR: planner returned error: need exactly one text index for $text query', code: 17007

Relational database design to mongoDB/mongoose design

I have recently started using mongoDB and mongoose for my new node.js application. Having only used relational databases before I am struggling to adapt to the mongoDB/noSQL way of thinking such as denormalization and lack of foreign key relationships. I have this relational database design:
**Users Table**
user_id
username
email
password
**Games Table**
game_id
game_name
**Lobbies Table**
lobby_id
game_id
lobby_name
**Scores Table**
user_id
game_id
score
So, each lobby belongs to a game, and multiple lobbies can belong to the same game. Users also have different scores for different games. So far for my user schema I have the following:
var UserSchema = new mongoose.Schema({
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
});
So my question is, how would I go about structing the relational design into mongoDB/mongoose schemas? Thanks!
EDIT 1
I have now tried to create all the schemas but I have no idea if this is the right approach or not.
var UserSchema = new mongoose.Schema({
_id: Number,
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
scores: [{ type: Schema.Types.ObjectId, ref: 'Score' }]
});
var GameSchema = new mongoose.Schema({
_id: Number,
name: String
});
var LobbySchema = new mongoose.Schema({
_id: Number,
_game: { type: Number, ref: 'Game' },
name: String
});
var ScoreSchema = new mongoose.Schema({
_user : { type: Number, ref: 'User' },
_game : { type: Number, ref: 'Game' },
score: Number
});
Mongoose is designed in such a way that you can model your tables relationally with relative ease and populate relational data based on the ref you defined in the schema. The gotcha is that you need to be careful with populating. If you populate too much or nest your populations you will run into performance bottle necks.
Your approach in Edit 1 is largely correct however you usually don't want to populate a remote ref based on a Number or set the _id of a model to a Number since mongo uses it's own hashing mechanism for managing the _id, this would usually be an ObjectId with _id implied. Example as shown below:
var ScoreSchema = new mongoose.Schema({
user : { type: Schema.Types.ObjectId, ref: 'User' },
game : { type: Schema.Types.ObjectId, ref: 'Game' },
score: Number
});
If for some reason you need to maintain a number id for your records consider calling it uid or something that won't conflict with mongo / mongoose internals. Good luck!
First of all, you are hitting on some good points here. The beauty of Mongoose is that you can easily connect and bind schemas to a single collection and reference them in other collections, thus getting the best of both relational and non-relational DBs.
Also, you wouldn't have _id as one of you properties, Mongo will add it for you.
I've made some changes to your schemas using the mongoose.Schema.Types.ObjectId type.
var UserSchema = new mongoose.Schema({
username: {
type: String,
index: true,
required: true,
unique: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
scores: [{ type: Schema.Types.ObjectId, ref: 'Score' }]
});
var GameSchema = new mongoose.Schema({
name: String
});
var LobbySchema = new mongoose.Schema({
_game: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Game'
},
name: String
});
var ScoreSchema = new mongoose.Schema({
_user : {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
_game : {
type: mongoose.Schema.Types.ObjectId,
ref: 'Game'
},
score: Number
});
This will allow you to query your database and populate any referenced collections and objects.
For example:
ScoreSchema.find({_id:##userIdHere##})
.populate('_user')
.populate('_game')
.exec(function(err, foundScore){
if(err){
res.send(err)
} else {
res.send(foundScore)
}
}
This will populate the related user and game properties.
As you edited the post, I think it would be good. At least not bad :)
Check Mongoose Query Population. It's very useful to get related data.
var mongoose = require('mongoose'),
ObjectId = mongoose.Schema.Types.ObjectId
// code, code, code
function something(req, res) {
var id = req.params.id
// test id
return Lobby.findOne({_id: new ObjectId(id)})
.populate('_game')
.exec(function(error, lobby) {
console.log(lobby._game.name);
});
}
Two ways (that I know of). You store an id (that is indexed) and once you query the first table, you then query the second table to grab info from that, as there are no joins. This means that if you grab say, user id's from one table, you will then need to make multiple queries to the user table to get the user's data.
The other way is to store it all in one table, even if it's repetitive. If all you need to store is for example, a user's screen name with something else, then just store it with the other data, even if it's already in the user table. I'm sure others will know of better/different ways.

Categories