Mongoose DBrefs - Cast to ObjectId failed for value - javascript

I have a Team Schema holding details about teams, and a Match Schema to store these teams in. I am trying to make it so that the home/away teams in the Match Schema are references to the Team object. I have put my code below, I'm getting an error when saving the Team but I can't help but feel I have done something wrong with the Schema's or the saving of the Match. Can anyone help?
So far I have the following code:
Team.js extract
var Team = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'name' : { type : String,
validate : [validatePresenceOf, 'Team name is required'],
index : { unique : true }
}
});
module.exports.Schema = Team;
module.exports.Model = mongoose.model('Team', Team);
Match.js extract
var util = require('util');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Team = require('../schemas/Team').Schema;
var Match = new Schema({
'key' : {
unique : true,
type : Number,
default: getId
},
'hometeam' : {
type : Schema.ObjectId,
ref : 'Team'
},
'awayteam' : {
type : Schema.ObjectId,
ref : 'Team'
}
});
module.exports = mongoose.model('Match', Match);
index.js
app.get('/match', function(req, res) {
var key = 1356136550152; // Reading
Team.findByKey(key, function(err, team) {
if(err) {
res.send("An error occured");
}
if(!team) {
res.send("The team does not exist");
}
var match = new Match();
match.hometeam = team;
match.save(function(err) {
if(err) {
util.log('Error while saving Match: ' + util.inspect(err));
res.send("An error occured whilst saving the match");
} else {
res.send("Saved the match");
}
});
});
});
ERROR:
Error while saving Match: { message: 'Cast to ObjectId failed for value "{ name: \'testTeam\',\n _id: 50d500663ca6067226000001,\n __v: 0,\n key: 1356136550152 }" at path "hometeam"',
name: 'CastError',
type: 'ObjectId',
value:
[ { name: 'testTeam',
_id: 50d500663ca6067226000001,
__v: 0,
key: 1356136550152 } ],
path: 'hometeam' }
Error with team._id
Error while saving Match: { [MongoError: E11000 duplicate key error index: testdb.matches.$team.name_1 dup key: { : null }]
name: 'MongoError',
err: 'E11000 duplicate key error index: testdb.matches.$team.name_1 dup key: { : null }',
code: 11000,
n: 0,
connectionId: 8,
ok: 1 }
db.matches.getIndexes()
[
{
"v" : 1,
"key" : {
"_id" : 1
},
"ns" : "testdb.matches",
"name" : "_id_"
},
{
"v" : 1,
"key" : {
"key" : 1
},
"unique" : true,
"ns" : "testdb.matches",
"name" : "key_1",
"background" : true,
"safe" : null
},
{
"v" : 1,
"key" : {
"team.key" : 1
},
"unique" : true,
"ns" : "testdb.matches",
"name" : "team.key_1",
"background" : true,
"safe" : null
}
]

In index.js it should be:
match.hometeam = team._id;
instead of:
match.hometeam = team;
UPDATE
Regarding the new error message, it looks like you have a unique index on the matches collection that refers to fields that don't exist. Drop it in the shell using:
db.matches.dropIndex('team.name_1')

Related

How can I increment & add to array on mongoose

In my database, I have a user table with 2 columns: score & history :
I have another table called card containing some specific information & a card _id.
Each time a specific card is clicked, I would like the '/CardClicked' router to be called for :
Additional (increment) a specific number value (1.5) on score column
Add the cart _id value on the array of history column: (to save, which card the user clicked)
I wanted to have something like that in my database:
users:
{ "_id" : 1, "score" : 6.0, "history" : [ 62k6hf45b0af050fe, 69k5hf45b0af450fg, 65k5hf45b0af450fg, ]}
{ "_id" : 2, "score" : 4.5, "history" : [ 65k5hf45b0af450fg,] }
{ "_id" : 3, "score" : 10.0, "history" : [ 66k5hf45t0af930rp, 67k5hf45b0af450fg, 62k6hf45b0af050fe, 61y5hf884b0af450vb, ] }
But, idk why it doesn't update properly on columns, however i don't have any error, that my code i made it :
My UserSchema.js :
const mongoose = require('mongoose')
const UserSchema = new mongoose.Schema({
...
score: {
type: Number,
},
history: {
type: [String]
},
createdAt: {
type: Date,
default: Date.now,
},
})
module.exports = mongoose.model('User', UserSchema)
My router.js :
app.post('/CardClicked', async function (req, res){
console.log("User ID "+req.user._id, "Card ID : "+req.body._id);
try{
const condition = { _id: req.user._id };
//the req.body._id contain the card id from my front to send that to router
const putPostID = { $addToSet: { history: req.body._id }};
const additionalPoints = {$inc : { score : 1.5 }};
await User.findOneAndUpdate(condition, additionalPoints, putPostID, {upsert: true})
}catch(err){
res.status(500).send({error: err })
}
});
Every update operation must go into one object. In your case:
User.findOneAndUpdate(condition, {
$inc: { score: 1.5 },
$addToSet: { history: req.body._id },
},
{ upsert: true },
)

