Mongoose is saving only objectId instead of the whole object - node.js

I am trying to save array of objects, but it is just saving the ObjectIds instead of whole objects, even thought in schema there is no refference or something like that.
So, I have this schema:
let MatchPlayerSchema = new Schema({
deck: {type: Array, default: []}
});
And I am trying to save this array tooken from database (playerDeck):
[ { _id: 5a1fc7ee667b103aace5f3ec,
magicResist: 10,
armor: 10,
attack: 10,
health: 10,
name: 'Test',
__v: 0,
type: 'CardUnit' },
{ _id: 5a1fc7ee667b103aace5f3ec,
magicResist: 10,
armor: 10,
attack: 10,
health: 10,
name: 'Test',
__v: 0,
type: 'CardUnit' }]
Like this:
let player = new MatchPlayer();
player.deck = playerDeck;
player.save();
However, the result is:
"deck" : [
ObjectId("5a1fc7ee667b103aace5f3ec"),
ObjectId("5a1fc7ee667b103aace5f3ec")
]
I have tried to set deck to: [Schema.Types.Mixed], but it didnt not help either.
When I try to save something just like: ['test', 'test'], it saves alright.
I just can not figure out, what am I doing wrong.
Any ideas? Thank you

I had the same issue and setting the
mongoose.set('debug', true);
helped me to see what mongoose actually sending to the DB.
I was having in the Schema
const ChatSchema = mongoose.Schema({
messages: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Message',
}],
isRemoved: {
type: Boolean,
default: false,
required: true
}
});
After switching the type to mongoose.Schema.Types.Mixed it worked perfectly. This is how I was invoking an update
Chat.findOneAndUpdate({ _id: req.params.chatId }, { $push: { messages: message }},{new: true});

let MatchPlayerSchema = new Schema({
deck: {
type: Schema.Types.Mixed
}
});
You should be able to store an array of objects into 'deck'

Related

Mongoose findById is not returning all fields

I'm calling findById using mongoose and it's not returning all fields, or at least it's not mapping to a field correctly. But it returns that field if I use aggregate
I have the following schema
const ratingSchema = new mongoose.Schema({
rating: {
type: Number,
default: 0,
min: 0,
max: 5
}
})
const locationSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
address: {
type: String,
required: true,
},
rating: ratingSchema,
facilities: [String],
});
locationSchema.index({coords: '2dsphere'});
mongoose.model('Location', locationSchema);
When I call
const Loc = mongoose.model('Location');
const locationsReadOne = (req, res) => {
Loc
.findById(req.params.locationid)
.exec((err, location) => {
if (!location) {
return res.status(404).json({"message":"location not found"});
} else if (err) {
return res.status(404).json(err);
}
console.log("locationsReadOne:",location);
res.status(200).json(location);
});
};
It returns the schema, but the rating field is not being returned. Here is a console.log of the returned object:
locationsReadOne: {
facilities: [ 'Hot drinks', 'Food', 'Premium wifi' ],
_id: 5f88bfdc4df4ca7709462865,
name: 'Starcups',
address: '125 High Street, Reading, RG6 1PS'
}
If I call Loc.aggregate, the rating field is returned:
{
_id: 5f8b2ee15b0b6784a847b600,
facilities: [ 'Tea', ' Restroom' ],
name: 'Tea Leaf',
address: '2192 Green St.',
rating: 0,
}
{
_id: 5f88bfdc4df4ca7709462865,
name: 'Starcups',
address: '125 High Street, Reading, RG6 1PS',
rating: 3,
facilities: [ 'Hot drinks', 'Food', 'Premium wifi' ]
}
Any idea why this would happen? I can clearly see the rating field in each of the documents in MongoDB compass and they are all listed as type Double. Why are they being returned in aggregate, but not in findById(id) or even in find()?
Thank you.
When you use find in mongoose, it will populate the document with the fields from the schema. For example - if you remove a field from the schema, you may not see that on the found document, even if it is in the database. Aggregate and Compass are showing you exactly what's in the database, so that data is there, it is just not being populated on the document because it doesn't see it on the schema.
The reason for that is your ratingSchema is an object with a rating property. So mongoose is looking for something like:
{
name: ...
rating: {
rating: 2
}
}
And not finding it, so it's not populating. To fix this, I would not define rating as a subschema, and instead define your schema as
const locationSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
address: {
type: String,
required: true,
},
rating: {
type: Number,
default: 0,
min: 0,
max: 5
},
facilities: [String],
});

