Closing mongoose db connection erases schema - javascript

I got a weird behavior with mongoose, it seems that closing the db connection after saving my schema erases it.
Here's my schema brand.js:
var brandSchema = new db.Schema({
id: {type: String, required: true, index: {unique: true}},
products: [String]
});
I execute the following script to populate my database (important precision: before executing the script, my brand table is not defined in the DB):
var async = require('async');
//Get config
var middlewares = require('./middlewares');
var config = require('./config');
//Load model
require('./models/brand');
var db = middlewares.db(config);
// Load brand.json
var brands = require('./brands.json');
// Save each brand in the database
async.each(Object.keys(brands), function(brandId, callback){
var brand = new Brand({id: brandId, products: brands[brandsId]);
brand.save(function(err){
if(err) console.log(err);
else console.log('brand ' + brand.id + ' added');
callback();
});
}, function(){
//Properly close the connection so the script can terminate
db.connection.close();
});
This script gives the following logs:
Mongoose: brand.ensureIndex({ id: 1 }) { safe: undefined, background: true, uni
que: true }
Mongoose: brand.insert({ __v: 0, products: [ 'phone', 'tv'], _id: ObjectId(
"54524f612245da0c2f0d2ba7"), id: 'samsung' }) {}
Mongoose: brand.insert({ __v: 0, products: [ 'food', 'beverages' ], _id: ObjectId(
"54524f612245da0c2f0d2ba8"), id: '' }) {}
The first line of the logs suggests that an index id is created with the right parameters. But when I look into the DB, my brands are saved but the brand schema is empty. However, if I remove db.connection.close(), brands are still saved and the schema is saved like defined in Brand.
I don't really understand why. Could it be because of buffered data not sent to the db ?

Related

Model find subdocument array using user ID instead of [ [Object] ]

Objective
I'm trying to pull the qr documents attached to each user (which have a maximum of six) and return both the documents and subdocuments from the my mongoDB using below controller function.
Function
const Qr = require('../models/qr');
const mongoose = require('mongoose');
module.exports.showActivations = async (req, res) => {
const qrCodes = await Qr.find({ user: req.params.id });
console.log(qrCodes);
};
Expected results
An example below shows how I expected the results to be returned by showing the subdocument details within 'redirect'
{
_id: new ObjectId("6317ad944f0f1db567b9608c"),
redirect: [
name: "Test",
url: "https://testurl.com"
qrcode: "data:image/png;base64,iVBORwasdf..."
],
user: new ObjectId("6310a2da50448e982336784a"),
__v: 0
}
Actual behavior
I am receiving [ [Object] ] instead of the redirect subdocuments with each document.
{
_id: new ObjectId("6317ad944f0f1db567b9608c"),
redirect: [ [Object] ],
user: new ObjectId("6310a2da50448e982336784a"),
__v: 0
}
qr.js (model)
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const qrSchema = new Schema({
redirect: [{
name: String,
url: String,
qrcode: String,
}],
user: {
type: Schema.Types.ObjectId,
ref: 'User'
},
event: {
type: Schema.Types.ObjectId,
ref: 'Event'
},
});
module.exports = mongoose.model('Qr', qrSchema);
How do I yield the full qr document and subdocument details from my query?

How to find a document of an ID from an array in JavaScript (Mongoose)

I have a function on my server that is supposed to get a post by its ID. The function works up until the "foundPost" constant, where I can't seem to find one of the documents from the "posts" array. I've tried substituting findOne for find and the ObjectIds work for the const 'post'.
I've double checked that post_id is 62067c1211eea1531d5872f4
Here is the function to find a post:
const postById = async (req, res) => {
const userId = req.params.userId;
const post_id = req.params.post_id;
const posts = await Post.findOne({ user: userId });
console.log(posts); //see this below
const foundPost = await posts.findOne({ "upload": post_id }); //error here
console.log(foundPost);
return res.json({ success: true, Post: foundPost });
};
Here is what 'console.log(posts)' returns:
[
{
upload: new ObjectId("623b681bdf85df9086417723"),
edited: false,
title: 'Test 1',
description: 'testing post 1',
name: 'John ',
sharedPost: 0,
},
{
upload: new ObjectId("62067c1211eea1531d5872f4"),
edited: false,
title: 'Test 2',
description: 'testing post 2',
name: 'John ',
sharedPost: 0,
}
]
I'm hoping that the function will return:
{
success: true,
{
upload: new ObjectId("62067c1211eea1531d5872f4"),
edited: false,
title: 'Test 2',
description: 'testing post 2',
name: 'John ',
sharedPost: 0,
},
}
Can anyone see why the line const foundPost = await posts.findOne({ "upload": post_id }); isn't working?
Thank you for your help.
****** Response to answer ******
Hello, thanks a lot for your answer, unfortunately it's still giving an error. Please see below the model for the code I'm using:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
post: [
{
user: {
type: Schema.Types.ObjectId,
ref: "user",
},
upload: {
type: Schema.Types.ObjectId,
ref: "upload",
},
title: {
type: String,
},
description: {
type: String,
},
},
],
date: {
type: Date,
default: Date.now,
},
});
I noticed that you used 'userProfile.posts' which I adapted to 'userProfile.post' to match this schema. I'm also not sure if you wanted to use 'subdoc' or 'subDoc' in line 11 of your code, but I tried both with the same error for each. I determined that the code stuck at the const subDocs = userPosts.filter(filter); line. I've looked into the .filter method you've used and can't find any potential errors. Not sure what the issue is.
Thanks
The issue is that you cannot run another mongo query on objects that were result of a previous query.
//will return a single document if found, or null if not found.
const posts = await Post.findOne({ user: userId });
//this will not work because at this point 'posts' will be either a Document or null value
//So the object will not have the method 'findOne' available.
const foundPost = await posts.findOne({ "upload": post_id });
The solution is to deal correctly with the types of objects you have.
Below is a functional and safe implementation that solves your issue:
const userPosts = await Post.findOne({ user: userId }).exec();
if (!userPosts) {
// document not found with provided userId
return res.json({ success: false });
}
//here we have a Document
//check if document has 'posts' property and is an array
if (userPosts.posts) {
//filter the posts array
const filter = function(subDoc) {
return subdoc.upload === post_id
}
const subDocs = userPosts.filter(filter);
//filter returns an array, so we must check if has itens
//then we grab the first item
if (subDocs.length > 0) {
const foundPost = subDocs[0];
return res.json({ success: true, Post: foundPost });
}
//subDoc not found, return correct response
return res.json({ success: false });
}
If your Post model schema is what I'm supposing to be, this code will work perfectly.
const schema = mongoose.schema({
user: Number,
posts: [{ upload: Number }]
})
In case of error, please add the code of the model schema structure.

