How to insert an array of ids with create function in node - arrays

I Have defined this mongoose schema in node
`const bookingSchema = new mongoose.Schema({
tour: [
{
type: mongoose.Schema.ObjectId,
ref: 'Tour',
required: [true, 'Booking must belong to Tours!'],
},
],
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: [true, 'Booking must belong to User!'],
},
price: {
type: Number,
required: [true, 'Booking must have a price'],
},
createdAt: {
type: Date,
default: Date.now(),
},
paid: {
type: Boolean,
default: true,
},
});
bookingSchema.pre(/^find/, function (next) {
this.populate('user').populate({
path: 'tour',
select: 'name',
});`your text`
});
Then when i try to create a booking i use the create function
await Booking.create({ tour, user, price });`
the tour param is an array of ids
but i get this error
"Booking validation failed: tour.0: Cast to [ObjectId] failed for value "["5c88fa8cf4afda39709c295a,5c88fa8cf4afda39709c2951"]" (type string) at path "tour.0"
I can do it with only one tour if the tour property wasnt an array of objects. My main issue is that a booking can be related to many tour objects in my database
*edit after some modifications the error become
"Unexpected token u in JSON at position 0"
My front end code(angular) is this. I am making a get request to my backend(node)
createBookingCheckout(params: any): Observable<any> {
console.log('serv');
return this.http.get<any>(`${CREATE_BOOKING_CHECKOUT}`, {
params,
withCredentials: true,
});
}
So i pass the ids as query param
And this is the backend where i am trying to create the booking
exports.createBookingCheckout = catchAsync(async (req, res, next) => {
const { order, tour: strTour } = req.query;
const user = req.user._id;
const parsedOrder = JSON.parse(order);
const tour = JSON.parse(strTour);
console.log(tour);
// const tours = await tour.forEach((id) => {
// Tour.find({ id });
// });
// console.log(tours);
let price = 0;
parsedOrder.forEach(
(obj) => (price = price + obj.price_data.unit_amount * obj.quantity)
);
if (!parsedOrder && !user) return next();
await Booking.create({ tour, user, price });
res.redirect(req.originalUrl.split('?')[0]);
res.status(200).json({
status: 'success',
});
});

I think the quick solution to this is that whenever you are going to add more than a tour you should create an array push all tours IDs to the array and then add the tourArray to the new Booking that you are going to create, and when you are going to update an existing Booking, you should repeat the process.
When you are going to create a new Booking document.
Scenario 1 : with one Tour
You just pass the id of the tour {always check if the tour exist}, that's obvious and simple, at the same time mongo is going to create a table referring to the model as Tour is defined as many within your Booking model
Scenario 2 : with more than one Tour
You need to define all the Tour's IDs in an array within the body
{
"tour": [ "id-1", "id-2", ...]
}
Updating a Booking
say that you are going to add a new Tour to an existing Booking,
Booking.findByIdAndUpdate(
{_id: id},
{
// ! here all your other data
$push: {tour: new_tour}
},
// this is just to return the updated booking
{new: true, runValidators: true}
}
=> the operator $push in mongoose is what you need to push into the existing array or tour that's already in your existing Booking
The new_tour is coming from your req.body
as for sure in some cases you are going to delete a Tour ID from a Booking,
=> in that case you are going to use the operator $pull

Related

MongoDB updating empty field after initial insert

