merge mongodb aggregate result - mongodb

I have one collection say user. Structure of each document is something like this
_id:String
status:Int32
account:{
firstName:
lastName:
.. some other nested property
}
..some more property
My end goal is to generate a new nested field fullName in accountfield which is a concatenation of two name fields. I can run aggregate query like this
db.user.aggregate(
[
{ $project: { 'account.name': { $concat: [ "$account.firstName", " ", "$account.lastName" ] } } }
])
if I write $out along with db name, but my existing data get replaced. How do I actually merge so that my final structure remains as
_id:String
status:Int32
account:{
firstName:String
lastName:String
fullName:String
.. some other nested property
}
..some more property

In your $project pipeline, you need to include the other fields using the dot notation on the embedded fields as follows:
db.user.aggregate([
{
"$project": {
"status": 1,
"field1": 1, // the other properties
"field2": 1,
"account.firstName": 1,
"account.lastName": 1,
"account.name": {"$concat":["$account.firstName", " ", "$account.lastName"]}
}
},
{ "$out": "tempcollection" }
])

Related

Group and Combine Text Fields Using Pymongo

I have a collection of user reviews and I'm trying to combine all the reviews by user so I can run some NLP analysis on them. This feels like it should be easy, but I'm missing something with how Mongo treats strings.
My documents look like this:
{'_id': ObjectId('57e079d3e3874f12ad721f70'),
'atmosphere': 5,
'review_id': 63,
'dedication': 3,
'orgName': 'Some Organization',
'enabled': True,
'accessibility': 3,
'efficiency': 3,
'orgId': '57e05e0de3874f121d516616',
'user': '5809f2c0bc0a53eb49eac583',
'date': '10/20/15 0:00',
'quality': 3,
'orgId_orig': 1098,
'description': 'Here is some sample text'
}
I've tried this:
agg_result = revs.aggregate( [
{ "$group": { "_id": "$user", "mergedText": { "$mergeObjects": "$description" } } }
])
for i in agg_result:
print(i)
But I'm getting this error:
OperationFailure: $mergeObjects requires object inputs, but input "Here is some sample text" is of type string
My expected output would be
{
'userId1':{'mergedText':'joined descriptions from this user'},
'userId2':{'mergedText':'this users descriptions'},
'userId3':{'mergedText':'all descriptions from this user'}
}
where the various userIds are Mongo ObjectIds from the 'user' field.
I'm brand new to Mongo and this has been tripping me up for awhile. Thank you.
try this , merge object needs objectbut your description is string you could push in array
agg_result = revs.aggregate( [
{ "$group": { "_id": "$user", "mergedText": { "$push": "$description" } } }
])
for i in agg_result:
print(i)

Can't remove object in array using Mongoose