how to avoid duplicate key error collection in mongodb

i want to build a cart for my website, this is the schema for the cart:
const productSchema = require("./product")[1];
const cartItemSchema = new Schema<CartItem>(
{
product: productSchema,
quantity: {
type: Number,
required: true,
min: [1, "Quantity can not be less then 1."],
},
},
{
timestamps: true,
}
);
const CartSchema = new Schema(
{
userID: {
type: Schema.Types.ObjectId,
ref: "User",
},
items: [cartItemSchema],
},
{ timestamps: true }
);
module.exports = model<Cart>("Cart", CartSchema);
the problem is, when I add a product in a specific user cart, while the same product is allready added to another user cart document, I get this error:
"message":"cannot add to cart E11000 duplicate key error collection: elec-store.carts index: items.productID_1 dup key: { items.productID: null }, stack: MongoError: E11000 duplicate key error collection: elec-store.carts index: items.productID_1 dup key
this is the add function
public async add(cartItem: CartItem, userID: string): Promise<Cart> {
let cartInDB = null;
await CartModel.findOne({ userID: userID }, (err, cart) => {
cartInDB = cart;
});
if (AppUtils.hasValue(cartInDB)) {
const index = cartInDB.items.findIndex(
(item) => item.product._id.toString() === cartItem.product._id
);
if (index !== -1) {
cartInDB.items[index].quantity =
cartInDB.items[index].quantity + cartItem.quantity;
cartInDB.items[index].product._id = cartItem.product._id;
const cartAfterAdding = await cartInDB.save();
return cartAfterAdding;
} else {
await CartModel.update(
{ _id: cartInDB._id },
{ $push: { items: cartItem } }
);
}
return cartInDB;
} else {
const itemsArray: CartItem[] = [];
itemsArray.push(cartItem);
let createdCart = new CartModel({
userID: userID,
items: itemsArray,
});
await createdCart.save(); \\ this is where the problem occurs
return createdCart;
}
}
and this is how my cart looks like in mongodb document:
db.carts.find().pretty()
{
"_id" : ObjectId("60ea9fb81b2b4c048c3b1544"),
"userID" : ObjectId("60dee5e1da81bd274cd304de"),
"items" : [
{
"_id" : ObjectId("60ea9fb81b2b4c048c3b1545"),
"product" : {
"_id" : ObjectId("60e62cb21f74572b7c0b3a30"),
"name" : "tv",
"description" : "the best tv",
"categoryID" : 2,
"quantity" : "2",
"serialNumber" : "226swaq12",
"price" : 2000,
"imgUrl" : "https://www.seekpng.com/png/full/774-7744281_samsung-electronics-samsung-electronic-product-png.png"
},
"quantity" : 6,
"createdAt" : ISODate("2021-07-11T07:37:29.790Z"),
"updatedAt" : ISODate("2021-07-11T07:38:15.583Z")
},
{
"_id" : ObjectId("60eaa16b1b2b4c048c3b155d"),
"product" : {
"_id" : ObjectId("60e066009be1060748201ad3"),
"name" : "samsung tv",
"description" : "the best tv",
"quantity" : "2",
"categoryID" : 2,
"serialNumber" : "2212",
"price" : 2000,
"imgUrl" : "https://www.seekpng.com/png/full/774-7744281_samsung-electronics-samsung-electronic-product-png.png"
},
"quantity" : 9,
"updatedAt" : ISODate("2021-07-11T07:46:19.313Z"),
"createdAt" : ISODate("2021-07-11T07:44:43.764Z")
}
],
"createdAt" : ISODate("2021-07-11T07:37:29.792Z"),
"updatedAt" : ISODate("2021-07-11T07:46:19.314Z"),
"__v" : 0
}
I use mongoose.Schema to create new schemas and then when making reference to a different schema I do it like this:
product: { type: mongoose.Schema.Types.ObjectId, ref: 'product' },
If later you need to show also the product info (db.carts.find()), you can use populate() to replace the reference for all the product entries.
You can use upsert true.
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> // Available starting in MongoDB 4.2
}
)
For example -
db.books.update(
{ item: "ZZZ135" }, // Query parameter
{ // Replacement document
item: "ZZZ135",
stock: 5,
tags: [ "database" ]
},
{ upsert: true } // Options
)
This may help: Mongo Update