Populate subdocument array field with Mongoose

I know this question has been asked before, but after hours of research I have tried several ways to solve my problem without success.
Schema
const ValuationsSchema = new Schema(
{
value: { type: Number, required: true, min: 0, max: 5 },
author: {
type: Schema.Types.ObjectId,
refPath: "fromModel",
required: true,
},
fromModel: {
type: String,
required: true,
enum: ["usertype1", "usertype2", "usertype3"],
trim: true,
},
},
{
timestamps: true,
}
);
const UserSchema = new Schema(
{
name: { type: String, required: true, trim: true },
valuations: [ValuationsSchema],
},
{
timestamps: true,
}
);
What am I trying to do
I am trying to populate the author field of ValuationsSchema, in the valuations array of a user.
Sample data
Result expected
{
_id: 5f1ef9f6039fea10c437939c,
name: 'Jose Maria',
createdAt: 2020-07-27T15:59:50.529Z,
updatedAt: 2020-08-01T15:34:47.414Z,
valuations: [
{
_id: 5f258b973c0ac544869c0434,
value: 5,
author: Object,
fromModel: 'particular',
createdAt: 2020-08-01T15:34:47.414Z,
updatedAt: 2020-08-01T15:34:47.414Z
}
]
}
Result gotten
{
_id: 5f1ef9f6039fea10c437939c,
name: 'Jose Maria',
createdAt: 2020-07-27T15:59:50.529Z,
updatedAt: 2020-08-01T15:34:47.414Z,
valuations: [
{
_id: 5f258b973c0ac544869c0434,
value: 5,
author: 5f1edaa83ce7cf44a2bd8a9a,
fromModel: 'particular',
createdAt: 2020-08-01T15:34:47.414Z,
updatedAt: 2020-08-01T15:34:47.414Z
}
]
}
Already tried solutions
At the moment I manually populated the field, but as it is a good practice, I am trying to use the API when possible.
As far as I am concerned executing this should get the job done, but it doesn't.
await user.populate("valuations.author").execPopulate();
Also tried this without luck:
await user.populate({
path: "valuations",
populate: {
path: "author",
},
}).execPopulate();
Tried the deepPopulate package too, but yet same result.
The refPath should be from parent level valuations.fromModel,
Change it in your schema,
author: {
type: Schema.Types.ObjectId,
refPath: "valuations.fromModel",
required: true,
}
Single document wise population,
let user = await User.find();
let user1 = await user[0].populate("valuations.author").execPopulate();
All documents population
let users = await User.find().populate("valuations.author").execPopulate();
Note: There is a bug #6913 in mongoose version 5.2.9, refPath not working in nested arrays, Make sure you have installed latest version.

Mongoose - Save array of strings

