TypeError: parent.child.push is not a function - issue pushing my object to database - mongodb

Problem
I'm trying to send data from my client-side form into my database and am running into the error below:
TypeError: artist.fans.push is not a function
The data is an email address that should be saved into my artist model as a subdoc (in an object).
What I've tried
I'm using the syntax from the mongo docs that says parent.children.push() is the proper way to add subdocs to arrays and parent.children.create() to create new subdocuments. Both yield the same error.
Here's my function:
module.exports.addFanEmail = async (req, res) => {
const fan = req.body;
const artist = await Artist.findById(req.params.id);
artist.fans.create(fan);
await artist.save();
res.redirect(`/artists/${artist._id}`);
}
Right now req.body is only the "fan's" email - here's an example of the object's format: { email: 'tom#test.com' }
DB Model
const artistSchema = new Schema({
image: [ImageSchema],
genre: [ String ],
fans: {
email: String,
subscribed: String,
gender: String,
age: Number
},
});
The object is coming through from the client to the function without any problem, I just can't get it to save to the db?

Related

Argument passed in must be a single String of 12 bytes or a string of 24 hex characters, Mongoose ObjectId err

I actually searched a ton and I saw a ton of mentions of my problem here but none of the things I tried helped me fix the issue i'm having.
I have a Room Scheme that looks like this:
const ObjectId = mongoose.Schema.ObjectId;
const roomSchema = mongoose.Schema({
users: [{
type: ObjectId,
ref: 'User'
}],
messages: [{
type: ObjectId,
ref: 'Message',
}],
post: {
type: ObjectId,
ref: 'Post'
}
});
As you can see I have an array of users with ref to another schema Users
I'm trying to query all the Rooms that has a User ObjectId in it (search ObjectId in an array).
while I can easily get this with querying mongo from cmd using this:
db.users.find({users:ObjectId('THE_OBJECT_ID')});
when I try to get the same while using mongoose it fails with:
Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
Here is how my route and find looks like:
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: "USER_ID" };
Room.find(query ).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
I tried to create type of object ID and use it but it still doesn't work.
var mongoose = require('mongoose'),
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
and than
Rooms.find({'users': id });
but it still doesn't work.
I also tried altering my query search using $in, $elemmatch it worked on cmd but failed when querying using mongoose.
Any help would be appreciated.
Issue :
If you check this :
var query = { users: "USER_ID" };
(Or)
userId = 'THE_USER_ID';
var id = mongoose.Types.ObjectId(userId);
What are you trying to do here ? You are passing in string USER_ID or THE_USER_ID as input and trying to convert it to type of ObjectId(). But string inside ObjectId() has certain restrictions which is why mongoose is failing to convert passed in string value to ObjectId() and getting error'd out.
Try this code :
Code :
const mongoose = require('mongoose');
app.route('/rooms/list/:user_id')
.get((req, res) => {
var query = { users: mongoose.Types.ObjectId(req.params.user_id) };
Room.find(query).populate('messages').then((data) => {
res.status(200).json(data);
}).catch((err) => {
console.log(err);
});
})
Your input should be value of user_id (Which will be string) - Convert it to ObjectId() and then query DB. So value of user_id should be a string that obeys ObjectId()'s restrictions, You can take string from one of existing doc's ObjectId() & test your get api.

Validation error using `create()` in Mongoose transaction

I'm testing transactions using mongoose and trying to accomplish a very simple task with the following code:
const session = await mongoose.startSession();
session.startTransaction();
try {
const opts = { session, new: true };
const A = await Author.
create({
firstName: 'Jon',
lastName: 'Snow'}, session);
if(!A) throw new Error('Aborted A by Amit!');
const B = await Author.
findOneAndUpdate({firstName: 'Bill'}, {firstName: 'William'}, opts);
if(!B) throw new Error('Aborted B by Amit!');
await session.commitTransaction();
session.endSession();
} catch (err) {
await session.abortTransaction();
session.endSession();
throw err;
}
All I'm trying to do is first insert (using mongoose create() method) a new document into a collection and then edit (using Mongo findOneAndUpdate() method) another document in the same collection. Failure of either query needs to abort the entire transaction suite.
It's the create() that seems to be giving me problems. The document does get created and inserted, however, it also throws an error:
"Author validation failed: lastName: Path lastName is required.,
firstName: Path firstName is required."
Any idea what this could mean? It seems it's complaining about not being given values for required fields (firstName and lastName) despite me having already given it those.
I have no idea why it would complain about missing values when I've provided them both and they're still getting added to the collection!
This is because Model.create() first parameters accept documents to insert, as an array OR as a spread. For example, these two are equivalent:
// pass a spread of docs
Candy.create({ type: 'jelly bean' }, { type: 'snickers' })
// pass an array of docs
Candy.create([{ type: 'jelly bean' }, { type: 'snickers' }])
The problem with your line, is that it's trying to take the second document {session: session} as another entry for Author, which is missing the required firstName and lastName fields.
Instead you should do:
Author.create([{ firstName: 'Quentin', lastName: 'Tarantino' }],
{ session: session }
);
You may also find Transactions in Mongoose a helpful reference.

