Update data in mongo db with condition of another collection - mongodb

I have 2 collection in mongodb: Account, Information.
Account:
{
"_id": {
"$oid": "6348dc197a7b552660170d8b"
},
"username": "12345",
"password": "123dsgfdsgdfsg",
"email": "1243",
"role": "123",
"_infoid": {
"$oid": "6348dc197a7b552660170d8a"
}
}
Information:
{
"_id": {
"$oid": "6348dc197a7b552660170d8a"
},
"avatar": "hello",
"name": "Abcd",
"phonenumber": "012345678",
"address": "abcd"
}
I wanna change "phonenumber" to "123" but I just have "_id" of Account. Can I change it with aggregation pipeline?

Does this seem what you try to achieve?
// populate database with test data
use("test_db")
db.account.insertOne({
"_id": "6348dc197a7b552660170d8b",
"username": "12345",
"password": "123dsgfdsgdfsg",
"email": "1243",
"role": "123",
"_infoid": "6348dc197a7b552660170d8a"
})
db.information.insertOne({
"_id": "6348dc197a7b552660170d8a",
"avatar": "hello",
"name": "Abcd",
"phonenumber": "012345678",
"address": "abcd"
})
// define some test variables to use
let some_account_id = "6348dc197a7b552660170d8b"
let new_phone_number = "+9876543210"
// change data
db.account.aggregate([
{
$match: {_id: some_account_id}
},
{
$addFields: {phonenumber: new_phone_number}
},
{
$project: {phonenumber: 1, _id: "$_infoid"}
},
{
$merge:{
into: "information",
whenNotMatched: "fail",
}
}
])
// show final results
db.information.find()
Result:
[
{
"_id": "6348dc197a7b552660170d8a",
"avatar": "hello",
"name": "Abcd",
"phonenumber": "+9876543210",
"address": "abcd"
}
]

You don't need to duplicate _id, findOneAndUpdate() can be executed for this use case.
The definition of it is:
db.collection.findOneAndUpdate( filter, update, options )
which updates a single document based on the filter and sort criteria.
Below you can refer to the link for more details:
https://www.mongodb.com/docs/manual/reference/method/db.collection.findOneAndUpdate/
.

Performa a $lookup and perform some wrangling to your desired format. Finally do a $merge to update to the collection Information
db.Information.aggregate([
{
"$lookup": {
"from": "Account",
"localField": "_id",
"foreignField": "_infoid",
"pipeline": [
{
"$project": {
role: 1
}
}
],
"as": "AccountLookup"
}
},
{
"$unwind": "$AccountLookup"
},
{
$set: {
phonenumber: "$AccountLookup.role"
}
},
{
$unset: "AccountLookup"
},
{
"$merge": {
"into": "Information",
"on": "_id",
"whenMatched": "merge",
"whenNotMatched": "discard"
}
}
])
Here is the Mongo Playground for your reference.

Related

How to replace array of object containing ids with the data using MongoDB aggregation

I am having a collection which contains the data like the following and want to have the desirable output which I have mentioned below.
db={
collectionA: [
{
"id": ObjectId("63b7c24c06ebe7a8fd11777b"),
"uniqueRefId": "UUID-2023-0001",
"products": [
{
"productIndex": 1,
"productCategory": ObjectId("63b7c24c06ebe7a8fd11777b"),
"productOwners": [
ObjectId("63b7c2fd06ebe7a8fd117781")
]
},
{
"productIndex": 2,
"productCategory": ObjectId("63b7c24c06ebe7a8fd11777b"),
"productOwners": [
ObjectId("63b7c2fd06ebe7a8fd117781"),
ObjectId("63b7c12706ebe7a8fd117778")
]
},
{
"productIndex": 3,
"productCategory": "",
"productOwners": ""
}
]
}
],
collectionB: [
{
"_id": ObjectId("63b7c2fd06ebe7a8fd117781"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
},
{
"_id": ObjectId("63b7c12706ebe7a8fd117778"),
"fullname": "Carry Minatti",
"email": "carry.minatty#pp.com"
},
]
}
Desirable Output = [
{
"id": ObjectId("507f1f77bcf86cd799439011"),
"uniqueRefId": "UUID-2023-0001",
"products": [
{
"productIndex": 1,
"productCategory": ObjectId('614g2f77bff86cd755439021'),
"productOwners": [
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41acd"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
}
]
},
{
"productIndex": 2,
"productCategory": ObjectId('614g2f77bff86cd755439021'),
"productOwners": [
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41acd"),
"fullname": "Jim Corbett",
"email": "jim.corbett#pp.com"
},
{
"_id": ObjectId("63ac1e59c0afb8b6f2d41ace"),
"fullname": "Carry Minatti",
"email": "carry.minatty#pp.com"
}
]
},
{
"productIndex": 3,
"productCategory": "",
"productOwners": ""
}
]
}
]
In the collectionA we are having other documents as well, its not just one document.
Similarly for collectionB we are having other documents too.
How we can get this desirable output?
I am expecting the mongodb query for getting this solution.
I have implemented the lookup like the following
db.collectionA.aggregate([
{
"$lookup": {
"from": "collectionB",
"localField": "products.productOwners",
"foreignField": "_id",
"as": "inventory_docs"
}
}
])
You can try this:
db.collectionA.aggregate([
{
"$unwind": "$products"
},
{
"$lookup": {
"from": "collectionB",
"localField": "products.productOwners",
"foreignField": "_id",
"as": "products.productOwners"
}
},
{
"$group": {
"_id": {
id: "$id",
uniqueRefId: "$uniqueRefId"
},
"products": {
"$push": "$products"
}
}
},
{
"$project": {
id: "$_id.id",
uniqueRefId: "$_id.uniqueRefId",
products: 1,
_id: 0
}
}
])
Playground link.
In this query, we do the following:
First we unwind the products array, using $unwind.
Then we calculate productOwners, using $lookup.
Then we group the unwinded elements, using $group.
Finally we, project the desired output using $project.

