I have a list of collection like the following in mongodb
{ _id: ObjectId("6087feeef467a4320883daf3"),
name: 'group 1',
admins: ['adam', 'ken']
}
{ _id: ObjectId("2087feeef467a4320883daf3"),
name: 'group 2',
admins: ['rebecca']
}
{ _id: ObjectId("9987feeef467a4320883daf3"),
name: 'group 3',
admins: []
}
I need to extract out all the admins, the end result would be something like this:
[
'admin',
'ken',
'rebecca'
]
How can i do that, i stuck at this part:
db.data.find({ admins: { $exists: true, $not: {$size: 0} } })
This will show all collections, but i just need the list
The proper way of doing so using single query is unwinding the array of admins and adding them to a new set.
db.collection.aggregate([
{
$unwind: "$admins"
},
{
$group: {
_id: null,
adminsSet: {
$addToSet: "$admins"
}
}
}
])
The end result will be the following so you could extract your flattened array:
[
{
"_id": null,
"adminsSet": [
"ken",
"rebecca",
"adam"
]
}
]
Mongo playground ref: https://mongoplayground.net/p/LFYsqHdTucN
You can do something like this
db.data.aggregate([
{$unwind: "$admins"},
{$project:{_id:0}}
]).map(x => x.admins);
Result
[
"adam",
"ken",
"rebecca"
]
Related
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
This is the schema model
const tokoSchema = new mongoose.Schema({
name: String
product: [{display: Boolean}]
}
So, what I want is filtering the data by product.display. When I filtered it using toko.find({"product.display": true}) it only shows list of toko that has product.display: true but what I want is if the product display is false it keep showing the _id and name of toko with empty array of product []. Example query expected
// showing all tokos
[
{
_id: blabla
name: "test",
product: [{_id: blabla, display: true}], // filter display:true
},
{
_id: blabla,
name: "test",
product: [] // no product with display:true
}
]
example of toko.find({"product.display": true}) query
// not showing all tokos
[
{
_id: blabla
name: "test",
product: [{_id: blabla, display: true}]
},
]
any solution for this? should I use aggregate?
You need $filter for an array:
db.toko.aggregate([
{
$addFields: {
product: {
$filter: {
input: "$product",
cond: {
$eq: [ "$$this.display", true ]
}
}
}
}
}
])
Mongo Playground
I have a collection with the following data structure:
{
_id: ObjectId,
text: 'This contains some text',
type: 'one',
category: {
name: 'Testing',
slug: 'test'
},
state: 'active'
}
What I'm ultimately trying to do is get a list of categories and counts. I'm using the following:
const query = [
{
$match: {
state: 'active'
}
},
{
$project: {
_id: 0,
categories: 1
}
},
{
$unwind: '$categories'
},
{
$group: {
_id: { category: '$categories.name', slug: '$categories.slug' },
count: { $sum: 1 }
}
}
]
This returns all categories (that are active) and the total counts for documents matching each category.
The problem is that I want to introduce two additional $match that should still return all the unique categories, but only affect the counts. For example, I'm trying to add a text search (which is indexed on the text field) and also a match for type.
I can't do this at the top of the pipeline because it would then only return categories that match, not only affect the $sum. So basically it would be like being able to add a $match within the $group only for the $sum. Haven't been able to find a solution for this and any help would be greatly appreciated. Thank you!
You can use $cond inside of your $group statement:
{
$group: {
_id: { category: '$categories.name', slug: '$categories.slug' },
count: { $sum: { $cond: [ { $eq: [ "$categories.type", "one" ] }, 1, 0 ] } }
}
}
Suppose there is the following collection
People:
{
_id: 1,
name: 'john',
last_name: 'blah1',
job: 'lifeguard'
}
{
_id: 2,
name: 'john',
last_name: 'blah2',
job: 'lifeguard'
}
{
_id: 3,
name: 'alex',
last_name: 'blah3',
job: 'lifeguard'
}
{
_id: 4,
name: 'alex',
last_name: 'blah4',
job: 'lifeguard'
}
{
_id: 5,
name: 'alex',
last_name: 'blah5',
job: 'gardener'
}
I need to get the distict jobs with an array of distict names:
Trying to get the following result:
[
{
value: 'lifeguard',
names: [
'john',
'alex'
],
},
{
value: 'gardener',
names: [
'alex'
],
},
]
I understand how to get the unique jobs
db.people.find().distinct('jobs')
However i did not figure out how to do a distinct query with multiple properties.
Better to use the aggregation framework where you have a pipeline that has a $group stage to group the documents by the job key and then construct the names distinct array within the group by the accumulator $addToSet.
Consider the following aggregate operation:
db.people.aggregate([
{
"$group": {
"_id": "$job",
"names": { "$addToSet": "$name" }
}
}
])
#chridam did help me find the right answer, in the real world my object was more like
{
_id: 1,
name: ['john', 'bah1', 'blah2', 'blah3'],
last_name: 'blah1',
job: 'lifeguard'
}
so i had to $unwind the names and aggregate $group just like in #chridam's answer.
model.aggregate([
{$unwind: "$name"},
{
$group: {
_id:"$name",
jobs: {
$addToSet: "$job"
}
}
}
]
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!