Inserting multiple ObjectIds to sub-document at once with mongoose - javascript

I'm using mongoose and I have the following model:
const UniversitySchema = new Schema({
name: {
type: String,
required: [true, 'Name is required']
},
color: {
type: String
},
paths: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Path'
}]
})
I want to be able to insert multiple paths (which cast to the Path model) into the universities collection when first creating it. I tried the following code:
const newUni = new University({
name: name,
paths: [...pathIds],
color: color
})
newUni.save()
.then(uni => {
return res.send(uni)
})
.catch(err => console.log(err));
})
However it raises this error:
CastError: Cast to ObjectId failed for value "[ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ]" at path "_id" for model "Path"
messageFormat: undefined,
stringValue: '"[ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ]"',
kind: 'ObjectId',
value: [ 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ],
path: '_id',
reason: Error: Argument passed in must be a single String of 12 bytes or a string
of 24 hex characters
It looks like mongoose does not cast the Ids separately when there are more than one, because when I tried it with one id, it worked.
I also tried to save the documents and then push the ids to the path array, but it didn't work.
Is there a way to do it all at once?
Thanks in advance.

You need to make sure that there are no whitespaces in each of the array's id- values:
With this code
const newUni = new University({
name,
paths: ["5f122f9967c59932b44214b6","5f2762858f9060327c4f6b2f"],
color
})
it should definitely work.
So assuming pathIds initially holds the string " 5f122f9967c59932b44214b6, 5f2762858f9060327c4f6b2f ", you can simply do
...
paths: pathIds.split(",").map(v => v.trim()),
...
to get an array of the trimmed id-values:

Related

Is it possible to populate objects in map schema type?