Mongodb , "JOIN" a nested object

I need to join the faults array
{
"_id": "99812930-37CE-456F-A9D9-837E9E3F712A",
"faultsChanged": [
{
"_id": "7C628A46-7E80-4615-8B08-10C5E9A6B1D7",
"faults": [
"BF221A71-0217-42E7-B853-53112EDA9694",
"E4A54172-7E93-49C4-840B-8E6116116979"
],
"isDeleted": false,
"partition": "indego",
"sessionUuid": "A83CE9A1-7539-493F-8BA4-6FBE25B18B57",
"source": "1",
"timestamp": {
"$date": {
"$numberLong": "1630603342700"
}
},
"unmigratedNote": null,
"uuid": "7C628A46-7E80-4615-8B08-10C5E9A6B1D7"
}
]
}
So that it gets replaced by a document from another collection OOFaultEntry.
New to the aggregation pipeline, I have tried
{$lookup:{
from: "OOFaultEntry",
localField: 'faultsChanged.faults',
foreignField: "_id",
let:{faults:"$faultsChanged.faults"},
pipeline:[],
as:"faults"
}}
but this just created a key on the result and was not embedded inside of each respective faultChanged object
expected result
OOFaultEntry would be JOINED on _id . So ,it should look something like
{
"_id": "99812930-37CE-456F-A9D9-837E9E3F712A",
"faultsChanged": [
{
"_id": "7C628A46-7E80-4615-8B08-10C5E9A6B1D7",
"faults": [
{
"_id": "BF221A71-0217-42E7-B853-53112EDA9694",
"some_key": "value"
},
{
"_id": "E4A54172-7E93-49C4-840B-8E6116116979",
"some_key": "value"
}
],
"isDeleted": false,
"partition": "indego",
"sessionUuid": "A83CE9A1-7539-493F-8BA4-6FBE25B18B57",
"source": "1",
"timestamp": {
"$date": {
"$numberLong": "1630603342700"
}
},
"unmigratedNote": null,
"uuid": "7C628A46-7E80-4615-8B08-10C5E9A6B1D7"
}
]
}
One way would be $unwind the faultsChanged array first. Perform the $lookup and regroup the results.
db.Faults.aggregate([
{
"$unwind": "$faultsChanged"
},
{
"$lookup": {
"from": "00FaultEntry",
"localField": "faultsChanged.faults",
"foreignField": "_id",
"as": "faultsChanged.faults"
}
},
{
"$group": {
"_id": "$_id",
"faultsChanged": {
$push: "$faultsChanged"
}
}
}
])
Mongo Playground

How to use current field in second $match?