Correctly inserting and/or updating many datasets to MongoDB (using mongoose)?

So from time to time I get new exports of a cities database of POIs and info about them and I want to have all that data in my MongoDB with a Loopback-API on it. Therefore I reduce the data to my desired structure and try to import it.
For the first time I receive such an export, I can simply insert the data with insertMany().
When I get a new export, it means that it includes updated POIs which I actually want my existing POIs to be replaced with that new data. So I thought I'd use updateMany() but I could'nt figure out how I'd do that in my case.
Here's what I have so far:
const fs = require('fs');
const mongoose = require('mongoose');
const data = JSON.parse(fs.readFileSync('data.json', 'utf8'));
// Connect to database
mongoose.connect('mongodb://localhost/test', {
useMongoClient: true
}, (err) => {
if (err) console.log('Error', err);
});
// Define schema
let poiSchema = new mongoose.Schema({
_id: Number,
name: String,
geo: String,
street: String,
housenumber: String,
phone: String,
website: String,
email: String,
category: String
});
// Set model
let poi = mongoose.model('poi', poiSchema);
// Generate specified data from given export
let reducedData = data['ogr:FeatureCollection']['gml:featureMember'].reduce((endData, iteratedItem) => {
endData = endData.length > 0 ? endData : [];
endData.push({
_id: iteratedItem['service']['fieldX'],
name: iteratedItem['service']['fieldX'],
geo: iteratedItem['service']['fieldX']['fieldY']['fieldZ'],
street: iteratedItem['service']['fieldX'],
housenumber: iteratedItem['service']['fieldX'],
phone: iteratedItem['service']['fieldX'],
website: iteratedItem['service']['fieldX'],
email: iteratedItem['service']['fieldX'],
category: iteratedItem['service']['fieldX']
});
return endData;
}, []);
//
// HERE: ?!?!? Insert/update reduced data in MongoDB collection ?!?!?
//
mongoose.disconnect();
So I just want to update everything that has changed.
Of course if I leave it to insertMany() it fails due to dup key.
For the second time, use mongo's update command with upsert set to true.
db.collection.update(query, update, options)
In the query pass the _id ,in update pass the object and in option set upsert to true. This will update the document if it exists creates a new document if that doesn't exist.

Why I can't get document via mongoose?

I'm getting document via MongoDB Shell:
db.page_about_love.find()
But I can't get document via mongoose. What is wrong?
mongoose.connect(db_uri);
var loveSchema = new mongoose.Schema({
title: String,
content: String,
tag: String
});
mongoose.model('page_about_love', loveSchema);
var about = mongoose.model('page_about_love');
about.find(function (err, love) {
if (err) return console.error(err);
console.log(love);
});
Test output:
[]
To prevent Mongoose generating a collection name to use, you should be explicit and pass which collection name it should use:
var loveSchema = new mongoose.Schema({
title: String,
content: String,
tag: String
}, { collection : 'page_about_love' });
Otherwise, Mongoose will apply the utils.toCollectionName() function to the model name to determine the collection name, which in your case would yield page_about_loves (notice the pluralization).
More information here.

inserting in mongo using express

i am trying to put data in the mongodb using express but it is storing blank always ...also it is not printing any console logs :
the url i am hitting after starting the sever is
http://localhost:3000/posts?title=test&link=http://test.com
and it is showing the below output:
{"__v":0,"_id":"562717b064002b1c2e697b33","comments":[],"upvotes":0}
router.get('/posts', function(req, res, next) {
console.log('reached ere '+req);
var post = new Post(req.body);
post.save(function(err, post){
if(err){ return next(err); }
res.json(post);
});
});
Post Scheme:
var mongoose = require('mongoose');
var PostSchema = new mongoose.Schema({
title: String,
link: String,
upvotes: {type: Number, default: 0},
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
mongoose.model('Post', PostSchema);
You are calling your API as
http://localhost:3000/posts?title=test&link=http://test.com
which will send title and link to server as query parameters and not body parameters. So your req.body in this case would be an empty object. That is the reason no data in being saved in your posts collection.
You have two options here:
Change your API to save req.query in posts collection which you can do as follows:
Replace
var post = new Post(req.body);
with
var post = new Post(req.query);
Pass link and title as body parameters instead of query parameters.