Mongoose pushing many documents into a document field - mongodb

I'm trying to push many documents into a field to populate my mongo db, but for I keep getting the following error: SyntaxError: Invalid property id
This is the query I'm trying to do,
db.stripeproducts.findOneAndUpdate(
{ stripeProductId: 'prod_Ijq9Y1uiFV0bSl' },
{$push: {
bundlePhotoCodes:
{value: 'XFTYUONMBUNDLE', status: 'valid', validUntil: null, modelID: null },
{value: 'XFTYUONMBUNDLE', status: 'valid', validUntil: null, modelID: null },
{value: 'YIHH878BUNDLE', status: 'valid', validUntil: null, modelID: null},
{value: 'HSGYGVI8BUNDLE', status: 'valid', validUntil: null, modelID: null},
{value: 'HSGYGVI8BUNDLE', status: 'valid', validUntil: null, modelID: null},
{value: 'BMDUKKSBUNDLE', status: 'valid', validUntil: null, modelID: null},
{value: 'XFTYUONMBUNGTY', status: 'valid', validUntil: null, modelID: null },
{value: 'YIHH878BUNTRD', status: 'valid', validUntil: null, modelID: null},
{value: '88UJHDI8BUNRSD', status: 'valid', validUntil: null, modelID: null}
{value: 'HSGYGVI8BUNQSD', status: 'valid', validUntil: null, modelID: null},
{value: 'HSGYGVI8BUNDDF', status: 'valid', validUntil: null, modelID: null},
{value: 'BMDUKKSBUNDFFF', status: 'valid', validUntil: null, modelID: null}
}},
{upsert: true}
)
This is my schema:
const stripeProductSchema = new mongoose.Schema(
{
productName: String,
productPrice: Number,
productDescription: [{ type: String }],
productImage: String,
stripeProductId: String,
stripePriceId: String,
rate: String,
amount: String,
bundlePhotoCodes: [photoCodes],
modelPayment: {
type: Schema.Types.ObjectId,
ref: 'ModelPayment',
required: false,
},
},
{ strict: true, timestamps: true },
)

Your syntax is invald. To append multiple values to an array, you need to use $push with $each. For example:
db.stripeproducts.findOneAndUpdate(
{ stripeProductId: 'prod_Ijq9Y1uiFV0bSl' },
{
$push: {
bundlePhotoCodes: {
$each: [
{value: 'XFTYUONMBUNDLE', status: 'valid', validUntil: null, modelID: null},
{value: 'XFTYUONMBUNDLE', status: 'valid', validUntil: null, modelID: null},
...
]
}
}
},
{upsert: true}
)

Related

Mongoose/Mongodb Aggregate - group and average multiple fields

I have a Post model with 2 fields : date and rating. How would I go about getting an average aggregate rating for each date? So group by date first and then average the rating across all posts for that date. I need to do this within mongoose but their docs are so difficult to understand.
const PostSchema = new Schema({
date: {
type: String,
default: getToday() //this is just a new Date() formatted
},
rating: {
type: Number,
required: true
}
},
)
This gives me the average across all dates but I can't figure out how to filter it by date:
Post.aggregate([
{ $group: { _id: null, avgRating: { $avg: '$rating' }}}
])
.then(function (res) {
console.log(res[0]["avgRating"]);
})
This worked for me:
Post.aggregate([
{ $group: { _id: "$date", avgRating: { $avg: '$rating' }}}
]).
then(function (res) {
console.log(res);
})
Output:
[
{ _id: 'Aug 18, 2021', avgRating: 3.0212234706616727 },
{ _id: 'Aug 19, 2021', avgRating: 2.9680319680319682 },
{ _id: 'Aug 20, 2021', avgRating: 3.023976023976024 },
{ _id: 'Aug 17, 2021', avgRating: 2.9600665557404326 },
{ _id: 'Aug 21, 2021', avgRating: 3.072661217075386 }
]
BUT it would be great if I could somehow filter this based on other factors. For example, each post has an author (reference to User model). How would I go about filtering based on the author's country.name or gender?
User model:
const userSchema = new Schema({
email: {
type: String,
required: true,
unique: true
},
birthday: {
type: Date,
required: true,
},
gender:{
type: String,
required: true
},
country:{
name: {
type: String,
required: true
},
flag: {
type: String,
// default: "/images/flags/US.png"
}
},
avatar: AvatarSchema,
displayName: String,
bio: String,
coverColor: {
type: String,
default: "#343a40"
},
posts: [
{
type: Schema.Types.ObjectId,
ref: "Post"
}
],
comments: [
{
type: Schema.Types.ObjectId,
ref: "Comment"
}
],
postedToday: {
type: Boolean,
default: false
},
todaysPost: {
type: String
}
})
Something like this
Post.aggregate([
{$match: {"date": today}},
{$group: {_id: {"country": "$author.country.name"}, avgRating: {$avg: "$rating"}}}
]).then(function(res) {
console.log(res)
})

MongoDB: Add field from Aggregation Output to Query

