Mongodb: limit 2-level nested document - mongodb

Mongod deb albums collection contains such items:
var album = {
name: 'album1',
tracks: [{
title: 'track0',
language: 'en',
processing: {
tasks: [
{_id: 1, name: 'someTask1'},
{_id: 2, name: 'someTask2'},
]
}
},{
title: 'track1',
language: 'en',
},{
title: 'track2',
language: 'es',
}]
}
I need to select only one Album, track0 and task with _id 1, so that result set would be looking like (contains only one track and only one task)
{
name: 'album1'
tracks: [{
title: 'track0',
language: 'en',
processing: {
tasks: [
{_id: 1, name: 'someTask1', },
]
}
]
}
Is it possible to that without aggregation framework just using find?
I tried $elemMatch and .$ projection to limit output, but it seems that it doesn't work on nested levels > 1 (tasks in that case) =(

Related

MongoDB Select documents by the value of a specific field

MongoDB - userModel (simplified)
{
_id: "exampleid1",
name: "examplename1",
following: [
{
_id: "exampleid2",
name: "examplename2"
},
{
_id: "exampleid3",
name: "examplename3"
}
],
followers: [
{
_id: "exampleid4",
name: "examplename4"
},
{
_id: "exampleid5",
name: "examplename5"
}
]
}
Hey,
I'm building a social media platform and I need to get from the database only the users that I follow.
I tried the query below, but gives me nothing:
User.find( { "followers": { _id: "exampleid1"} } )
Try to divide the document selection ({_id: "exampleid1"}) from the projection ({followers: 1, _id: 0}):
db.collection.find({
_id: "exampleid1"
},
{
followers: 1,
_id: 0
})
See how it works on the playground example

Mongoose complex query for search

This is my document, where user register and create their store and in each store user can add books now anonymous user want to fetch the book by id or all the books available in system
[
{
_id: ObjectId("61c0a1a895a761d0fe1a1408"),
storeName: 'Rose Book Store',
ownerName: 'Rose Ahmed',
books: [
{
name: 'economics',
totalStock: 12,
stockStatus: true,
_id: ObjectId("61c20d9fcfef88e51e37bdaa")
}
],
createdAt: ISODate("2021-12-21T17:02:30.221Z"),
__v: 0
},
{
_id: ObjectId("61c17c1481adc44ea10c6083"),
storeName: 'Toufik Book Store',
ownerName: 'Toufik Ahmed',
books: [
{
name: 'Database',
totalStock: 14,
stockStatus: true,
_id: ObjectId("61c217b72e2dc3395071bb26")
},
{
name: 'Database',
totalStock: 14,
stockStatus: true,
_id: ObjectId("61c217e92e2dc3395071bb2b")
},
{
name: 'MySql',
totalStock: 0,
stockStatus: false,
_id: ObjectId("61c218142e2dc3395071bb31")
}
],
createdAt: ISODate("2021-12-21T18:03:13.296Z"),
__v: 0
}
]
how can i get all books, expected output
books:[{
name:"Some_book",
totalStock:12,
stockStatus: true,
_id: "Some_id"
},
{
name:"Some_other_book"
totalStock:0,
stockStatus:false
_id: "Some_id"
}]
how can i get book by id when user enters any valid book _id
expected output in given below
books:{
name:"some_book",
totalStock: 2,
_id: "some object_id"}
Here i am sharing normal aggregation query https://mongoplayground.net/p/SOO_Ffia5-q
where we are just grouping all books into one document so you get all the books.
Same aggregation can be used to get the matching book based on id. https://mongoplayground.net/p/ILHMW-kPueD
in aggregation we used below stages:
match: filter book based on _id of book
unwind: unwind matched document as matched document may have other nonmatched booked in same user/store
match: filter unwinded books so we just get matched book
project: to just return book and not all fields
Using this sample queries you can easily create your mongoose equivalent code,

How to query multiple collections in mongodb (without using $lookup)?

I would like to create a single query that gets data from three different collections from providing a single query parameter. I have seen methods that use $lookup but I do not want to use that as I cannot use it on sharded collections.
Here is an example to explain further.
I have three collections: user, chatroom and chatMessage.
user collection:
{
_id: ObjectId('456'),
username: 'John',
contacts: [
{
_id: ObjectId('AB12'),
name: 'Mary',
idOfContact: ObjectId('123'),
},
{
_id: ObjectId('AB34'),
name: 'Jane',
_idOfContact: ObjectId('234'),
},
{
_id: ObjectId('AB56'),
name: 'Peter',
_idOfContact: ObjectId('345'),
}
],
}
chatroom collection:
{
_id: ObjectId('AB34'),
usersInThisChatRoom: [
ObjectId("456"),
ObjectId("123"),
ObjectId("234"),
]
}
chatMessage collection:
[
{
_id: ObjectId("M01"),
chatRoomObjectId: _id: ObjectId('AB34'),
senderObjectId: ObjectId('456'),
message: 'Hello humans!',
date: ISODate("2019-09-03T07:24:28.742Z"),
},
...(other messages)
]
What I would like to be returned
[
{
chatRoomObjectId: ObjectId('AB34'),
usersInThisChatRoom: [
{
contactName: 'John',
contactUserId: ObjectId('456'),
},
contactName: 'Mary',
contactUserId: ObjectId('123'),
},
contactName: 'Jane',
contactUserId: ObjectId('234'),
}
]
chatMessages: [
{
_id: ObjectId("M01"),
senderObjectId: ObjectId('456'),
message: 'Hello humans!',
date: ISODate("2019-09-03T07:24:28.742Z"),
},
...(other messages)
]
},
...(other documents)
]
Is there a way to get my desired results by making a single query using the user._id and will that be performance friendly?
Or, do I have to make several queries, one after another, to achieve what I want?
According to this answer, you cannot perform a single query across multiple collections (aside from the $lookup aggregation pipeline function.
This means that You either use the $lookup aggregation pipeline or you make several queries to the DB.

Mongodb how to use $elemMatch to limit results

My problem is: I have a structure similar to this:
{
id: 1,
participants: [
{ name: "joe", status: 0 },
{ name: "james", status: 2}
],
content: "mongomongo"
}
{
id: 2,
participants: [
{ name: "joe", status: 1 },
{ name: "jordan", status: 3}
],
content: "dongodongo"
}
What I want to do is run a query with almost the same effect as this:
db.find({ '_id': { $in: someArray}}, { participants: {$elemMatch: {'name': someName }}}
I would specify an array of object IDs for the $in, and then I would provide an username. What happens is that it would give me back both objects, but the participants array only has the entry that the $elemMatch found:
{
id: 1,
participants: [
{ name: "joe", status: 0 }
]
}
{
id: 2,
participants: [
{ name: "joe", status: 1 }
]
}
This is what I want, but the part that I DON'T want is that it leaves out other fields (namely content). How can I adjust the query so it that still returns one field in the participants array, but also returns the other fields such as content?
Thank you in advance!
Actually found the solution to my question. Just had to tweak the original query I used. I had confused the projection field and the options field since I was using Mongoose to manage mongodb interactions.
Here's the query that works:
db.find({ '_id': { $in: someArray}}, { participants: {$elemMatch: {'name': someName }}, content: 1, [anything] : 1});
EDIT:
I misunderstood the original post and example. If the only other field you are worried about returning is 'content', then you could add it to the projection argument like so:
db.collection.find(
{
'_id': {
$in: someArray
}
},
{
'participants': {
$elemMatch: {
'name': someName
}
},
'content' : 1
}
)
Hope this helps!

How to find a subdocument by id in mongoose and exclude some fields

I have the a document stored in mongodb:
shop: {
_id: '...'
title: 'my shop'
users: [
{
_id: '...',
name: 'user1',
username: '...'
},
{
_id: '...',
name: 'user2',
username: '...'
}
]
}
I use this query to get a subdocument user by his id:
Shop.findOne({'users._id': userId}, {'users.$': 1}, function (err, user) {
console.log(user);
});
Output:
{ _id: ...,
users:
[{
name: 'user1',
username: '...',
_id: ...
}]
}
How can I filter the result to only return the user name.
The way I do it now:
Shop.findOne({'users._id': userId}, {'users.$': 1}, function (err, shop) {
shop = shop.toObject()
user = shop.users[0]
filtered = {
name: user.name
}
callback(filtered);
});
But is there a better way to do it all in the query?
This question is almost two years old, but I noticed that people are still looking for a solution to this problem. fernandopasik's answer helped me very much, but is missing a code sample on how to use the suggested aggregation operations. That's why I post a more detailed answer.
The document I used is:
{
_id: '...'
title: 'my shop'
users: [
{
_id: 'user1Id',
name: 'user1',
username: '...'
},
{
_id: 'user2Id',
name: 'user2',
username: '...'
}
]
}
The solution I came up with (after reading the mongodb docs about aggregation) was:
Shop.aggregate([
{$unwind: '$users'},
{$match: {'users._id': 2}},
{$project: {_id: 0, 'name': '$users.name'}}
]);
To understand how the aggregation is working, it's best to try one operation at a time and read the mongodb docs of this operation.
Shop.aggregate([{$unwind: '$users'}])
$unwind deconstructs the users array (don't forget to include $ on the array name), so you end up with:
{
_id: '...',
title: 'my shop',
users: {
_id: 'user1Id',
name: 'user1',
username: '...'
}
}
{
_id: '...',
title: 'my shop',
users: {
_id: 'user2Id',
name: 'user2',
username: '...'
}
}
2. Using {$match: {'users._id': 'user2Id'}} on the aggregation pipeline (the two docs in this example) will return the whole document where users._id is 'user2Id':
{
_id: '...',
title: 'my shop',
users: {
_id: 'user2Id',
name: 'user2',
username: '...'
}
}
3. to return only name: 'user2' you can use {$project: {_id: 0, 'name': '$users.name'}}:
{name: 'user2'}
The aggregation pipeline is not easy to grasp at first. I recommend reading through the mongodb aggregation docs and try one aggregation operation at a time. It is sometimes hard to spot the error in the whole aggregation pipeline. Most of the time you simply get no result document from the pipeline when there is an error somewhere in the pipeline.
You should try mongodb aggregation framework with mongoose:
http://mongoosejs.com/docs/api.html#aggregate-js
I suggest you first apply unwind users and then match the user id and then project the username.