MongoDB remove nested objects - node.js

I have a collection like this:
_id: {name: 'name', family: 'family'}
i want to remove some objects by _id using by $in, how i can do this?
for example my query should be something like:
db.persons.remove({_id: {$in: [ { name: 'name1', family: 'family1' }
, { name: 'name2', family: 'family2' }
]
}
})

You can also do this with an $or query and dot notation if your fields are not always in the same order:
db.persons.remove({
"$or": [
{ "_id.name": "name1", "_id.family": "family1" },
{ "_id.name": "name2", "_id.family": "family2" },
}
})
Not the mongoose syntax, but you get the idea. It is logically the same thing but the field order is not dependent as it is with the full object you are specifying to $in.

Related

MongoDB adding ObjectId to array of IDs

I have Poems schema which has a linked array of ObjectIds under field name 'communities':
{
_id: ObjectId("61659ef70e87b90018f7baa1"),
schemaName: 'Poem',
helps: [ ObjectId("5d15c609832d390c41ab6872") ],
communities:
[ ObjectId("5eafbabaf0be6f0017303eb3"),
ObjectId("5eba549a45bd9300170f6311") ],
}
I am trying to add a new ObjectId to the array using updateOne and $push:
db.poems.updateOne(
{title: "My stillness"},
{$push: {communities: {ObjectId: ('61f942b737bdc10018722539')}}}
)
While the new Id gets added, it is not in the correct format (see also attached image from MongoDB Compass for further clarity on the difference in format). How can I adjust my updateOne/$push method to add the ObjectId in the correct format? Thanks
{
_id: ObjectId("61659ef70e87b90018f7baa1"),
schemaName: 'Poem',
helps: [ ObjectId("5d15c609832d390c41ab6872") ],
communities:
[ ObjectId("5eafbabaf0be6f0017303eb3"),
ObjectId("5eba549a45bd9300170f6311"),
{ ObjectId: '61f942b737bdc10018722539' } ],
}
You are pushing key-value pair into the array.
ObjectId: ('61f942b737bdc10018722539')
Instead, it should be:
ObjectId('61f942b737bdc10018722539')
db.poems.updateOne(
{ title: "My stillness" },
{ $push: { communities: ObjectId('61f942b737bdc10018722539') } }
)
Sample Mongo Playground

mongodb aggregate in array and group [duplicate]

Is there an easy way to "$push" all fields of a document?
For example:
Say I have a Mongo collection of books:
{author: "tolstoy", title:"war & peace", price:100, pages:800}
{author: "tolstoy", title:"Ivan Ilyich", price:50, pages:100}
I'd like to group them by author - for each author, list his entire book objects:
{ author: "tolstoy",
books: [
{author: "tolstoy", title:"war & peace", price:100, pages:800}
{author: "tolstoy", title:"Ivan Ilyich", price:50, pages:100}
]
}
I can achieve this by explicitly pushing all fields:
{$group: {
_id: "$author",
books:{$push: {author:"$author", title:"$title", price:"$price", pages:"$pages"}},
}}
But is there any shortcut, something in the lines of:
// Fictional syntax...
{$group: {
_id: "$author",
books:{$push: "$.*"},
}}
You can use $$ROOT
{ $group : {
_id : "$author",
books: { $push : "$$ROOT" }
}}
Found here: how to use mongodb aggregate and retrieve entire documents
Actually you cant achieve what you are saying at all, you need $unwind
db.collection.aggregate([
{$unwind: "$books"},
{$group: {
_id: "$author",
books:{$push: {
author:"$books.author",
title:"$books.title",
price:"$books.price",
pages:"$books.pages"
}},
}}
])
That is how you deal with arrays in aggregation.
And what you are looking for to shortcut typing all of the fields does not exist, yet.
But specifically because of what you have to do then you could not do that anyway as you are in a way, reshaping the document.
If problem is that you don't want to explicitly write all fields (if your document have many fields and you need all of them in result), you could also try to do it with Map-Reduce:
db.books.mapReduce(
function () { emit(this.author, this); },
function (key, values) { return { books: values }; },
{
out: { inline: 1 },
finalize: function (key, reducedVal) { return reducedVal.books; }
}
)

How to return field based on other fields with mongoose

I have mongoose schema that looks something like this:
{
_id: someId,
name: 'mike',
keys: {
apiKey: 'fsddsfdsfdsffds',
secretKey: 'sddfsfdsfdsfdsds'
}
}
I don't want to send back to the front the keys of course, but I want some indication, like:
{
_id: someId,
name: 'mike',
hasKeys: true
}
There is built in way to create 'field' on the way based on other fields, or do I need every time fetch the whole document, check if keys is not empty and set object property based on that?
For Mongo version 4.2+ What you're looking for is called pipelined updates, it let's you use a (restricted) aggregate pipeline as your update allowing the usage of existing field values.
Here is a toy example with your data:
db.collection.updateOne(
{ _id: someId },
[
{
"$set": {
"hasKeys": {
$cond: [
{
$ifNull: [
"$keys",
false
]
},
true,
false
]
}
}
},
])
Mongo Playground
For older Mongo versions you have to do it in code.
If you don't want to update the actual document but just populate this field when you fetch it you can use the same aggregation to fetch the document
you can use $project in mongoose aggregation like this.
$project: { hasKeys: { $cond: [{ $eq: ['$keys', null] }, false, true]}}

Getting first element of embedded array in mongoDB using Node JS driver

Let's say I have the following document stored in a mongoDB collection 'people':
{
_id: 489324,
name: "Ryan Jones"
skills: [ "fishing", "programming" ]
}
I am trying to retrieve Ryan Jones's first skill in the array (skills[0]).
This seems like a dead simple operation but I can't seem to do it using the Node JS driver. I can retrieve just the skills array easily:
db.collection('people').findOne({ name:"Ryan Jones"},{ projection: { skills:1 }})
...but I don't want to transfer the whole array over the wire. I just want to get "fishing".
I have tried using slice and arrayElemAt within projection but I get a MongoError. How can I achieve this using the NodeJS driver? Does it require a more complex aggregation operation instead of findOne?
You can achieve that with aggregation , with $arrayElemAt something like this
db.collection('people').aggregate([
{
$match: {
name: "Ryan Jones"
}
},
{
$project: {
name: 1,
skills: {
$arrayElemAt: [
"$skills",
0
]
},
}
}
])
See demo here
Try this one:
db.collection('people').findOne({
name: "Ryan Jones"
},
{
skills: {
$slice: 1
}
})
MongoTemplate with .find

MongoDB Searching if element exist in array

So I have something like Survey Schema (I am using mongoose).
In this Schema, for each voting option, I have votes[] array that contains ObjectIds of Users.
Now I want to check if User can vote again, or if he already voted?
The simple solution is iterating thru votes with .indexOf() and checking if id exists. Now this is a blocking way for Node JS, since this operation is sync.
Is there a way to do this with Mongo aggregate or query? So for each voting option I would get additional field like:
didIVoted: true
My Schema looks like this:
const SurveySchema = new Schema({
title: {
type: String
},
options: [{
value: String,
votes: [{ type: mongoose.Schema.Types.ObjectId, ref: 'User' }]
}]
}, { timestamps: true })
You can use $addFields and $map to overwrite existing options field. To check if userId exists in votes array you can use $indexOfArray
SurveySchema.aggregate([
{
$addFields: {
options: {
$map: {
input: "$options",
in: {
value: "$$this.value",
votes: "$$this.votes",
didIVote: { $ne: [ { $indexOfArray: [ "$$this.votes", userId ] }, -1 ] }
}
}
}
}
}
])

Resources