MongoDB insert a document for every documents in another collection - mongodb

I have a collection (users) containing some documents like so :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
I would like to create a new document for every entry in the user collection.
The documents would be created from a notification object like this :
{
type: 'alert',
msg: 'This is important'
}
The collection notifications should look something like this :
{
_id: ObjectId("56d45406be05db3021bf20a1"),
someoldcontent: "This was here before the request"
},
{
_id : ObjectId("56d45406be05db4022be20b1"),
user: ObjectId("56d45406be05db4022be51f9"),
type: 'alert',
msg: 'This is important'
},
{
_id : ObjectId("56d45406be05db3021be32e3"),
user: ObjectId("56d45406be05db3021be32e3"),
type: 'alert',
msg: 'This is important'
}
Is there any way to do this in a mongodb request?

Thanks to professor79 for helping me a lot with the question.
After lots of effort, the query was found. We used the aggregation framework in order to succeed.
The only 2 aggregations needed are $project and $out. The $out will automatically take care of the new _id in the notification document.
Given this collection named user :
{
_id: ObjectId("56d45406be05db4022be51f9"),
morecontent : ""
},
{
_id: ObjectId("56d45406be05db3021be32e3"),
morecontent : ""
}
We want to create a notification containing the same msg and type field for every document in the user collection.
Each notifications will be in the notifications collection an will have as a reference its corresponding userId.
Here is the query to achieve such a result :
db.user.aggregate([
{
$project: {
userId: '$_id',
type: { $literal: 'danger' },
msg: { $literal: 'This is a message' },
},
},
{ $out: 'notifications' },
])
The output in notifications collection:
{
"_id" : ObjectId("56d6112e197b4ea11a87de1a"),
"userId" : ObjectId("56d45406be05db4022be51f9"),
"type" : "danger",
"msg" : "This is a message"
},
{
"_id" : ObjectId("56d6112e197b4ea11a87de1b"),
"userId" : ObjectId("56d45406be05db3021be32e3"),
"type" : "danger",
"msg" : "This is a message"
}

As we have some chat to clarify problem:
the steps are needed to perform this operation server side:
first step - match > get users id
project ids to new documents as required
out -> store output in notification collection

Related

Mongoose Populate searching issue

I'm facing an issue while data fetching from database.
Here is my db collections structure:
Collection: customers
Document structure:
{
_id: ObjectId("5f4776ec5c5e60582a3cb3a4"),
name: "Karan",
phone: "98767867867"
}
Collection: merchants
Document structure:
{
_id: ObjectId("5f4777b45c5e60582a3cb3ab"),
business_name: "Myntra",
phone: "343434344"
}
Collection: transactions
Document structure:
{
"_id" : ObjectId("5f477cd6d6053560bf945280"),
"transact_from" : ObjectId("5f4776ec5c5e60582a3cb3a4"),
"transact_from_ref" : "users",
"transact_to" : ObjectId("5f4777b45c5e60582a3cb3ab"),
"transact_to_ref" : "merchants",
"transaction_amount" : 250,
},
{
"_id" : ObjectId("5f477cd6d6053560bf945280"),
"transact_from" : ObjectId("5f4777b45c5e60582a3cb3ab"),
"transact_from_ref" : "merchants",
"transact_to" : ObjectId("5f4776ec5c5e60582a3cb3a4"),
"transact_to_ref" : "users",
"transaction_amount" : 250,
}
You can see here that in transactions records, from and to user id can be of either merchants or customers collection id.
I have to fetch data by using concept of joins:
1. populate (in find query)
2. lookup (in aggregate query)
CASE 1: When I use .find() query with populate(), the result are getting as per my expectation because I defined "refPath" instead of "ref" in Model for populating
Result:
[
{
"_id" : ObjectId("5f477cd6d6053560bf945280"),
"transact_from" : "transact_to" : {
_id: ObjectId("5f4777b45c5e60582a3cb3ab"),
name: "Karan",
phone: "98767867867"
},
"transact_from_ref" : "users",
"transact_to" : {
_id: ObjectId("5f4777b45c5e60582a3cb3ab"),
business_name: "Myntra",
phone: "343434344"
},
"transact_to_ref" : "merchants",
"transaction_amount" : 250,
}
{
"_id" : ObjectId("5f477cd6d6053560bf945280"),
"transact_from" : {
_id: ObjectId("5f4777b45c5e60582a3cb3ab"),
business_name: "Myntra",
phone: "343434344"
},
"transact_from_ref" : "merchants",
"transact_to" : {
_id: ObjectId("5f4777b45c5e60582a3cb3ab"),
name: "Karan",
phone: "98767867867"
},
"transact_to_ref" : "users",
"transaction_amount" : 250,
}
]
Issue is Searching is not happening properly for joined fields: transact_from.name, transact_to.business_name.
CASE 2: When I use .aggregate() with lookup(), I'm unable to join collections dynamically. Is there any concept like "refPath" for lookup?
Hope you are understanding my issue. Please help me if anyone has a solution?