delete mongoDB item using model after a setTime

I am storing chat app messages in MongoDB. After X time i would like them to delete themselves.
Where in the code do i add the line from the Docs
{expireAfterSeconds: x }
My code for creating the item is
try {
MessageModel.create({
username: user.username,
text: msg,
time: moment().format('h:mm a'),
room: user.room
})
} catch (error) {
// do stuff
}
and my model is set out as below
const MessageSchema = new mongoose.Schema(
{
userName: String,
text: String,
time: String,
room: String
},
{ collection: 'messages' }
)
const messageModel = mongoose.model('MessageSchema', MessageSchema)
Do I add the code to the model? or as a second argument to the create method?
Thanks in advance
The MongoDB TTL collection feature is set by using an index.
First, modify your time-field to store a timestamp as a valid date type. You can use moment().toISOString()
const MessageSchema = new mongoose.Schema(
{
userName: String,
text: String,
time: String,
room: String,
},
{ collection: 'messages' }
)
Set the TTL index like so
db.messages.createIndex( { "time": 1 }, { expireAfterSeconds: 3600 } )
For more information look at the docs

Removing element from nested array in Mongoose

I'm working on an upvoting/downvoting application using MongoDB and Node.JS
I have created two interlinked schemas:
var mongoose = require('mongoose');
var Voters = require('./voters');
var PostSchema = new mongoose.Schema({
title: String,
link: String,
upvotes: {type: Number, default: 0},
voters: [Voters.schema],
comments: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }]
});
mongoose.model('Post', PostSchema);
and for voters:
var mongoose = require('mongoose');
var votersSchema = new mongoose.Schema({
voter_id: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
votetype: Number
});
module.exports = mongoose.model('Voters', votersSchema);
For including users in the voters array, I'm using this code:
var voterModel = new Voters();
voterModel.voter_id = req.payload._id;
voterModel.votetype = 1;
foundPost.voters.push(voterModel);
foundPost.save();
Which works just fine. For removing users I tried several methods, but none seem to work. The current one is $pull:
foundPost.update({'voters.voter_id': req.payload._id}, {$pull: {'voters': {'voter_id': req.payload._id, 'votetype': 1}}}, function(err){
if (err) { console.log(err); }
});
The update action works in the mongo shell, but not from within node. I also tried foundPost.voters.remove, but the result was the same. Also tried Voters.findOne, but the query always returns null.
Any help would be appreciated.
Use the id method first to find the voter then remove it and last save document to apply changes:
var voter = foundPost.voters.id(req.payload._id).remove();
foundPost.save(function (err) {
if (err) return handleError(err);
console.log('the voter was removed')
});