Let's say i have 2 collections
// Post collection:
[
{
"_id": "somepost1",
"author": "firstuser",
"title": "First post"
},
{
"_id": "somepost2",
"author": "firstuser",
"title": "Second post"
},
{
"_id": "somepost3",
"author": "firstuser",
"title": "Third post"
}
]
// User collection:
[
{
"_id": "firstuser",
"nickname": "John",
"posts": {
"voted": []
}
},
{
"_id": "seconduser",
"nickname": "Bob",
"posts": {
"voted": [
{
"_id": "somepost1",
"vote": "1"
},
{
"_id": "somepost3",
"vote": "-1"
}
]
}
}
]
And i need to get this result:
[
{
"_id": "somepost1",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "First post",
"myvote": "1"
},
{
"_id": "somepost2",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "Second post",
"voted": "0"
},
{
"_id": "somepost3",
"author": {
"_id": "firstuser",
"nickname": "John"
},
"title": "Third post",
"myvote": "-1"
}
]
How can i make a request with aggregation, which will display this output with dynamic _id of elements?
I have problem with using current _id of post in second $match and setting "myvote" to 0 if there are no element in "posts.voted" associated with current post.
Here what i've tried: https://mongoplayground.net/p/v70ZUioVSpQ
db.post.aggregate([
{
$match: {
author: "firstuser"
}
},
{
$lookup: {
from: "user",
localField: "author",
foreignField: "_id",
as: "author"
}
},
{
$addFields: {
author: {
$arrayElemAt: [
"$author",
0
]
}
}
},
{
$lookup: {
from: "user",
localField: "_id",
foreignField: "posts.voted._id",
as: "Results"
}
},
{
$unwind: "$Results"
},
{
$unwind: "$Results.posts.voted"
},
{
$match: {
"Results.posts.voted._id": "ID OF CURRENT POST"
}
},
{
$project: {
_id: 1,
author: {
_id: 1,
nickname: 1
},
title: 1,
myvote: "$Results.posts.voted.vote"
}
}
])
From the $match docs:
The query syntax is identical to the read operation query syntax
The query syntax does not allow usage of document values. which is what you're trying to do.
What we can do is use $expr within the $match stage, this allows us to use aggregation oprerators, thus also giving access to the document values. like so:
{
$match: {
$expr: {
$eq: ['$Results.posts.voted._id', '$_id'],
}
},
},

How do I perform a mongoDB lookup and then merge results into a single data set?

Hi I am looking to join data from two tables
What I am looking to do is create a "friend" relationship between two users using a lookup and find out how many emails each friend (if any sent)
here is a mongo DB playground... I am close, I just can't figure out how to get the 'other users' email information
I should only see emails from a's friends (b and c) and NOT any emails sent from a
This playground almost does what I want...
https://mongoplayground.net/p/KKocPm3fzEv
Here is the input for the above playground
db={
"users": [
{
"_id": "a",
"email": "a#test.com",
},
{
"_id": "b",
"email": "b#test.com",
},
{
"_id": "c",
"email": "c#test.com",
}
],
"friends": [
{
"userId": "a",
"otherUserId": "b"
},
{
"userId": "a",
"otherUserId": "c"
},
],
"emailsSent": [
{
"userId": "a",
"number": "25"
},
{
"userId": "b",
"number": "3"
},
]
}
Here is the output from the above playground
[
{
"_id": "a",
"a_myfriends": [
{
"_id": ObjectId("5a934e000102030405000002"),
"otherUserId": "b",
"userId": "a"
},
{
"_id": ObjectId("5a934e000102030405000003"),
"otherUserId": "c",
"userId": "a"
}
],
"email": "a#test.com",
"emailaddr": [
{
"_id": "b",
"email": "b#test.com"
},
{
"_id": "c",
"email": "c#test.com"
}
],
"emailsent": [
{
"_id": ObjectId("5a934e000102030405000001"),
"number": "3",
"userId": "b"
}
]
}
]
There are three arrays of information now... how do I join them all together so each entry in the array is only for 'that' friend?
this is what I'd like to end up with
{
"_id": "a",
"a_myfriends": [
{
"otherUserId": "b",
"email": "b#test.com",
"number": "3"
},
{
"otherUserId": "c",
"email": "c#test.com"
}
]
}
NOTE: I tried concatenating unions from this article, but I think it's not working due to the disparity of IDs for the user (eg _id and userId)
MongoDB: Combine data from multiple collections into one..how?
I think you can reduce the number of collections => reduce the $lookups and data will look more simple
How about this schema of 1 collection?
Even if someone has too many friends like 200.000, you could have an extra field {"extra_friends" "_id"}, and rarely use the a secondary friends collections going rarely max to 2 collections.
users=
[
{
"_id": "a",
"email": "a#test.com",
"emails-send" 25,
"friends" ["b" "c"]
},
{
"_id": "b",
"email": "b#test.com",
"emails-send" 3,
"friends" [....]
},
{
"_id": "c",
"email": "c#test.com",
"emails-send" 0
"friends" [....]
}
]
Query
(for your schema, produces the expected data)
a series of $lookup and $unwind
from users
-to friends (get friends)
-to users (get friends email)
-to emailsSent (get number of emails for those that sended)
some $set in the middle to keep the schema simple
group by _id back and combine those friends
*query could become smaller a bit, but this way its easy to follow, stage by stage and see what is happening
Test code here
db.users.aggregate([
{
"$match": {
"$expr": {
"$eq": [
"$_id",
"a"
]
}
}
},
{
"$lookup": {
"from": "friends",
"localField": "_id",
"foreignField": "userId",
"as": "myfriends"
}
},
{
"$unwind": {
"path": "$myfriends"
}
},
{
"$lookup": {
"from": "users",
"localField": "myfriends.otherUserId",
"foreignField": "_id",
"as": "myfriendsEmails"
}
},
{
"$unwind": {
"path": "$myfriendsEmails"
}
},
{
"$set": {
"myfriends.email": "$myfriendsEmails.email"
}
},
{
"$unset": [
"myfriendsEmails",
"myfriends._id",
"myfriends.userId"
]
},
{
"$lookup": {
"from": "emailsSent",
"localField": "myfriends.otherUserId",
"foreignField": "userId",
"as": "friendsEmails"
}
},
{
"$unwind": {
"path": "$friendsEmails",
"preserveNullAndEmptyArrays": true
}
},
{
"$set": {
"myfriends.number": "$friendsEmails.number"
}
},
{
"$unset": [
"friendsEmails"
]
},
{
"$group": {
"_id": "$_id",
"email": {
"$first": "$email"
},
"friends": {
"$push": "$myfriends"
}
}
}
])

