MongoDB query: aggregate with "findOne" - mongodb

I'm trying to make a query on MongoDB 3.4 where I add a field for one specific element of an array. Example of the object:
{
"_id": 1,
"people": [
{
"name": "Jhon Smith",
"age": "30",
"state": "NY"
},{
"name": "Clint Mercer",
"age": "50",
"state": "NY"
},{
"name": "Walter Smith",
"age": "40",
"state": "WI"
}
]
}
And I want to make a query where I'll add to this document an attribute with the first person with "Smith" in it's name. Example:
{
"_id": 1,
"people": [
{
"name": "Jhon Smith",
"age": "30",
"state": "NY"
},{
"name": "Clint Mercer",
"age": "50",
"state": "NY"
},{
"name": "Walter Smith",
"age": "40",
"state": "WI"
}
],
"firstSmith": {
"name": "Jhon Smith",
"age": "30",
"state": "NY"
}
}
I already have the _id of the document I want, but I can't understand how to make a query like this. I'm trying using aggregate with "$match" for the id and "$addFields" after, but I can't make a query that works for this field to find exactly what I want. I think it would be similar to the "findOne" query, but I can't find anything that works on "$addFields".
Obs: I DON'T want the "firstSmith" to be an array with just one "people" inside, I want it as is in the example.
I'd appreciate some help with this one.

$match - to filter the relevant document
$filter with $regexMatch - to filter people array by the name property
arrayElemAt - to get only the first element of above array
$addFields - to add new field with value from above result
db.collection.aggregate([
{
"$match": {
"_id": 1
}
},
{
"$addFields": {
"firstSmith": {
"$arrayElemAt": [
{
"$filter": {
"input": "$people",
"cond": {
"$regexMatch": {
"input": "$$this.name",
"regex": "Smith"
}
}
}
},
0
]
}
}
}
])
Working example

Related

MongoDb group by two conditions in aggregate

I would like to group my data by different conditions but I do not understand how I could do it.
My data:
[
{"Id": "1", "Info": "X" "Date": 10/1},
{"Id": "2", "Info": "X" "Date": 13/2},
{"Id": "3", "Info": "Y" "Date": 13/2},
{"Id": "4", "Info": "X" "Date": 10/1},
{"Id": "5", "Info": "X" "Date": 10/1},
{"Id": "6", "Info": "X" "Date": 13/2},
]
And I would like to group them by Info and by Date, a result similar to this one:
[
{"Id": ["1","4","5"], "Info": "X" "Date": 10/1},
]
[
{"Id": ["2", "6"], "Info": "X" "Date": 13/2},
]
[
{"Id": ["3"], "Info": "Y" "Date": 13/2},
]
I am using aggregate and I just know how to use aggregate to group them just by one condition, I donĀ“t know how to continue and how to use date in $group, this is what I have and how I group it by info:
.aggregate([
{ "$match" : { "$or": [{"status": "downloading"}, {"status": "calculating"}]}},
{ "$project": {"Id": 1, "Date": 1, "info": 1}},
{ "$group" : { "_id" : "$Info", "Id" : { "$addToSet" : "$Id"}}},
{ "$project": {"_id": 0, "Info": "$_id", "Id": 1 }}
You can give the _id in the $group stage multiple fields, like so:
db.collection.aggregate([
{
"$project": {
"Id": 1,
"Date": 1,
"info": 1
}
},
{
"$group": {
"_id": {
date: "$Date",
info: "$info"
},
"Id": {
"$addToSet": "$Id"
}
}
},
{
"$project": {
"_id": 0,
"Info": "$_id.info",
"Date": "$_id.date",
"Id": 1
}
}
])
Mongo Playground

How can I get only specific object from nested array mongodb

I'm using mongoDB with PHP. I know how to get the document based on product_id, but I need only a specific object from the whole document, I don't know how to get only specific object from nested array based on product_id.
for ex. My expected output is:
products": [
{
"product_id": 547,
"name": "cola",
"quantity": 24
}
]
then make some changes on object p.s update the quantity then update it to the database.
My collection looks like
"_id": {
"$oid": "62ed30855836b16fd38a00b9"
},
"name": "drink",
"products": [
{
"product_id": 547,
"name": "cola",
"quantity": 24
},
{
"product_id": 984,
"name": "fanta",
"quantity": 42
},
{
"product_id": 404,
"name": "sprite",
"quantity": 12
},
{
"product_id": 854,
"name": "water",
"quantity": 35
}
]
}
Try this:
db.getCollection('test').aggregate([
{
"$unwind": "$products"
},
{
"$match": {
"products.product_id": 547
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$$ROOT",
"$products"
]
}
}
},
{
"$project": {
"products": 0
}
}
])
The query gives the following output:
{
"name" : "cola",
"product_id" : 547,
"quantity" : 24
}