Mongoose: Merging two documents that reference each other

I am currently trying to learn how to work with NoSQL, coming from a relational database background. In this project, I am using Express with Mongoose.
I am struggling with callbacks as I try to merge two models together, which reference each other. I am trying to edit each item in a group of one model (Ribbits) to contain the attributes of another (Users who posted a Ribbit). Because the call to find the User associated with a Ribbit is asynchronous, I am unable to return the collection of edited Ribbits (with user info).
In my website, I have ribbits (a.k.a. tweets) which belong to users. Users can have many ribbits. In one of my pages, I would like to list all of the ribbits on the service, and some information associated with the user who posted that ribbit.
One solution I found was embedded documents, but I discovered that this is, in my case, limited to showing ribbits which belong to a user. In my case, I want to start by getting all of the ribbits first, and then, for each ribbit, attach info about who posted that.
Ideally, I'd want my schema function to return an array of Ribbit objects, so that I can then render this in my view.
// models/user.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var userSchema = Schema({
username: String,
email: String,
password: String,
name: String,
profile: String,
ribbits: [{
type: Schema.Types.ObjectId,
ref: 'Ribbit',
}]
});
module.exports = mongoose.model('User', userSchema);
// models/ribbit.js
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
User = require('./user');
var ribbitSchema = Schema({
content: { type: String, maxlength: 140 },
created: { type: Date, default: Date.now() },
owner: { type: Schema.Types.ObjectId, ref: 'User' },
});
ribbitSchema.methods.getOwnerObj = function(cb) {
return User.findOne({ _id: this.owner }, cb);
}
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}, function(err, ribbits) {
console.log('Before Transform');
console.log(ribbits);
ribbits.forEach(function(ribbit) {
ribbit.getOwnerObj(function(err, owner) {
ribbit = {
content: ribbit.content,
created: ribbit.created,
owner: {
username: owner.username,
email: owner.email,
name: owner.name,
profile: owner.profile,
}
};
});
});
});
}
module.exports = mongoose.model('Ribbit', ribbitSchema);
If I understand correctly, you can use Mongoose populate method for this scenario:
ribbitSchema.statics.getAllRibbits = function(cb) {
this.find({}).populate('owner').exec(function(err, ribbits){
console.log(ribbits[0].owner)
return cb(err, ribbits);
})
}

Categories

Resources