MongoDB Multiple documents update with Positional Array index

Having a collections as below:
[{
"_id" : ObjectId("5ee111e541050ba2728adb3f"),
"User" : "user1",
"applications" : ["test1", "test2","test3"]
},
{
"_id" : ObjectId("5ee111e541050ba2728adb40"),
"User" : "user2",
"applications" : ["test1", "test3","test2"]
}]
Expected Output:
[{
"_id" : ObjectId("5ee111e541050ba2728adb3f"),
"User" : "user1",
"applications" : ["test1", "test2Updated","test3"]
},
{
"_id" : ObjectId("5ee111e541050ba2728adb40"),
"User" : "user2",
"applications" : ["test1", "test3","test2Updated"]
}]
Used Below Query:
db.getCollection('testUser').update({"applications":"test2"},
{$set:{"applications.$[elem]":"Test2Updated"}},
{ "arrayFilters": [{ "elem": "test2" }], "multi": true })
Not working in MongoDB 3.4
Any Solutions to make it work on MongoDB 3.4 could be helpful
As arrayFilters was only introduced after 3.6 you need to do it in two update operations, We can use .bulkWrite() to achieve this in one DB call.
Query :
db.collection.bulkWrite([
/** First push `test2Updated` element to applications array where `test2` exists */
{
updateMany: {
filter: { applications: "test2" },
update: { $push: { applications: "test2Updated" } }
}
},
/** Remove `test2` from applications */
{
updateMany: {
filter: { applications: "test2" },
update: { $pull: { applications: "test2" } }
}
}
]);
Note :
Since we can't use $set + $unset both on array field at same time we've split this into two ops.
Also if you've multiple test2 elements in same applications array use $pullAll to pull all of them out but you can't insert equivalent no.of test2Updated elements as you don't know how many test2's exist for a document in that case you need to individually read docs to code and update applications array(I guess this is not the case).
One other thing is test2Updated will not be pushed to same index at which test2 is but I guess this is not that important.

How to update Meteor array element inside a document

I have a Meteor Mongo document as shown below
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "b1#gmail.com",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I want to update a specific array element's email with custom generated id using Meteor query something like the below.
for instance, I want to update the document
if 'users.id' == "b1#gmail.com" then update it to users.id = 'SomeIDXXX'
So updated document should looks like below.
{
"_id" : "zFndWBZTvZPgSKXHP",
"activityId" : "aRDABihAYFoAW7jbC",
"activityTitle" : "Test Mongo Document",
"users" : [
{
"id" : "SomeIDXXX",
"type" : "free"
},
{
"id" : "JqKvymryNaCjjKrAR",
"type" : "free"
},
],
}
I have tried the below but didnt work.
Divisions.update(
{ activityId: activityId, "users.id": emailId },
{ $set: { "users": { id: _id } } }
);
Can someone help me with the relevant Meteor query ? Thanks !
Your query is actually almost right except for a small part where we want to identify the element to be updated by its index.
Divisions.update({
"activityId": "aRDABihAYFoAW7jbC",
"users.id": "b1#gmail.com"
}, {
$set: {"users.$.id": "b2#gmail.com"}
})
You might need the arrayFilters option.
Divisions.update(
{ activityId: activityId },
{ $set: { "users.$[elem].id": "SomeIDXXX" } },
{ arrayFilters: [ { "elem.id": "b1#gmail.com" } ], multi: true }
);
https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/
You need to use the $push operator instead of $set.
{ $push: { <field1>: <value1>, ... } }