In the Mongoose schema there is an empty field for objectId of another table. I want to fill that field after the User object is created, as the school object is made after user object is.
User:
name
age
dob
company (this is object id from company table)
Initially I thought using the FindOneandUpdate() function would do this but I have had no luck.
Anyone any idea's?
company.component.ts
joinCompany(i){
console.log(this.companyArr[i]._id);
this.authService.addmoreUser(this.companyArr[i]._id).subscribe(data =>{
if(data.success){
this.router.navigate(['dashboard']);
}
else{
this.flashMessages.show(data.msg,{cssClass: 'alert-
danger',timeout:3000});
}
})
}
auth.service.js
addmoreUser(id){
id = {companyid:id};
console.log(id);
let headers = new Headers();
headers.append('Authorization', this.authToken);
headers.append('Content-Type','application/json');
return this.http.put('http://localhost:3000/users/pushCompanyintoUser',id,{headers:headers})
.map(res => res.json());
}
companies.js - CRUD file
router.put('/pushCompanyintoUser', passport.authenticate('jwt', {session:false}), (req, res, next) => {
console.log(req.body);
User.findByIdAndUpdate(req.user._id,{companyid:req.body} , function(err,user) {
if (err) throw res.json({success:false, msg:'Failed to Update user!'});
else res.json({success: true, msg:'User Updated!!'})
});
});
That is from the angular frontend being passed to server and then trying to insert to db.
DB Schema
const UserSchema = mongoose.Schema({
fullname:{ type: String},
email:{ type:String, required:true},
username:{ type:String, unique:true,required:true },
companyid:{ type:mongoose.Schema.Types.ObjectId, ref: 'Company', required:false}, <== (This is the where I am trying to submit to, in an object thats already made)
dob:{ type:String, required:true },
addressline:{ type:String, required:true },
town:{ type:String,required:true },
county:{ type:String,required:true},
country:{ type:String, required:true},
phone:{ type:Number, required:true },
password:{ type:String, required:true }
});
The User object is inserted already just without a companyid, how do I go about adding the companyid after.

Output all documents in mongoose

I am using mongoose ODM and have a schema which looks like this:
var banSchema = new Schema({
userid: { type: String, required: true, unique: true },
name: String,
groupid: String,
reason: String,
timestamp: Date
});
I want to output every single user id from all documents in the collection. I am using this query to obtain the userid objects. However I cannot seem to get the full list automatically. I have to manually enter the object number as seeen below:
bot.onText(/\/sync/i, function (msg) {
var fromId = msg.from.id;
var chatId = msg.chat.id;
if (fromId == config.sudo) {
console.log('Sudo Confirmed And Authorized!');
Ban.find({}, function (err, obj) {
console.log(obj[0].userid); // Returns A Single ID
console.log(obj[1].toObject().userid); // Returns a different ID
bot.sendMessage(chatId, obj[1].toObject().useridid);
});
} else {
console.log('Someone Is Trying To Act Like Sudo! *sigh*');
bot.sendMessage(chatId, 'You Are Not A Mod!');
}
});
This however does not return a full list of id's as I want. How could I solve this issue?
The code above is for a telegram bot which on a /sync command it should return a message with all ids from the collection.
Telegram bot API Limits
Due to the API limits, the entire output should be in a single message.
var query = Ban.find({}).select({
"userid": 1,
//Add more column fields here
"_id": 0 //Ensures _id is not displayed
});
var arr = [];
query.exec(function (err, results) {
if (err) throw err;
results.forEach(function (result) {
arr.push(result.userid);
// Add more column fields here;
});
var fixedJoin =arr.join("\n");
console.log(fixed);
bot.sendMessage(chatId, 'List\n\n' + fixedJoin);
});
The easiest way to get all values of a particular field across all docs in the collection is to use distinct:
Ban.distinct('userid', function (err, userids) {
// userids is an array containing all userid values in the collection.
// string.join into a single string for the message.
bot.sendMessage(chatId, 'USER IDs\n\n' + userids.join('\n'));
});
Use this syntax
Ban.find({}).
select('userid').
exec(function(err, result) {
//result is array of userid of all document
});
You can use this syntax:
Ban.find({}, 'userid', function(err, users) {
users.forEach(function(user) {
console.log(user);
bot.sendMessage(chatId, 'users \n' + user);
});
})

"User is not authorized" on meanjs

I would like to ask if there is anyone getting the same response on JSON format:
Objectdata: "User is not authorized"headers: (name) {status: 403statusText: "Forbidden"
Scenario:
User A post a product and add comment on the product.
Result: Successful.
User B comment on the same product:
Result: User is not authorized.
The code I'm using to update the product comment is here:
applicationname/`
// Add comment to Product
$scope.comment = function(){
// console.log("name: ",$scope.user);
// console.log("textarea: ",this.commentarea);
var comment = {
name: $scope.product.user.displayName,
text: this.commentarea
};
$scope.product.comments.push(comment);
$scope.product.$update(function() {
console.log('success update');
}, function(errorResponse) {
console.log('success error', errorResponse);
});
};
This is the server side.
'use strict';
/**
* Module dependencies.
*/
var init = require('./config/init')(),
config = require('./config/config'),
mongoose = require('mongoose'),
chalk = require('chalk');
/**
* Main application entry file.
* Please note that the order of loading is important.
*/
// Bootstrap db connection
var db = mongoose.connect(config.db, function(err) {
if (err) {
console.error(chalk.red('Could not connect to MongoDB!'));
console.log(chalk.red(err));
}
});
// Init the express application
var app = require('./config/express')(db);
// Bootstrap passport config
require('./config/passport')();
// Start the app by listening on <port>
app.listen(config.port);
// Expose app
exports = module.exports = app;
// Logging initialization
console.log('MEAN.JS application started on port ' + config.port);
If your Products schema looks like this:
var ProductSchema = new Schema({
created: {
type: Date,
default: Date.now
},
title: {
type: String,
default: '',
trim: true,
required: 'Title cannot be blank'
},
comments: [{
type: String,
default: '',
trim: true
}]
});
And you have restricted your products route in your app/routes/products.server.routes.js file like so:
app.route('/products/:productId')
.get(products.read)
.put(users.requiresLogin, products.hasAuthorization, products.update)
.delete(users.requiresLogin, products.hasAuthorization, products.delete);
Then a non-authorized user cannot add a comment because they can't update the Product record.
You probably want to create a separate CommentsSchema and use the Mongoose ObjectId type to create a one-to-many relationship with the product:
var CommentSchema = new Schema({
product: ObjectId,
content: {
type: String,
default: '',
trim: true,
required: 'Content cannot be blank'
},
})
That will preserve the security of your product and allow non-authorized users to comment, but would require you to do slightly more complex queries to get your comments in your product view.

Populate method in mongoose virtual: nothing is being returned. [duplicate]

I have two mongoose schemas as follow:
var playerSchema = new mongoose.Schema({
name: String,
team_id: mongoose.Schema.Types.ObjectId
});
Players = mongoose.model('Players', playerSchema);
var teamSchema = new mongoose.Schema({
name: String
});
Teams = mongoose.model('Teams', teamSchema);
When I query Teams I would to get also the virtual generated squad:
Teams.find({}, function(err, teams) {
JSON.stringify(teams); /* => [{
name: 'team-1',
squad: [{ name: 'player-1' } , ...]
}, ...] */
});
but I can't get this using virtuals, because I need an async call:
teamSchema.virtual('squad').get(function() {
Players.find({ team_id: this._id }, function(err, players) {
return players;
});
}); // => undefined
What is the best way to achieve this result?
Thanks!
This is probably best handled as an instance method you add to teamSchema so that the caller can provide a callback to receive the async result:
teamSchema.methods.getSquad = function(callback) {
Players.find({ team_id: this._id }, callback);
});

Does Backbone Relational have problems when two separate (but "related") collections contain models with identical Ids?

I'm using Backbone.js, Marionette.js and Backbone-relational on a page that displays lists of users. Each user is identified as either "pending" or "active." A "pending" user is one that has been sent an invitation to join (the party) but has not accepted; "active" are those who have confirmed. The list of pending users is fetched by one API call and the list of active users is fetched by another.
So here's the situation:
A user, let's call him "Miguel", is fetched into the collection of active users. But if a "pending" user who has the same ID as Miguel is fetched to the collection of pending users, Miguel will receive the attribute "Status":"pending". I can verify that Miguel is not fetched into (or passed to the parse function of--) the collection of pending users.
So I'm wondering whether Backbone relational has problems having relationships to two collections whose model IDs sometimes match each other.
To illustrate:
If Collection of active users:
{
Id: 22,
'FirstName': 'Miguel'
}
And Collection of pending users:
{
Id: 22,
'FirstName': 'Betty',
'Status': 'Pending'
}
Then Miguel winds up looking like: {
Id: 22,
'FirstName': 'Miguel',
'Status': 'Pending'
}
Here's the code for my model and collection:
/**
* "NV" is a base class we created for this application. It handles .save(), .toPatchJSON(), tracks changed attrs and other such functions.
*/
/**
* This is the collection of "pending" users
*/
var PendingUsers = NV.Collection.extend({
model: User,
initialize: function(models, options) {
Backbone.Collection.prototype.initialize.call(this,models,options);
this.org = options.org;
},
url: function() {
return "/api/organization/" + this.org.get("Id") + "/invites";
},
parse: function(resp, options) {
// "Miguel" never shows up in this function yet obtains the "Pending" Status property.
return _.map(resp, function(invite) {
return {
Id: invite.Id,
FirstName: invite.FirstName,
LastName: invite.LastName,
Email: invite.Email,
InvitationTime: invite.InvitationTime + "+00:00",
Status: 'Pending'
}
});
}
});
/**
* This is the collection of "active" users
*/
var ActiveUsers = NV.Collection.extend({
model: User,
initialize: function(models, options) {
Backbone.Collection.prototype.initialize.call(this,models,options);
this.org = options.org;
},
url: function() {
return "/api/organization/" + this.org.get("Id") + "/users";
}
});
/**
* The collections of active and pending users are relations of the "Organization" model. This is what's passed to the View that renders the list of users.
*/
var Organization = NV.Model.extend({
defaults: {
Name: ''
},
relations: [
{
type: Backbone.HasMany,
key: 'Users',
relatedModel: User,
collectionType: ActiveUsers,
collectionOptions: function(org) {
return {'org': org };
}
},
{
type: Backbone.HasMany,
key: 'InvitedUsers',
relatedModel: User,
collectionType: PendingUsers,
collectionOptions: function(org) {
return {'org': org };
}
}
],
urlRoot: "/api/organization"
});
return Organization;
/**
* Both pending and active collections build from the User model, so here it is
*/
var User = NV.Model.extend({
schema: {
Name: 'Text',
Email: { validators: ['required', 'email'] },
password: 'Password'
},
defaults: {
FirstName: '',
LastName: '',
Email: ''
},
fetch: function(options) {
this.me = options.me || false;
NV.Model.prototype.fetch.call(this,options);
},
avatar: function(options){
return "/api/user/" + this.get("Id") + "/avatar";
},
url: function() {
if (this.me) return "/api/user/me";
return "/api/user/" + this.get("Id");
}
});
return User;
Let me know if I've failed to include important info. in this question! And thanks!
You are right, Backbone.Relational has a problem with that. Behind the scenes it's keeping a global collection of all models of given type - in this case all Users.
What happens when you fetch is actually Backbone behavior. It recognizes that you are feching into existing collection ( the global one ), finds a model with same ID and attempts to merge data, which results in "Status" attribute on the "Miguel" model.
This is usually a desired functionality as keeping multiple models of same type with same id often indicates some problem or suboptimal data loading.
A quick workaround would be to create additional User 'class', like PendingUser, by extending User.

Resources