I have schema type Map in my mongoose model. In this map, each element has reference to another model. I know that it's possible to populate attributes in array, but how about Map type? Be cause nesting like "map_type_attribute.some_attribute_to_populate" doesn't work. :)
This is my model:
const Mongoose = require('mongoose');
const parameter = Mongoose.Schema({
definition: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Definition',
},
value: {},
}, {_id: false});
const schema = Mongoose.Schema({
model: {
type: Mongoose.Schema.Types.ObjectId,
ref: 'Model'
},
name: String,
objectid: Number,
externalId: String,
properties: Mongoose.Schema.Types.Mixed,
parameters: {
type: Map,
of: parameter
}
});
module.exports = Mongoose.model('Element', schema);
This is how i'm trying to populate definition field:
const request = Element.find(query, projection);
request.populate('parameters.definition');
request.exec( (err, docs) => {
...
This functionality was added in Mongoose 5.10.3 (September 2020). You simply denote every element in the map with a $*.
In your example this would be:
const request = Element.find(query, projection);
request.populate('parameters.$*.definition');
request.exec( (err, docs) => {
I also trying to find answer on this question. It seems that deep-populate work, but only if you put keys from the Map to populate method/function. In your case, if you have data like:
{
model: ObjectId("111"),
name: "MyName",
objectid: 111,
externalId: "ExternalId",
properties: ...,
parameters:{
"parameter1":{
"definition":ObjectId("333"),
"value":"value of parameter 1"
},
"parameter2":{
"definition": ObjectId("444"),
"value": "value of parameter 2"
}
}
}
Then you may find and populate like this:
Element.find({}).populate("parameters.parameter1.definiton")
But it's not a good solution. It would be nice if we have something like regexp inside this populate path.
Currently I've managed only to grab all inner collection, and then manually work with Map to substitude collections.
It shouldn't be a huge overhead, since you have only 2 queries to DB. In you case it can be like:
const elements = Element.find({});
const parameters = Parameter.find({});
// go through the elements.parameters and replace it with appropriate value from parameters collection.

mongoose, trying to find one object by a value

What I'm trying to accomplish here is to find one object by a value, and then adding that object to another objects property.
Code
q = {
_id: Schema.Types.ObjectId,
category: QuestionCategory,
question: String,
name: String,
};
QuestionCategory.findOne().sort({ category: "ExampleCategory" }).exec((err, doc) => {
if(err)
{
return err
}
q.category = doc;
})
error:
throw new TypeError('Invalid sort value: {' + field + ': ' + value + ' }');
^
TypeError: Invalid sort value: {category: ExampleCategory }
I've also tried without the .sort and write the filter inside findOne, but didn't work either
The Schema
const QuestionCategorySchema= new Schema({
_id: Schema.Types.ObjectId,
category: String,
icon: String,
order: Number
}, { collection: 'QuestionCategory' });
if you are trying to query the documents with category field ExampleCategory you need to make a find query not a sort query
and also there is no need of sorting the data when your are using findOne as it only gets one QuestionCategory
QuestionCategory.findOne({category:"ExampleCategory"}).exec(err, doc)=>{})
The first thing you can't use sort() like this sort({ category: "ExampleCategory" }), You need to sort like
.sort({ category: -1 }) OR .sort({ category: 1 }), Another thing we can't use sort() while performing top fetch a single record from database(Ex.findOne()). The sort() basically used for sorting your data respectively your keys. By the way you can get your desired result by below query:
QuestionCategory.find({}).sort({ _id: -1 }).limit(1)

return single object based on ObjectId

hi im currently building something for fun that allows users to post anything. and im experincing some problems here is my code.
return details.findOne({'data': {$elemMatch: {'_id':req.params.id}}}).then((a) => {
return res.render('post', { a });
}).catch(err => console.log(err));
i only want this to return one post i thought by using the objectId id would be able todo that but it seems to return everything in the data array anybody have any ideas below is my schema.
i need this to only return the single object whos objId is in the url
var schemaMode = new mongoose.Schema({
email: {type: String, required: true},
password: {type: String, required: true},
username: {type: String,required:true},
data: [{
author: String,
title: String,
comments: [String],
article: String,
}]
});
Details.findById(req.params.id, function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
if you only want certain fields back, like you want the object to only have its data field for example, then you do:
Details.findById(req.params.id, "data",
function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
OR
Details.findById(req.params.id).select("data")
.exec(function(err, foundObject){
//foundObject contains the object with the ._id matching req.params.id
});
to be clear, in the code above, Details is the imported (with require) schema (in your case, the one named schemaMode)
Is "data" your "post"? If so, I think you need a projection.
return details.findOne({'data': {$elemMatch: {'_id':req.params.id}}},{'data.$'}).then((a) => {
return res.render('post', { a.data[0] });
}).catch(err => console.log(err));
'data.$' will project you the whole model, filled only with the desired "data"/"post"

Cast to ObjectId failed for value error in Mongoose findOne

I've been struggling with a weird exception and still confused about it after an hour.
CastError: Cast to ObjectId failed for value "pedrammarandi#gmail.com"
at path "_id" for model "Account"
I'm trying to retrieve an Account via email address. Here is my query
export async function getPendingRecipients(user_id, email_address) {
const account = await Account
.find({email: email_address})
.exec();
return true;
}
This is my Schema object
const userGmailSchema = new Schema({
id: {
type: String,
unique: true
},
displayName: String,
image: Object,
accessToken: String,
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
refreshToken: {
type: String,
default: null
},
email: {
type: String,
unique: true
},
emails: [
{
type: Schema.Types.ObjectId,
ref: 'Emails'
}
]
});
I'm not sure, but I guess the problem is you wrote an id field.
In MongoDB, the "primary key" is _id field, which is an ObjectId object (actually it's a 12-byte-value), and in mongoose, id is a virtual getter of _id, easily said, id is an alias of _id.
(A little different is that, _id returns ObjectId, id returns String version of _id.)
By default, mongoose manage _id field automatically, so commonly we should not write anything about id in schema.
If your id is for something like primary key ID in SQL DB, just remove it from mongoose schema. If it's means something else in your app, try to add an option:
const userGmailSchema = new Schema({
// your schemas here
},
{
{ id: false } // disable the virtual getter
})
or rename it.
http://mongoosejs.com/docs/guide.html#id
Hope this helps.

Grab object from MongoDB but limit number of items pulled from array

My API currently has a route for Getting an event from my MongoDB database based on event_id. This works fine. However, I have a 'photos' array within this event object that is growing (currently over 3,000 objects within this array).
I want to pass a limit parameter to limit the number of results pulled from this array, but cannot figure out how. Below is my current node route and mongoDB schema:
route:
// get event by _id
app.get('/api/events/:event_id', function(req, res) {
// use mongoose to get event
Event.findOne({object_id: req.params.event_id}, function(err, event) {
// if there is an error retrieving, send the error. nothing after res.send(err) will execute
if (err)
res.send(err)
if (req.params.limit >= 0) {
// res.jsonp(event) with photos array limited to req.params.limit
}
res.jsonp(event); // return event in JSON format
});
});
schema:
var eventSchema = new Schema({
event: String,
city: String,
state: String,
date: String,
start: String,
end: String,
dateState: String,
radius: String,
team_1: String,
team_2: String,
object_id: String,
longitude: String,
latitude: String,
cover: {
img: String,
username: String
},
photos: []
})
Don't have a constantly growing array field. It's not good for performance because MongoDB (well, if <= 2.6/using mmap) will be moving the document around when it grows outside of the space allocated for it by the storage engine, causing performance problems. You should change your schema to avoid an array like this, but I can't really say more about how you should do it because I don't know much about your use case.
There is a way to limit the number of array elements returned in a find query though, using $slice projection.
> db.test.drop()
> db.test.insert({ "_id" : 0, "x" : [0, 1, 2, 3, 4] })
> db.test.find({ "_id" : 0 }, { "x" : { "$slice" : 2 } })
{ "_id" : 0, "x" : [0, 1] }
Event.findOne({object_id: req.params.event_id})
.limit(10)
.exec(function(e,doc){
...
});
Edit
Or if you have ref on the photo... you can populate the doc array of referenced id with limit option. Hope it helps :) All abount population
.find(...)
.populate({
path: 'photos',
options: { limit: 5 }
})
.exec(...)
Schema
var eventSchema = new Schema({
event: String,
city: String,
state: String,
...
photos: [{ type:String, ref:'pictureSchema' }]
}
var pictureSchema = new Schema({
name : {type:String},
url : {type:String},
...
}
In photos array than you just put id of the pictures doc, when you populate the photos array it will put pictureSceham doc insted of _id.

Categories

Resources