What's the most economical alternative to multiple positional identifiers in MongoDB?

I have a collection named authors with the following schema:
authors: {
_id,
firstName,
lastName,
posts: [
post 1: {...},
post 2: {...},
post 3: {
_id,
title,
content,
tags: [
tag 1: {...},
tag 2: {...},
tag 3: {
_id,
name,
description,
},
],
},
],
}
As can be seen, posts is an array of objects inside the authors collection. Each object inside this array, in turn, has tags, another array of objects. And each of these tags objects has three fields: _id, name, and description.
I'm trying to write a GraphQL mutation to update this name field on matching documents in the collection.
const updatedTagInAuthor = await Author
.updateMany({ 'posts.tags._id': args.newTagInfo._id },
{
$set: {
'posts.$.tags.$.name': args.newTagInfo.name,
'posts.$.tags.$.description': args.newTagInfo.description,
},
}, opts);
The above snippet obviously fails since MongoDB doesn't allow multiple positional elements ($) in a query. So is there any economical alternative to accomplish what I'm trying to do?
I tried the ArrayFilter method as MongoDB suggests:
const updatedTagInAuthor = await Author.update(
{},
{ $set: { 'posts.$[].tags.$[tag].name': args.newTagInfo.name } },
{ arrayFilters: [{ 'tag._id': args.newTagInfo._id }], multi: true }
);
But this throws the following error:
Cannot read property 'castForQuery' of undefined
Still confused!
These are the documents I'm updating with the kind of query I have given,
{"name" : "Steve","details" : [ {
"work" : [ {
"Company" : "Byjus",
"id" : 1,
"country" : "India"
},
{
"Company" : "Vodafone",
"id" : 2,
"country" : "UK"
}]
}]},{"name" : "Virat","details" : [ {
"work" : [ {
"Company" : "Byjus",
"id" : 1,
"country" : "India"
},
{
"Company" : "Verizon",
"id" : 3,
"country" : "US"
}]
}]}
QUERY:
db.getCollection('Users').update({"details": {"$elemMatch": {"work.id": 1}}}, {'$set': {'details.$[].work.$.Company': 'Boeing', 'details.$[].work.$.country': 'US'} }, {multi: true});
It's similar to what you asked right?
Try inserting those two Documents in a collection called User and try the above query in Mongo CONSOLE directly, not in GUI. Use the Query completely not just the $set method.
Try this,
Author.update({"posts": { "$elemMatch": { "tags.id": args.newTagInfo._id } }},
{'$set': {'posts.$[].tags.$.name': args.newTagInfo.name, 'posts.$[].tags.$.description': args.newTagInfo.description} },
{multi: true});

MongoDB fields limitation in array [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 8 years ago.
I am looking for a way - and dont even now if this is possible - just to return a part of a list saved in mongodb.
Lets have a look in my currently document:
{
_id : 'MyId',
name : 'a string',
conversations : [
{
user : 'Mike',
input : 'Some input'
},
{
user : 'Stephano',
input : 'some other input'
}
]
}
What I now want to do is smth like this:
var myOutput;
myOutput = db.my_collection.find(
{
_id : 'MyId',
'conversations.user' : 'Mike'
}, {
_id : 1,
name : 1,
conversations : {
$where : {
user : 'Mike'
}
}
});
Goal is it just to get back the conversation array item where user has the value Mike.
Is this still possible in MongoDB ? didn't found any reference in the documentation for the field limitations in mongoDB.
Use the $ positional operator in a projection:
> db.my_collection.find({ "_id" : "MyId", "conversations.user" : "Mike" },
{ "_id" : 1, "name" : 1, "conversations.$" : 1 })
{
"_id" : 'MyId',
"name" : 'a string',
"conversations" : [
{ "user" : 'Mike', "input" : 'Some input' }
]
}
This projects only first matching array element.
Are you aware of the aggregation pipeline?
db.my_collection.aggregate([
{ "$match": { "_id": "MyId"}}, { "$unwind": "$conversations"},
{ "$match": {"conversations.user": "Mike"}}
])
Output
{
"_id" : "MyId",
"name" : "a string",
"conversations" :
{
"user" : "Mike",
"input" : "Some input"
}
}