Cannot read property on POST json object

I'm getting an error
TypeError: Cannot read property 'tasks' of undefined
When I try a new POST request on my api, following is the request body I'm trying to make
{
"name": "tecnica1",
"description": "tecnica ensino1",
"rules": ["Regra1", "Regra2"],
"delivery_mode": ["Face to face", "Blended"],
"interaction": ["Group based", "One to many"],
"interrelationship": "High",
"motivation": "High",
"participation": ["Medium", "Low"],
"performance": ["Low", "None"],
"scope": ["Open ended", "Close ended"],
"feedback_use": "Low",
"target_audience": [15, 17],
"learning_objectives" : [
{
"knowledge_category": "Factual",
"behaviour": "Comparing",
"subject_matter": "teste",
"conditions": "teste",
"degree": "teste"
},
{
"knowledge_category": "Procedural",
"behaviour": "Explaining",
"subject_matter": "teste"
}
],
"affective_objectives": "teste",
"social_objectives": "teste",
"structure": {
"modules": [{
"name": "teste",
"phases": [{
"name": "teste",
"tasks": [{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
}]
},
{
"name": "test2",
"tasks": [{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
},
{
"type": "tipo1",
"description": "teste",
"role": "Student",
"resources": ["Recurso1", "Recurso2"]
}]
}]
}]
}
}
The error refers to the nested objects "Tasks", under Structure/Modules/Phases
Here's the model describing the object I'm trying to post and the controller function handling that.
Tecnica.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const idValidator = require('mongoose-id-validator');
const EVAL = ['High', 'Medium', 'Low', 'None']
let TaskSchema = new Schema({
type : {
type : String,
required : true
},
description : {
type : String,
required : true
},
role : {
type : String,
required : true,
enum : ['Student', 'Staff']
},
resources : [String]
});
let PhaseSchema = new Schema({
name : {
type : String,
required : true
},
tasks : {
type : [TaskSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one task should be defined.'
}
}
});
let ModuleSchema = new Schema({
name : {
type : String,
required : true
},
phases : {
type : [PhaseSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one phase should be defined.'
}
}
});
let EstruturaTecnicaSchema = new Schema({
modules : {
type : [ModuleSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one module should be defined.'
}
}
});
let LearningSchema = new Schema({
knowledge_category : {
type : String,
required : true,
enum : ['Factual', 'Conceptual', 'Procedural', 'Metacognitive']
},
behaviour : {
type : String,
required : true
},
subject_matter : {
type : String,
required : true
},
// Optinal/if any
conditions : String,
degree : String
});
/*
Technique defined by:
Init Data + Tlt Data.
Init Data consists of name, description and set of rules.
Tlt Data is defined by context and structure
*/
let TecnicaSchema = new Schema({
//Init Data
name : {
type : String,
required : true
},
description : {
type : String,
required : true
},
rules : [String],
//Context
delivery_mode : {
type : [String],
required : true
},
interaction : {
type : [String],
required : true
},
// Perception
// interrelationship, motivation, participation, performance
interrelationship : {
type : [String],
required : true,
enum : EVAL
},
motivation : {
type : [String],
required : true,
enum : EVAL
},
participation : {
type : [String],
required : true,
enum : EVAL
},
performance : {
type : [String],
required : true,
enum : EVAL
},
scope : {
type : [String],
required : true
},
feedback_use : {
type : [String],
required : true,
enum : EVAL
},
target_audience : {
type : [Number],
required : true
},
learning_objectives : {
type : [LearningSchema],
validate : {
validator: function(v){
return v.length >= 1;
},
message : 'At least one learning objective should be defined.'
}
},
affective_objectives : {
type : [String]
},
social_objectives : {
type : [String]
},
structure : {
type : EstruturaTecnicaSchema,
required : true
},
psychologist : {
type : mongoose.Schema.Types.ObjectId,
ref : 'User',
required : true
}
});
TecnicaSchema.plugin(idValidator);
module.exports = mongoose.model('Tecnica', TecnicaSchema);
tecnicas.controller.js
function create(req, res) {
let tecnica = new Tecnica();
let learning_objectives = req.body.learning_objectives;
let structure = req.body.structure;
let modules = req.body.structure.modules;
let phases = req.body.structure.modules.phases
let tasks = req.body.structure.modules.phases.tasks;
phases.tasks = tasks.map(t => { return t; });
modules.phases = phases.map(p => { return p; });
structure.modules = modules.map(m => { return m; });
tecnica.name = req.body.name;
tecnica.description = req.body.description;
tecnica.rules = req.body.rules;
tecnica.delivery_mode = req.body.delivery_mode;
tecnica.interaction = req.body.interaction;
tecnica.interrelationship = req.body.interrelationship;
tecnica.motivation = req.body.motivation;
tecnica.participation = req.body.participation;
tecnica.performance = req.body.performance;
tecnica.scope = req.body.scope;
tecnica.feedback_use = req.body.feedback_use;
tecnica.target_audience = req.body.target_audience;
tecnica.learning_objectives = learning_objectives.map(l => {
return l;
});
tecnica.affective_objectives = req.body.affective_objectives;
tecnica.social_objectives = req.body.social_objectives;
tecnica.structure = req.body.structure;
tecnica.psychologist = req.user;
tecnica.save()
.then(t => {
return res.status(201).json(t);
})
.catch(utils.handleError(req, res));
}
How can I parse the tasks?
I'm already using body-parser with the options
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
modules, phases, tasks are Arrays. You can't call
req.body.structure.modules.phases.tasks
req.body.structure.modules is an array
req.body.structure.modules.phases doesn't exist and is undefined.
A better way to assign properties like this:
function create(req, res) {
let tecnica = new Tecnica();
Object.assign(tecnica, { ...req.body });
tecnica.psychologist = req.user;
tecnica.save()
.then(t => {
return res.status(201).json(t);
})
.catch(utils.handleError(req, res));
}