This has been extensively covered here, but none of the solutions seems to be working for me. I'm attempting to remove an object from an array using that object's id. Currently, my Schema is:
const scheduleSchema = new Schema({
//unrelated
_id: ObjectId
shifts: [
{
_id: Types.ObjectId,
name: String,
shift_start: Date,
shift_end: Date,
},
],
});
I've tried almost every variation of something like this:
.findOneAndUpdate(
{ _id: req.params.id },
{
$pull: {
shifts: { _id: new Types.ObjectId(req.params.id) },
},
}
);
Database:
Database Format
Within these variations, the usual response I've gotten has been either an empty array or null.
I was able slightly find a way around this and accomplish the deletion by utilizing the main _id of the Schema (instead of the nested one:
.findOneAndUpdate(
{ _id: <main _id> },
{ $pull: { shifts: { _id: new Types.ObjectId(<nested _id>) } } },
{ new: true }
);
But I was hoping to figure out a way to do this by just using the nested _id. Any suggestions?
The problem you are having currently is you are using the same _id.
Using mongo, update method allows three objects: query, update and options.
query object is the object into collection which will be updated.
update is the action to do into the object (add, change value...).
options different options to add.
Then, assuming you have this collection:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
},
{
"_id": 3
}
]
}
]
If you try to look for a document which _id is 2, obviously response will be empty (example).
Then, if none document has been found, none document will be updated.
What happens if we look for a document using shifts._id:2?
This tells mongo "search a document where shifts field has an object with _id equals to 2". This query works ok (example) but be careful, this returns the WHOLE document, not only the array which match the _id.
This not return:
[
{
"_id": 1,
"shifts": [
{
"_id": 2
}
]
}
]
Using this query mongo returns the ENTIRE document where exists a field called shifts that contains an object with an _id with value 2. This also include the whole array.
So, with tat, you know why find object works. Now adding this to an update query you can create the query:
This one to remove all shifts._id which are equal to 2.
db.collection.update({
"shifts._id": 2
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example
Or this one to remove shifts._id if parent _id is equal to 1
db.collection.update({
"_id": 1
},
{
$pull: {
shifts: {
_id: 2
}
}
})
Example

Mongoose how to use positional operator to pull from double nested array with specific condition, and return new result

Suppose I have the following schema:
{
_id: ObjectId(1),
title: string,
answers: [
{
_id: ObjectId(2),
text: string,
upVotes: [
{
_id: ObjectId(3),
userId: ObjectId(4)
}
]
}
]
}
What I want is pull vote of a specific user from answer upvotes, and return the new update result.
For example, find a question with id 1, and get its specific answer with id 2, then from that answer pull my vote using userId inside upvotes.
I want to do it with a single findOneAndUpdate query
You can even use single $ positional with the $pull operator to update the nested array
db.collection.findOneAndUpdate(
{ "_id": ObjectId(1), "answers._id": ObjectId(2) },
{ "$pull": { "answers.$.upVotes": { "userId": ObjectId(4) }}}
)
I think I understood that you want to do a search in the specific array
db.collection.update(
{
"_id": "507f1f77bcf86cd799439011", // id field
"answers.upVotes._id":"507f1f77bcf86cd799439011" //id array
}
),{
"$set":{"answers.$.upVotes": {userId :"507f1f77bcf86cd799439011"}}},//edit
//use "addToSet" for add

Replace all occurences of a substring in a field in all documents in MongoDB

I have a collection Brands, I have to replace the path ./assets/ to D:/data/db/images , I have multiple occurrences of this in multiple documents. How do I acheive this using a query ?
Sample of each document
{"name":"Dell","images":["./assets/dell1.jpg","./assets/dell2.jpeg","./assets/dell3.jpg"],
"captions":["Yours is here","Easy as dell","Uniquely you"],
"logo":"./assets/dell4.png",
"details":{"headOffice":"Bangalore","address":"No.12/1, Divyashree Green, Koramangala Inner Ring Rd, Domlur, Bengaluru - 560071",
"phoneNumber":"(080) 28077000 ","website":"www.dell.com"}
}
You can use Aggregation Framework's $out operator to redirect the output of your aggregation to particular collection. If you specify the same collection name then it will replace existing collection.
To overwrite existing field you can use $addFields operator. Then you just have to remove the length of ./assets/ using $substr and concatenate that with your new prefix using $concat
db.Brands.aggregate([
{
$addFields: {
images: {
$map: {
input: "$images",
as: "image",
in: {
$concat: [ "D:/data/db/images", { $substr: [ "$$image", 8, { $strLenBytes: "$$image" } ] } ]
}
}
}
}
},
{ $out: "Brands" } //replaces existing collection
])
In MongoDB 3.2 you can run following script:
db.Brands.find().forEach(function(doc){
doc.images = doc.images.map(function(image){ return image.replace("./assets/","D:/data/db/images/") })
db.Brands.save(doc);
})

Convert ObjectID to String in mongo Aggregation

I'm in this scenario right now:
I have a collection X:
{
_id:ObjectId('56edbb4d5f084a51131dd4c6'),
userRef:ObjectId('56edbb4d5f084a51131dd4c6'),
serialNumber:'A123123',
...
}
I need to aggregate all documents, grouping them by the userRef + serialNumber, so I'm trying to use concat like this:
$group: {
_id: {
'$concat': ['$userRef','-','$serialNumber']
},
...
So basically in my aggregation in MongoDB, I need to group documents by the concatenation of a ObjectId and a string. However, It seems that $concat only accepts strings as parameters:
uncaught exception: aggregate failed: {
"errmsg" : "exception: $concat only supports strings, not OID",
"code" : 16702,
"ok" : 0
}
Is there a way to convert an ObjectId to a String within an aggregation expression?
EDIT:
This question is related, but I the solution doesn't fit my problem. (Specially because I can't use ObjectId.toString() during the aggregation)
Indeed I couldn't find any ObjectId().toString() operation in Mongo's documentation, but I wonder if there's any tricky thing that can be done in this case.
Now you can try with $toString aggregation which simply
converts ObjectId to string
db.collection.aggregate([
{ "$addFields": {
"userRef": { "$toString": "$userRef" }
}},
{ "$group": {
"_id": { "$concat": ["$userRef", "-", "$serialNumber"] }
}}
])
You can check the output here
I couldn't find a way to do what I wanted, so instead, I created a MapReduce function that, in the end, generated the keys the way I wanted to (concatenating other keys).
At the end, it looked something like this:
db.collection('myCollection').mapReduce(
function() {
emit(
this.userRef.str + '-' + this.serialNumber , {
count: 1,
whateverValue1:this.value1,
whateverValue2:this.value2,
...
}
)
},
function(key, values) {
var reduce = {}
.... my reduce function....
return reduce
}, {
query: {
...filters_here....
},
out: 'name_of_output_collection'
}
);
You can simply use $toString to apply $concat in aggregation on ObjectIDs in the following way -
$group: {
'_id': {
'$concat': [
{ '$toString' : '$userRef' },
'-',
{ '$toString' : '$serialNumber'}
]
},
}
I think you may try to resolve it by using an Array which contains both fields:
{$project:{newkey:['$userRef','$serialNumber']},{$match:{newkey:{$in:filterArray}}}}
this may match the data with both fields to the filter. Please notice that the data in the newkey array should have the same data type with the filterArray elements.
You can use $substr https://docs.mongodb.com/manual/reference/operator/aggregation/substr/#exp._S_substr to cast any object to string before $concat.
This is a sample of code that's working for me.
group_id_i['_id'] = {
'$concat' => [
{ '$substr' => [ {'$year' => '$t'}, 0, -1] }, '-',
{ '$substr' => [ {'$month' => '$t'}, 0, -1] }, '-',
{ '$substr' => [ {'$dayOfMonth' => '$t'}, 0, -1] }
]
}
Where t is DateTime field, this aggregation returns data like so.
{
"_id" => "28-9-2016",
"i" => 2
}