I can't save an array of strings into my DB using Mongoose.
(Note all code below is simplified for ease of writing here)
So i declare a variable of a person schema I have:
var newPerson = new Person ({
tags: req.body.tags
});
The schema itself looks like:
var personSchema = new mongoose.Schema({
tags: Array
});
And when it comes to saving its just a simple:
newPerson.save(function(err) {
//basic return of json
});
So using Postman I send in an array in the body - however everytime I check the DB, it just shows one entry with the array as a whole i.e. how I sent it:
Any ideas what extra I'm supposed to do?
Write up from my comment:
The way to specify an array of strings in mongoose is like so:
var personSchema = new mongoose.Schema({
tags: [{
type: String
}]
However, the problem here is most-likely to do with Postman as it is sending the 'array' as a string. You can check this by checking the type of req.body.tags like so:
console.log(typeof req.body.tags)
If this returns a String, make sure to set the content-type in Postman to JSON as seen in this screenshot rather than the default 'form-data' option.
var schema = new Schema({
name: String,
binary: Buffer,
living: Boolean,
updated: { type: Date, default: Date.now },
age: { type: Number, min: 18, max: 65 },
mixed: Schema.Types.Mixed,
_someId: Schema.Types.ObjectId,
decimal: Schema.Types.Decimal128,
array: [],
ofString: [String],
ofNumber: [Number],
ofDates: [Date],
ofBuffer: [Buffer],
ofBoolean: [Boolean],
ofMixed: [Schema.Types.Mixed],
ofObjectId: [Schema.Types.ObjectId],
ofArrays: [[]],
ofArrayOfNumbers: [[Number]],
nested: {
stuff: { type: String, lowercase: true, trim: true }
},
map: Map,
mapOfString: {
type: Map,
of: String
}
})
// example use
var Thing = mongoose.model('Thing', schema);
var m = new Thing;
m.name = 'Statue of Liberty';
m.age = 125;
m.updated = new Date;
m.binary = Buffer.alloc(0);
m.living = false;
m.mixed = { any: { thing: 'i want' } };
m.markModified('mixed');
m._someId = new mongoose.Types.ObjectId;
m.array.push(1);
m.ofString.push("strings!");
m.ofNumber.unshift(1,2,3,4);
m.ofDates.addToSet(new Date);
m.ofBuffer.pop();
m.ofMixed = [1, [], 'three', { four: 5 }];
m.nested.stuff = 'good';
m.map = new Map([['key', 'value']]);
m.save(callback);
On Schema:
( Since you have mentioned in the problem that it is an array of strings )
var personSchema = new mongoose.Schema({
tags:{
type:[String],
required: true
}
});
On Postman:
{
"tags": ["css", "javascript", "mongoose", "node"]
}
On MongoDB
{
"tags":["css", "javascript", "mongoose", "node"]
}
Similarly, you can create other types of primitive arrays and document arrays in the mongoose schema as:
({
toys: [ToySchema],
buffers: [Buffer],
strings: [String],
numbers: [Number]
// ... etc
});
Try changing the schema to
var personSchema = new mongoose.Schema({
tags: [{type: String}]
});
or you can use Mixed type
var personSchema = new mongoose.Schema({
tags: mongoose.Schema.Types.Mixed
});
EDIT
i think the problem is with assignment. Use:
person.tags.push("string to push");
On Schema
techs: Array
On Postman
"techs": ["express","rect","html","css","scss"]
On DB (MongoDB)
"techs" : [
"epxress",
"rect",
"html",
"css",
"scss"
]
var personSchema = new mongoose.Schema({
tags: [{type: String}]
});
Use this in the schema.
Saving the Array:
var etc = new modename({yourprimaryid: primaryid});
for (var i = 0; i < tag.length; i++) {
etc.tag.push(tag[i]);
}
etc.save(function(err) {
//whatever you want here
}
Define a Schema:
const schema = new Schema({
name: { type: String, required: true },
tags: [String]
});
In postman add each element separately using the array syntax below
name:Thing
tags[]:task
tags[]:other
tags[]:thing
Return Data:
{
"__v": 0,
"name": "Thing",
"_id": "5a96e8d0c7b7d1323c677b33",
"tags": [
"task",
"other",
"thing"
]
}
this will also work
var personSchema = new mongoose.Schema({
tags: {
type: [String], default: []
}
});
Firstly, as many people have noted, the schema needs to change to indicate that the tags field is intended to hold an array of strings, and not just a single one. So that needs to change to:
var personSchema = new mongoose.Schema({
tags: [String]
});
The other thing you need to keep in mind (and which caused me a lot of trouble), is that when saving, make sure to use a fresh array for the tags field. For example, this won't work:
person.tags[0] = "new tag";
person.save();
Instead, you need to do something like:
person.tags = person.tags.slice(); // Clone the tags array
person.tags[0] = "new tag";
person.save();
Hope this helps.
I had a simialr problem,
In the model, do this :
tags : {[String], default: undefined}
So that it defaults to undefined unstead of an empty array,
and instead of this:
const person = new Person({
tags : req.body.tags
});
Do this :
const person = new Person();
person.tags = req.body.tags;
My requirement;
ingredients: Type Array of Strings
Solution:
ingredients: {
type: [String],
},
const productSchema = new mongoose.Schema(
{
name: {
type: String,
},
description: {
type: String,
},
price: {
type: String,
},
categoryId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Category",
},
sellerId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Seller",
},
discount: {
type: String,
},
status: {
type: String,
default: "active",
enum: ["active", "inactive", "deleted"],
},
images: {
type: Array,
required: true,
},
},
{ timestamps: true }
);

Default values in array mongoose

I am trying to make default values for array in mogoose schema:
warning:
type: Array
default: [10, 50, 99]
Am I right in such decision or there is some other way to do this?
Regarding to the Mongoose-Documentation, your way is correct.
Here a small example:
var arrayTestSchema = new Schema({
anArray: {
type: Array,
'default': [1, 2, 3]
}
});
And a link to the related documentation page: http://mongoosejs.com/docs/2.7.x/docs/defaults.html
for Mongoose v5.x
If you want to specify the type of the array child, you can define it like example below:
const BlogSchema = new Schema({
tags: {
type: [String],
default: ["tech", "economy"],
},
})
or
const BlogSchema = new Schema({
tags: {
type: [
{
type: String,
// Another properties
},
],
default: ["tech", "economy"],
},
})
References:
https://mongoosejs.com/docs/schematypes.html#arrays
It should be a json, I don't know what is what you posted there.
new Schema({
warning: { type: Array, default: [10, 50, 99] }
})

Mongoose Relationship Populate Doesn't Return results

var SecuritySchema = new Mongoose.Schema({
_bids: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'BuyOrder'
}],
_asks: [{
type: Mongoose.Schema.Types.ObjectId,
ref: 'SellOrder'
}]
});
var OrdersSchema = new Mongoose.Schema({
_security: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Security'
},
price: {
type: Number,
required: true
},
quantity: {
type: Number,
required: true
}
});
// declare seat covers here too
var models = {
Security: Mongoose.model('Security', SecuritySchema),
BuyOrder: Mongoose.model('BuyOrder', OrdersSchema),
SellOrder: Mongoose.model('SellOrder', OrdersSchema)
};
return models;
And than when I save a new BuyOrder for example:
// I put the 'id' of the security: order.__security = security._id on the client-side
var order = new models.BuyOrder(req.body.order);
order.save(function(err) {
if (err) return console.log(err);
});
And attempt to re-retrieve the associated security:
models.Security.findById(req.params.id).populate({
path: '_bids'
}).exec(function(err, security) {
// the '_bids' array is empty.
});
I think this is some sort of naming issue, but I'm not sure, I've seen examples here and on the moongoose website that use Number as the Id type: http://mongoosejs.com/docs/populate.html
The ref field should use the singular model name
Also, just do:
models.Security.findById(req.params.id).populate('_bids').exec(...
My main suspicion given your snippet at the moment is your req.body.order has _security as a string instead of an array containing a string.
Also, you don't need an id property. Mongodb itself will automatically do the _id as a real BSON ObjectId, and mongoose will add id as a string representation of the same value, so don't worry about that.
While I don't understand your schema (and the circular nature of it?), this code works:
var order = new models.BuyOrder({ price: 100, quantity: 5});
order.save(function(err, orderDoc) {
var security = new models.Security();
security._bids.push(orderDoc);
security.save(function(err, doc) {
models.Security.findById({ _id: doc._id })
.populate("_bids").exec(function(err, security) {
console.log(security);
});
});
});
It:
creates a BuyOrder
saves it
creates a Security
adds to the array of _bids the new orderDoc's _id
saves it
searches for the match and populates
Note that there's not an automatic method for adding the document to the array of _bids, so I've done that manually.
Results:
{ _id: 5224e73af7c90a2017000002,
__v: 0,
_asks: [],
_bids: [ { price: 100,
quantity: 5,
_id: 5224e72ef7c90a2017000001, __v: 0 } ] }

Resources