Mongodb aggregation lookup to add field in each array with condition

I have 3 collections.
User:
{
"_id":ObjectId("60a495cdd4ba8b122899d415"),
"email":"br9#gmail.com",
"username":"borhan"
}
Panel:
{
"_id": ObjectId("60a495cdd4ba8b122899d417"),
"name": "borhan",
"users": [
{
"role": "admin",
"joined": "2021-05-19T04:35:47.474Z",
"status": "active",
"_id": ObjectId("60a495cdd4ba8b122899d418"),
"user": ObjectId("60a495cdd4ba8b122899d415")
},
{
"role": "member",
"joined": "2021-05-19T04:35:47.474Z",
"status": "active",
"_id": ObjectId("60a49600d4ba8b122899d41a"),
"user": ObjectId("60a34e167958972d7ce6f966")
}
],
}
Team:
{
"_id":ObjectId("60a495e0d4ba8b122899d419"),
"title":"New Teams",
"users":[
ObjectId("60a495cdd4ba8b122899d415")
],
"panel":ObjectId("60a495cdd4ba8b122899d417")
}
I want to receive a output from querying Panel colllection just like this:
{
"_id": ObjectId("60a495cdd4ba8b122899d417"),
"name": "borhan",
"users": [
{
"role": "admin",
"joined": "2021-05-19T04:35:47.474Z",
"status": "active",
"_id": ObjectId("60a495cdd4ba8b122899d418"),
"user": ObjectId("60a495cdd4ba8b122899d415"),
"teams":[
{
"_id":ObjectId("60a495e0d4ba8b122899d419"),
"title":"New Teams",
"users":[
ObjectId("60a495cdd4ba8b122899d415")
],
"panel":ObjectId("60a495cdd4ba8b122899d417")
}
]
},
{
"role": "member",
"joined": "2021-05-19T04:35:47.474Z",
"status": "active",
"_id": ObjectId("60a49600d4ba8b122899d41a"),
"user": ObjectId("60a34e167958972d7ce6f966")
}
],
}
I mean i want to add teams field (which is array of teams that user is existed on it) to each user in Panel collection
Here is my match query in mongoose to select specific panel:
panel_model.aggregate([
{
$match: {
users: {
$elemMatch: {user: ObjectId("60a495cdd4ba8b122899d415"), role:"admin"}
}
}
},
])
Is it possible to get my output with $lookup or $addFields aggregations?
You need to join all three collections,
$unwind to deconstruct the array
$lookup there are two kind of lookups which help to join collections. First I used Multiple-join-conditions-with--lookup, and I used standrad lookup to join Users and Teams collections.
$match to match the user's id
$expr - when you use $match inside lookup, u must use it.
$set to add new fields
$group to we already destructed using $unwind. No we need to restructure it
here is the code
db.Panel.aggregate([
{ $unwind: "$users" },
{
"$lookup": {
"from": "User",
"let": { uId: "$users.user" },
"pipeline": [
{
$match: {
$expr: {
$eq: [ "$_id", "$$uId" ]
}
}
},
{
"$lookup": {
"from": "Team",
"localField": "_id",
"foreignField": "users",
"as": "teams"
}
}
],
"as": "users.join"
}
},
{
"$set": {
"users.getFirstElem": {
"$arrayElemAt": [ "$users.join", 0 ]
}
}
},
{
$set: {
"users.teams": "$users.getFirstElem.teams",
"users.join": "$$REMOVE",
"users.getFirstElem": "$$REMOVE"
}
},
{
"$group": {
"_id": "$_id",
"name": { "$first": "name" },
"users": { $push: "$users" }
}
}
])
Working Mongo playground
Note : Hope the panel and user collections are in 1-1 relationship. Otherwise let me know