I am working on a project where user can upload a *.csv* file to enter user data, also he can choose which one is the email field (user may have used Email, email, E-mail or something else as the email field).
Previous Developer saved the data in following format.
[
{
"_id" : ObjectId("5de7d0d65223850135eac968"),
groupName:"AGroup",
primaryField:"EMail",
FieldsData:[
{EMail:"abc#gmail.com",otherField:123},
{EMail:"def#outlook.com",otherField:345}
]
},
{
"_id" : ObjectId("5de7d0d65223850135eac969"),
groupName:"BGroup",
primaryField:"userEmail",
FieldsData:[
{userEmail:"hij#hotmail.com",otherField:678},
{userEmail:"kl#outlook.com",otherField:910}
]
}
]
here FieldData is the data from .csv file and primaryField is the field user have chosen as the email field .
I have to get the primaryField name and get the corresponding field value from FieldData.
For Example: For first group i.e.. AGroup primaryField is EMail hence the mailList will contain the value of FieldData.EMail
Right now I have to do this in following steps:
Get the primaryField of all the groups
db.Group.aggregate({$project:{_id:1,PrimaryKey:"$primaryField"}})
Loop over all the data
Make query with each groupId and primaryField
db.Group.aggregate({$match:{ "_id" : ObjectId("5de7d0d65223850135eac968")}},
{$project:{MailList:"$FieldsData.EMail"}})
Please suggest a way to do the job in one query. Basically I need the data in following format
[
{ "_id" : ObjectId("5de7d0d65223850135eac968"), "MailList" : [ "abc#gmail.com", "def#outlook.com" ] }
{ "_id" : ObjectId("5de7d0d65223850135eac969"), "MailList" : [ "hij#hotmail.com", "kl#outlook.com" ] }
]
To read and compare object keys dynamically you need to use $objectToArray. You can use $let to define temporary variable element which will hold first found k-vpair matching your PrimaryField and then return v part.
db.collection.aggregate([
{
$project: {
_id: 1,
MailList: {
$map: {
input: "$FieldsData",
in: {
$let: {
vars: {
element: {
$arrayElemAt: [
{
$filter: {
input: { $objectToArray: "$$this" },
cond: {
$eq: [ "$$this.k", "$primaryField" ]
}
}
}, 0
]
}
},
in: "$$element.v"
}
}
}
}
}
}
])
Mongo Playground
Related
> db.Dashboards.find({user:{$regex:"adams"}}).pretty();
{
"_id" : ObjectId("123"),
"user" : "adam.adams",
"widgets" : [
{
"_id" : ObjectId("124"),
"column" : 1,
"configMap" : {
"coleg" : "bas.baser,cer.ceras,tom.tomsa"
},
}
]
}
I have a Mongo database that keeps records like the one above, unfortunately I need to find all users who have "cer.ceras" in the "coleg" field and then replace this with "per.peras"
I try with
db.Dashboards.find({widgets:{"$elemMatch":{configMap:{"$elemMatch":{coleg:/.*ceras.*/}}}}}}).pretty();
But I'm not finding anything for me
This may a bit complex.
Filter:
The criteria should work with $regex to find the occurrence of the text.
Update:
Require the update with aggregation pipeline.
$set - Set widgets field.
1.1. $map - Iterate the element in the widgets array and return a new array.
1.1.1. $mergeObjects - Merge current iterate document with the result of 1.1.1.1.
1.1.1.1. A document with configMap array. With $mergeObjects to merge current iterate configMap document with the result 1.1.1.1.1.
1.1.1.1.1. A document with coleg field. With $replaceAll to replace all matching text "cer.ceras" to "per.peras".
Update Options
{ multi: true } aims to update multiple documents.
db.collection.update({
widgets: {
$elemMatch: {
"configMap.coleg": {
$regex: "cer\\.ceras"
}
}
}
},
[
{
$set: {
widgets: {
$map: {
input: "$widgets",
in: {
$mergeObjects: [
"$$this",
{
configMap: {
$mergeObjects: [
"$$this.configMap",
{
coleg: {
$replaceAll: {
input: "$$this.configMap.coleg",
find: "cer.ceras",
replacement: "per.peras"
}
}
}
]
}
}
]
}
}
}
}
}
],
{
multi: true
})
Demo # Mongo Playground
MongoDB: 4.4.9, Mongosh: 1.0.4
I have a MongoDB collection full of documents with monthly production data as separate fields (monthlyProd1, monthlyProd2, etc.). Each field is one month's production data, and the values are an object data type.
Document example:
_id: ObjectId("314e0e088f183fb7e699d635")
name: "documentName"
monthlyProd1: Object
monthlyProd2: Object
monthlyProd3: Object
...
I want to take all the months and put them into a single new field (monthlyProd) -- a single array of objects.
I can't seem to access the fields with the different methods I've tried. For example, this gets close to doing what I want:
db.monthlyProdData.updateMany({},
{ $push: { "monthlyProd": { $each: [ "$monthlyProd1", "$monthlyProd2", "$monthlyProd3" ] } } }
)
...but instead of taking the value / object data from each field, like I had hoped, it just outputs a string into the monthlyProd array ("$monthlyProd1", "$monthlyProd2", ...):
Actual output:
monthlyProd: Array
0: "$monthlyProd1"
1: "$monthlyProd2"
2: "$monthlyProd3"
...
Desired output:
monthlyProd: Array
0: Object
1: Object
2: Object
...
I want the data, not a string! Lol. Thank you for your help!
Note: some months/fields may be an empty string ("") because there was no production. I want to make sure to not add empty strings into the array -- only months with production / fields that have an object data type. That being said, I can try figuring that out on my own, if I can just get access to these fields' data!
Try this one:
db.collection.updateMany({}, [
// convert to k-v Array
{ $set: { monthlyProd: { $objectToArray: "$$ROOT" } } },
{
$set: {
monthlyProd: {
// removed not needed objects
$filter: {
input: "$monthlyProd",
cond: { $not: { $in: [ "$$this.k", [ "name", "_id" ] ] } }
// or cond: { $in: [ "$$this.k", [ "monthlyProd1", "monthlyProd2", "monthlyProd3" ] ] }
}
}
}
},
// output array value
{ $project: { monthlyProd: "$monthlyProd.v" } }
])
Mongo playground
Thank you to #Wernfried for the original solution to this question. I have modified the solution to incorporate my "Note" about ignoring any empty monthlyProd# values (aka months that didn't have any production), so that they are not added into the final monthlyProd array.
To do this, I added an $and operator to the cond: within $filter, and added the following as the second expression for the $and operator (I used "" and {} to take care of the empty field values if they are of either string or object data type):
{ $not: { $in: [ "$$this.v", [ "", {} ] ] } }
Final solution:
db.monthlyProdData2.updateMany({}, [
// convert to k-v Array
{ $set: { monthlyProd: { $objectToArray: "$$ROOT" } } },
{
$set: {
monthlyProd: {
// removed not needed objects
$filter: {
input: "$monthlyProd",
cond: { $and: [
{ $not: { $in: [ "$$this.k", [ "name", "_id" ] ] } },
{ $not: { $in: [ "$$this.v", [ "", {} ] ] } }
]}
}
}
}
},
// output array value
{ $project: { monthlyProd: "$monthlyProd.v", name: 1 } }
])
Thanks again #Wernfried and Stackoverflow community!
I would like to perform a join on a collection where student name are equal in each collection AND WHERE the last string after "_" in column log's value is what the variable id is.
I got the join to work but the issue is on the match statement. How can I match with a substring of a string on the logs column in the collection that I am about to join?
I can split the log column value into an array like this:
{ $split: [ "$studentInfo.log", "_" ]}
I just need to get the last value after underscore now to match variable id
var id = "123";
dbo.collection("student").aggregate([
{ "$lookup": {
"localField": "name",
"from": "data",
"foreignField": "data.studentName",
"as": "studentInfo"
}
}]).toArray(function(err, results) {
console.log(results);
});
The issue is that student name is not unique so in order to get the join to work correctly we need to join by name AND make sure the characters after underscore match with the variable we have id
student collection
{
"_id" : ObjectId("(Object ID here"),
"name": "Test"
}
data collection
{
"_id" : ObjectId("(Object ID here"),
"studentName": "Test",
"log": "NC_Test_123"
},
{
"_id" : ObjectId("(Object ID here"),
"studentName": "Test",
"log": "FC_Test_444"
}
I need to get NC_Test_123 when the variable I have for id is 123.
You need to define custom lookup condition with both conditions: student name and log field. To get the last value of splitted string you can use $arrayElemAt with index set to -1
db.student.aggregate([
{
$lookup: {
from: "data",
let: { "student_name": "$name" },
pipeline: [
{
$match: {
$expr: {
$and: [
{ $eq: [ "$$student_name", "$studentName" ] },
{ $eq: [ { $arrayElemAt: [ { $split: [ "$log", "_" ]}, -1 ] }, "123" ] }
]
}
}
}
],
as: "studentInfo"
}
},
{
$unwind: "$studentInfo"
}
])
In mongoDB, how can we get the count of particular key in an array
{
"_id" : ObjectId("52d9212608a224e99676d378"),
"business" : [
{
"name" : "abc",
"rating" : 4.5
},
{
"name" : "pqr"
},
{
"name" : "xyz",
"rating" : 3.6
}
]
}
in the above example, business is an array (with "name" and/or "rating" keys)
How can i get the count of business array with only "rating" key existing ?
Expected output is : 2
Looks like you have to use Aggregation Framework. In particular you need to $unwind your array, then match only elements with rating field included, then $group documents back to original format.
Try something like this:
db.test.aggregate([
{ $match: { /* your query criteria document */ } },
{ $unwind: "$business" },
{ $match: {
"business.rating": { $exists: 1 }
}
},
{ $group: {
_id: "$_id",
business: { $push: "$business" },
business_count: { $sum: 1 }
}
}
])
Result will look like the following:
{
_id: ObjectId("52d9212608a224e99676d378"),
business: [
{ name: "abc", rating: 4.5 },
{ name: "xyz", rating: 3.6 }
],
business_count: 2
}
UPD Looks like OP doesn't want to group results by wrapping document _id field. Unfortunately $group expression must specify _id value, otherwise it fails with exception. But, this value can actually be constant (e.g. plain null or 'foobar') so there will be only one resulting group with collection-wise aggregation.
I have a document structure like
{
"_id" : ObjectId("52263922f5ebf05115bf550e"),
"Fields" : [
{
"Field" : "Lot No",
"Rules" : [ ]
},
{
"Field" : "RMA No",
"Rules" : [ ]
}
]
}
I have tried to update by using the following code to push into the Rules Array which will hold objects.
db.test.update({
"Fields.Field":{$in:["Lot No"]}
}, {
$addToSet: {
"Fields.Field.$.Rules": {
"item_name": "my_item_two",
"price": 1
}
}
}, false, true);
But I get the following error:
can't append to array using string field name [Field]
How do I do the update?
You gone too deep with that wildcard $. You match for an item in the Fields array, so you get a access on that, with: Fields.$. This expression returns the first match in your Fields array, so you reach its fields by Fields.$.Field or Fields.$.Result.
Now, lets update the update:
db.test.update({
"Fields.Field": "Lot No"
}, {
$addToSet: {
"Fields.$.Rules": {
'item_name': "my_item_two",
'price':1
}
}
}, false, true);
Please note that I've shortened the query as it is equal to your expression.