I would like to perform an aggregation query, then a find query, and apply the output of the aggregation as a new field in the find results, ie:
A have dataset like this:
{id: 1, city: "Paris", comment: "...", status: "Active"},
{id: 2, city: "London", comment: "...", status: "Active"},
{id: 3, city: "Paris", comment: "...", status: "Active"},
{id: 4, city: "New York", comment: "...", status: "Active"},
{id: 5, city: "London", comment: "...", status: "Active"},
{id: 6, city: "London", comment: "...", status: "Active"},
{id: 7, city: "London", comment: "...", status: "Disabled"}
I want to get the counts for each active city:
collection.aggregate([
{$match: {status: "Active"}},
{$group: {_id: "$city", count: {$sum: 1}}}
])
But I would like to apply the count to each entry, matched according to city. It would return something like this:
{id: 1, city: "Paris", comment: "...", status: "Active", count: 2},
{id: 2, city: "London", comment: "...", status: "Active", count: 3},
{id: 3, city: "Paris", comment: "...", status: "Active", count: 2},
{id: 4, city: "New York", comment: "...", status: "Active", count: 1},
{id: 5, city: "London", comment: "...", status: "Active", count: 3},
{id: 6, city: "London", comment: "...", status: "Active", count: 3},
{id: 7, city: "London", comment: "...", status: "Disabled", count: 3}
Ideally I would like to do this in a single query so that it can be sorted and paginated according to count.
$group by city and push root object to a root field, count status that is Active only
$unwind deconstruct root array
$mergeObjects to merge $root object and count field
$replaceRoot to replace merged object to root
db.collection.aggregate([
{
$group: {
_id: "$city",
root: { $push: "$$ROOT" },
count: {
$sum: {
$cond: [{ $eq: ["$status", "Active"] }, 1, 0]
}
}
}
},
{ $unwind: "$root" },
{
$replaceRoot: {
newRoot: { $mergeObjects: ["$root", { count: "$count" }] }
}
}
])
Playground

how to query mongoose based on OR condition

i have a transaction collection and i require to query this from mongoose
_id: 5ecba0d446d0354084ad0b89
amount: 3
userId: 5ec3285cc7762963c88db765
type: 4
userType: 1
recipientId: 5ec328f2c7762963c88db768
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
_id: 5ecba0d446d0354084ad0b92
amount: 4
userId: 5ec3285cc7762963c88db888
type: 4
userType: 1
recipientId: 5ec3285cc7762963c88db765
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
_id: 5ecba0d446d0354084ad0b97
amount: 8
userId: 5ec3285cc7762963c88db332
type: 4
userType: 1
recipientId: 5ec328f2c7762963c88db589
status: "succeeded"
createdAt: 2020-05-25T10:41:24.449+00:00
updatedAt: 2020-05-25T10:41:24.449+00:00
__v: 0
how to query this such that i can get the transactions based on following condition
userId = 5ec3285cc7762963c88db765 or recipientId = 5ec3285cc7762963c88db765
and type = 4
and userType = 1
use $or and $and operators
model.find({
$or: [
{ userId: 5ec3285cc7762963c88db765 },
{ recipientId: 5ec3285cc7762963c88db765 }
],
$and: [ { type: 4 }, { userType: 1 } ]
});

Aggregate string values to array of unique string values per field with mongo or mongoose and node.js

I need to aggregate "lastNames" and "occupations" for a "name" to get a result:
{
name: 'John',
occupations: ['software engineer', 'qa']
lastNames: ['Smith', 'Red', 'Doe']
}
input
name: 'John'
documents present in mongo:
{name: 'John', lastName: 'Smith', occupation: 'software engineer'}
{name: 'Steve', lastName: 'Smith', occupation: 'senior software engineer'}
{name: 'John', lastName: 'Doe', occupation: 'qa'}
{name: 'Steve', lastName: 'Doe', occupation: 'manager'}
{name: 'John', lastName: 'Red', occupation: 'software engineer'}
I started with this aggregation query:
Employees.aggregate([
{ $match: { name: name } },
{
$unwind: {
path: '$lastName',
},
},
{
$unwind: {
path: '$occupation',
},
},
{ $group: { _id: '$name' } },
]);
but this returns an empty array, so I kinda stuck as I never did aggregations before.
Is there a way to produce this required result?
Would be this one:
db.collection.aggregate([
{ $match: { name: "John" } },
{
$group: {
_id: "$name",
occupations: { $addToSet: "$occupation" },
lastNames: { $addToSet: "$lastName" },
}
},
{
$project: {
_id: 0,
name: "$_id",
occupations: 1,
lastNames: 2
}
}
])
Mongo playground

Mongoose update push, delete in array

I have a mongoose model:
var schema = new Schema({
loginName: {
type: String,
unique: true,
required: true
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
},
created: {
type: Date,
default: Date.now
},
rooms: [{ _id: Schema.Types.ObjectId, loginName: [{ type: String }] }]
});
Example result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
What I need:
search on field : rooms._id in every user document,
push new loginName in array loginName in every found user document
delete selected loginName in array
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy", "John"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
Example params:
rooms._id : 56c0a986eeb118741109a461
loginName: "John"
Result:
{
_id: "56c0a986eeb118741109a45f",
loginName: "MegaDaddgy",
hashedPassword: "*****",
salt: "******",
__v: 10,
rooms: [
{
_id: "56c0a986eeb118741109a461",
loginName: [
"MegaDaddgy"
]
},
{
_id: "56c0d9e332f6ddc80ec7271c",
loginName: [
"MegaDaddgy"
]
}
],
created: "2016-02-14T16:21:26.272Z"
}
How can I do this?
You could push John into loginName array through $push
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$push: {'rooms.$.loginName': 'John'}}, function(...));
delete John from loginName array through $pull
Model.update({'rooms._id': ObjectId('56c0a986eeb118741109a461')},
{$pull: {'rooms.$.loginName': 'John'}}, function(...));