MongoDB aggregation lookup, push output of both collections into sub objects

https://mongoplayground.net/p/xQp-y1iXUtZ
There are 2 collections:
profile
subs
After a lookup, the foreign collection is returned as an array. Since there can be only one element in that array (a sub can only have one profile), we take the first one.
Now, I would like to "push" the "subs" collection into an object as well. There are a lot of fields in "subs".
This is what I have:
[
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"_id": "1",
"f1": "f1",
"f2": "f1",
"f3": "f1",
"f4": "f1",
"username": "gk"
},
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"_id": "2",
"f1": "f1",
"f2": "f3",
"f3": "f4",
"f4": "f5",
"username": "gk"
}
]
This is what I am looking for:
[
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"SUBS": {
"_id": "1",
"f1": "f1",
"f2": "f1",
"f3": "f1",
"f4": "f1",
"username": "gk"
}
},
{
"PROFILE0": {
"_id": "1",
"name": "gk"
},
"SUBS": {
"_id": "2",
"f1": "f1",
"f2": "f3",
"f3": "f4",
"f4": "f5",
"username": "gk"
}
}
]
Essentially the contents of the "local" collection also as an object.
You add projection before lookup,
$project to create a field SUBS and set $$ROOT as the value that is the root document
$lookup to join profile collection, pass SUBS.username as localField, and set PROFILE0 as as value, and we don't need $unset stage
$set same as you did
db.subs.aggregate([
{
$project: {
_id: 0,
SUBS: "$$ROOT"
}
},
{
$lookup: {
from: "profile",
localField: "SUBS.username",
foreignField: "name",
as: "PROFILE0"
}
},
{
$set: {
"PROFILE0": { $arrayElemAt: ["$PROFILE0", 0] }
}
}
])
Playground

mongodb map data in one collection

My intention is to map the reference column based on the id. This is my data in one collection.
[{
"title": "A",
"reference": ["2"],
"id": "1",
"author": ["Doraemon"]
},{
"title": "B",
"reference": [],
"id": "2",
"author": ["Naruto"]
}]
Does anyone can help me how to make it like this
[{
"title": "A",
"reference": ["B"],
"id": "1",
"author": ["Doraemon"]
},{
"title": "B",
"reference": [],
"id": "2",
"author": ["Naruto"]
}]
You can do self lookup
db.collection.aggregate([
{
"$lookup": {
"from": "collection",
"localField": "reference",
"foreignField": "id",
"as": "selfJoin"
}
},
{
$addFields: {
reference: "$selfJoin.title"
}
},
{
$project: {
selfJoin: 0
}
}
])
Working Mongo playground

Performing Repeated filtering in mongodb

I want to get the names of people who work in bmw and use these names as filter to find and return the documnents containing the field "car". So the end result must be the last two documents given in this example.
[
{"_id": "235", "name": "indu", "dob": "31/4/15", "company": "bmw"},
{"_id": "236", "name": "prith", "dob": "01/4/98", "company": "bmw"},
{"_id": "237", "name": "rames", "dob": "07/4/00", "company": "renault"},
{"_id": "238", "name": "indu", "salary": "10,000", "car": "yes", "married": "yes"},
{"_id": "239", "name": "prith", "salary": "80,000", "car": "yes", "children": "no"}
]
I appreciate your help, Thanks in advance
You want to use $exists
names = db.collection.distinct{"name", {"company": "bmw"})
db.collection.find({"car": {"$exists": true}, "name": {"$in": names}})
You can also do it in 1 aggregation call although I would not recommend it as It's less efficient.
db.collection.aggregate([
{
"$match": {
"company": "bmw"
}
},
{
$lookup: {
from: "this_collection",
localField: "name",
foreignField: "name",
as: "roots"
}
},
{
"$unwind": "$roots"
},
{
"$replaceRoot": {
"newRoot": "$roots"
}
},
{
"$match": {
"car": {"$exists": true}
}
}
])