deleteMany not working with $size operator - mongodb

I am trying to delete all collections where nested document "nested" has 0 element.
Tag.deleteMany({ "blog": { $size: 0 } }).exec()
For some reason, it doesn't work with Mongoose,
but it works when I run it this in Robo
db.getCollection('tags').deleteMany({ "blog": { $size: 0 } })
Anybody knows why it works in the query shell but not with Mongoose code?
Here's the Schema.
var tagSchema = new mongoose.Schema({
tag: String,
created: { type: Date, default: Date.now },
blog: [{ type: mongoose.Schema.Types.ObjectId,
ref: "blog" }]
var blogSchema = new mongoose.Schema({
title: String,
image: String,
description: String,
body: String,
created: { type: Date, default: Date.now },
tag:[{ type: mongoose.Schema.Types.ObjectId,
ref: "tag" }]
UPDATE... I think it is now Promise chaining issue?
let foundBlog
Blog.findOne({ title: '1st Post' })
.then((blog) => {
foundBlog = blog;
})
.then(() => {
console.log(foundBlog.tag)
Tag.updateMany(
{ _id : { $in: foundBlog.tag} },
{ $pull: { blog: foundBlog._id.toString()} }).exec()
})
.then(() => {
Tag.deleteMany({ "blog": { $size: 0 } }).exec()
})
.then(() => done())
For some reason Tag.deleteMany did not work after Tag.updateMany.
Is my promise chain correct? Thanks

Try chaining this way:
let foundBlog
Blog.findOne({ title: '1st Post' })
.then((blog) => {
foundBlog = blog;
})
.then(() => {
console.log(foundBlog.tag)
Tag.updateMany(
{ _id : { $in: foundBlog.tag} },
{ $pull: { blog: foundBlog._id.toString()} })
.then(() => {
Tag.deleteMany({ "blog": { $size: 0 } }).then(() => done())
})
})

Related

Mongo How to use select to return multiple selected properties from document?

I am using findOneAndUpdate, where I want
to return updated document
i dont want to return the entire document but only the following:
one object out of an array + a virtual property in the document.
const notifications = {
to:
messages: [
{_id: "23452", title:"hello"}, {_id: "23452", title:"bye"}
]
...
}
so for example I would want to only return the object {_id: "23452", title:"bye"} AND unreadCount virtual field prop.
my code works so far as I am returning updated document and only the message I want, but I dont know how to return also the unreadCount prop.
schema:
const notificationSchema = new mongoose.Schema({
to: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
messages: [{
title: {
type: String,
required: true
},
isRead: {
type: Boolean,
default: false
},
createdAt: {
type: Date,
default: new Date()
}
}, ]
},
{timestamps: true, toObject: {virtuals: true}
});
notificationSchema.virtual('unreadCount').get(function() {
... return count;...
})
updateRead: async (userId, id) => {
const notification = await Notification.findOneAndUpdate({to: userId, 'messages._id': id}, {
$set: { "messages.$.isRead": true} },
{ select: {
messages: {
$elemMatch: {_id: id}
}
}, new: true});
}

Get results of aggregation query in mongoose using objectId, virtual types (it works in mongo shell)

My code on the backend, in case it matters (NodeJS and MogoDB):
//my includes at the top of the file
const mongoose = require('mongoose');
const Appt = mongoose.model('Appt');
const ApptType = mongoose.model('ApptType');
const ApptStatus = mongoose.model('ApptStatus');
var moment = require('moment-timezone');
moment().tz('America/New_York');
now = moment(); // add this 2 of 4
dayStart = now.startOf('day');
dayEnd = now.endOf('day');
// the aggregation query that's not returning correctly
Appt.aggregate([
{
$match: {
patientID: appt.patientID._id,
scheduled: {
$gte: new Date(start),
$lt: new Date(appt.pmtduedate)
}
}
},
{
$group: {
_id: 'id',
payment: { $sum: '$payment' },
pmtdue: { $sum: '$pmtdue' },
visits: { $sum: 1 }
}
}
]).exec(
err => {
console.log(`Error finding past payments`, err);
callback(err);
},
result => {
console.log(`RESULT: ${result}`);
pastPayments = result;
if (!pastPayments || pastdueamt === 0) {
pastdueamt = 0;
console.log(`2. getCurrentDue ${pastdueamt}`);
this.getCurrentDue(appt, pastdueamt, today, callback);
} else {
console.log(`pastPayments ${pastPayments}`);
console.log(
`planamt ${planamt} pmtdue ${pastPayments.pmtdue} payments: ${pastPayments.payment}`
);
pastdueamt =
pastPayments.pmtdue === 0
? planamt - pastPayments.payment
: pastPayments.pmtdue - pastPayments.payment;
console.log(`pastdueamt calculated: ${pastdueamt}`);
console.log(`2. getCurrentDue`);
this.getCurrentDue(appt, pastdueamt, today, callback);
}
}
);
When I run my query in mongo, the expected results return. In my app, the results of this query above return nothing (no error, either). I've tried doing the following:
$match: {
patientID: new mongoose.types.ObjectId(appt.patientID._id),
I've also tried:
$match: {
patientID: { $toObjectId: appt.patientID._id },
but I get errors on both of these options. The first returns an error of
TypeError: Cannot read property 'ObjectId' of undefined.
The second returns some sort of mongo error
errmsg: 'unknown operator: $toObjectId',
code: 2,
codeName: 'BadValue',
name: 'MongoError',
[Symbol(mongoErrorContextSymbol)]: {} }
How do I do mongoose aggregation successfully using objectIds, virtual types, etc.?
EDITED TO ADD MY SCHEMAS:
const apptSchema = new mongoose.Schema(
{
ID: Number,
patientID: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Patient'
},
oldPatientID: Number,
status: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ApptStatus'
},
type: {
type: mongoose.Schema.Types.ObjectId,
ref: 'ApptType'
},
scheduled: Date,
note: String,
reminder: Boolean,
cell: Boolean,
email: Boolean,
subjective: String,
assessment: String,
plan: String,
planamt: Number,
objective: {
clearUC: Boolean,
UCcheck: String,
thompson: String,
activator: String,
other: String
},
updated: {
type: Date,
default: new Date()
},
pmtdue: Number,
pmtduedate: Date,
payment: Number,
pmttype: String,
paid: Boolean,
pmtnote: String
},
{ toJSON: { virtuals: true } }
);

How can I get total length(count) of all comments inside Post?

Let's say I have this Schema:
const PostSchema = new Schema({
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
comments: [
{
user: {
type: Schema.Types.ObjectId,
ref: 'users'
},
text: {
type: String,
required: true
},
date: {
type: Date,
default: Date.now
}
}
],
date: {
type: Date,
default: Date.now
}
});
And let's say I save 10 posts to the database. How can I get total length(count) of all comments they have?
This works only for whole collection of Post. It returns 10.
router.get( '/total', ( req, res ) => {
Post.estimatedDocumentCount().then( ( totalCount) => {
res.json( totalCount );
}).catch( ( err ) => {
console.log( err );
});
});
I don't want to use .count() method since it's deprecated.
Thank you
You can use $group and find total counts of comments as below:
db.collection.aggregate([
{
$group: {
_id: null,
count : { $sum: { $size: "$comments"}}
}
}
])

Why do I get array of nulls in my database? [duplicate]

This question already has answers here:
Node.js Mongoose.js string to ObjectId function
(9 answers)
Closed 4 years ago.
I have an array of ids which is launchIds.
I'm trying to push it on a model field trips with
$addToSet: { trips: { $each: launchIds }. This gives me an error: Cast to [ObjectId] failed for value \"[\"1\",\"2\",\"3\"]\...
if I try to map through launchIds and convert to Mongoose.Shema.Types.ObjectId I get in the database trips: [null,null,null]
lauchIds = ['1','2','3']
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const mongoIds = launchIds.map(l => Mongoose.Schema.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$addToSet: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Here's my model Schema:
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
I'm passing ids via grapql playground. Here's my mutation:
bookTrips: async (_, { launchIds }, { dataSources }) => {
console.log(launchIds);
// logs ['1','2','3']
console.log(typeof launchIds);
//Object
const results = await dataSources.userAPI.bookTrips({ launchIds });
console.log(results);
return { message: "hello" };
}
To convert a string or a number into mongo object use Mongoose.Types.ObjectId,
const mongoIds = launchIds.map(l => Mongoose.Types.ObjectId(l));
I was getting back an array of strings where this should be numbers
The solution:
My model (same as above):
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
crud API:
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const idsToNums = launchIds.map(Number);
const mongoIds = idsToNums.map(l => Mongoose.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$push: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Notice the Mongoose.Schema.Types.ObjectId on model and Mongoose.Types.ObjectId on api. If I remove Schema from model or add Schema to api I'm getting an error. Not sure why, but the above example works. I hope someone will find this helpful or suggests a better solution.

How to Check current user's vote before votes are grouped and sumed in same aggregate function

var PostSchema = new mongoose.Schema({
item: {
type: mongoose.Schema.ObjectId,
ref: 'item',
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
vote: {
type: Number,
default: 0
},
total: {
type: Number,
default: 0
},
awsPostKey: {type: String},
picture: {type: String, required: true}
});
var data = function(){
return Post
.find({})
.then(function(post){
return post;
})
};
var userId = //mongo objectId for current user
//postVote schema:
var PostVoteSchema = new mongoose.Schema({
post: {
type: mongoose.Schema.ObjectId,
ref: 'Post',
required: true
},
user: {
type: mongoose.Schema.ObjectId,
ref: 'User',
required: true
},
vote: {
type: Number,
default: 0
}
});
//pass data from Post query to PostVote sum function:
PostVoteSchema.statics.sum = function (data, userId) {
var postIds = data.map(function (a) {
return a._id;
});
return PostVote
.aggregate(
[
{ $match: { 'post': {$in: postIds}}},
{ $group: { _id:'$post' ,vote:{$sum:'$vote'}}}
])
.execAsync()
.then(function(votes){
return votes;
//desired output to client, _id is for specific post
{_id: 5802ea4bc00cb0beca1972cc, vote: 3, currentUserVote: -1}
});
};
I'm successfully able to get the total sum of all votes with the same postId.
Now, I"m wanting to see if the current user (userId) has placed a vote for the given post as well, then to return how they voted (+1 or -1) along with the sum of all votes for the specific post.
Is it possible to do this, or will I have to do this outside of my aggregate pipeline -- within a second query? It just seems potentially taxing to have to query the collection again.
Yes, that's possible. Within the $group pipeline, you can use the $cond operator as the logic for feeding the $sum accumulator operator. For example:
return PostVote.aggregate([
{ "$match": { "post": { "$in": postIds } } },
{
"$group": {
"_id": "$post",
"votes": { "$sum": "$vote" },
"userVotes": {
"$sum": {
"$cond": [
{ "$eq": ["$user", userId] },
"$vote",
0
]
}
}
}
}
]).execAsync().then(function(votes){
return votes;
});