How we remove null error encounter in referencing node js mongodb

I have to use populate method for referencing my second collection. I have two collections one is levels and second child.
I have referenced to my level collection through child collection. For this, I use the populated method but it returns null.
This is node js code
function(){
var query = {'_id': result._id };
var params = 'usercurrLevelId';
childQuizInfo.findOne(query, params).populate({
path : 'usercurrLevelId',
model : 'Level',
select : '_id level_num age'
}).exec(function(err,levelinfo){
if(err) return next(err);
res.send(levelinfo);
});
}
This level schema
var LevelSchema = new Schema({
_id: { type: String },
age: { type: Number },
level_num: { type: Number },
min_score: { type: Number },
max_questions: { type: Number }
});
var Level = mongoose.model('Level', LevelSchema);
This is child schema
usercurrLevelId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Level',
index: true
}
This is the level stored in mongoDB
{
"_id" : ObjectId("57cb1ef8b0b05a2ce9b0c24b"),
"age" : 5,
"level_num" : 1,
"min_score" : 12,
"max_questions" : 30
}
{
"_id" : ObjectId("57cb1ef9b0b05a2ce9b0c24c"),
"age" : 5,
"level_num" : 2,
"min_score" : 15,
"max_questions" : 33
}
This is the child stored in mongoDB
{
"_id" : ObjectId("57cc0410d51045452644251b"),
"usercurrLevelId" : ObjectId("57cb1ef8b0b05a2ce9b0c24b")
}
And the output i get
{
"_id": "57cc0410d51045452644251b",
"usercurrLevelId": null,
"__v": 0
}
I using this node js query so that I get level_num and _id from my level schema using this referencing.

Mongoose findByIdAndUpdate not pulling

I am trying to pull an object from a subdocument in a mongodb. The structure is as follows (from db.users.find().pretty()):
{
"_id" : ObjectId("56a660b42819b770b89950bd"),
"userName" : "someGuy",
"itemCollection" : [
{
"item_name" : "item1",
"_id" : ObjectId("56a661232819b770b89950be")
}
],
"__v" : 13
}
This code:
var userID = req.user._id;
var transactionID = req.body._id;
console.log('userID: '+userID);
console.log('transactionID: '+transactionID);
User.findByIdAndUpdate(
userID,
{$pull: {'itemCollection': {'_id': transactionID}}},{new: true}, function(err, model){
if(err){
console.log('ERROR: ' + err);
}
console.log(model);
});
Gets me the following output:
> userID: 56a660b42819b770b89950bd
> transactionID: 56a661232819b770b89950be
> { itemCollection:
[ { item_name: 'item1',
_id: 56a661232819b770b89950be } ],
__v: 13,
userName: 'someGuy',
_id: 56a660b42819b770b89950bd }
So nothing seems to have happened. I've tried every variation on stack overflow that I could find but it never deletes it. Does anyone see what is wrong?
You need to convert your _ids from string to an ObjectID type:
var mongoose = require('mongoose');
var userID = mongoose.mongo.ObjectID(req.user._id);

Categories