Replacing field while taking data from MongoDB by aggregation - mongodb

community,
I struggle with replacing values while getting data from MongoDB by using aggregation.
Let’s imagine we have two collections:
mappedUsers = [
{
groupName: 'Group 1',
users: [
'Nick',
'John',
]
},
{
groupName: 'Group 2',
users: [
'Mark',
'Samuel',
]
}
]
data = [
{
date: '22/13/07',
results: [
{
user: 'Nick',
result: 5
},
{
user: 'Mark',
result: 7
}
]
},
{
date: '22/13/06',
results: [
{
user: 'John',
result: 6
},
{
user: 'Helga',
result: 9
}
]
},
]
I want to replace users with their groups. If a user doesn’t belong to any group we get his name. Same table with just mapped users:
results = [
{
date: '22/13/07',
results: [
{
user: 'Group 1', <-- replaced
result: 5
},
{
**user: 'Group 2', <-- replaced
result: 7
}
]
},
{
date: '22/13/06',
results: [
{
**user: 'Group 1', <-- replaced
result: 6
},
{
**user: 'Helga', <-- NOT replaced
result: 9
}
]
},
]
How can I replace those values?

Related

Sequelize - How return rows only if included model has at least one record

I want to this query in Sequelize:
const results = await WorkflowAssignation.findAndCountAll(
{
limit: limit,
offset: offset,
where:
{
userId: userIds,
status: status
},
include: [
{
model: WorkflowStepAssignation,
include: [
{ model: WorkflowStepAssignationHasUser, include: [ users ]}
]
},
{ model: users }
],
distinct: true,
order: [ [ 'createdAt', 'DESC'], [ { model: WorkflowStepAssignation, as: "steps" }, 'stepNumber', 'ASC' ] ]
});
The problem is that I need to return only WorkflowAssignation if the WorkflowStepAssignationHasUser table contain at least 1 record with the value passed as parameter for userid.
if I used a where in this line { model: WorkflowStepAssignationHasUser, include: [ users ]} it will still return all the WorkflowAssignation with only the steps that has the userId value and that's not what I want.
It is like I want to return all the WorkflowAssignation including all the tables but only if in WorkflowStepAssignation -> WorkflowStepAssignationHasUser has the users in the parameters.
Relations are:
WorkflowAssignation
has Many
WorkflowStepAssignation
has Many
WorkflowStepAssignationHasUser (has a userId column)
add required: true
const results = await WorkflowAssignation.findAndCountAll(
{
limit: limit,
offset: offset,
where:
{
userId: userIds,
status: status
},
include: [
{
model: WorkflowStepAssignation,
include: [
{ model: WorkflowStepAssignationHasUser, include: [ users ]}
]
},
{ model: users }
],
distinct: true,
required: true,
order: [ [ 'createdAt', 'DESC'], [ { model:
WorkflowStepAssignation, as: "steps" }, 'stepNumber', 'ASC' ] ]
});

[MongoDB]: Changing the type of values in all array fields

I have the authors and books test collections which have a many-to-many relationship between them.
> db.books.find()
[
{
_id: ObjectId("60a676f24312c6d8ea7bd6ec"),
title: '300 years of peanut juggling: A longitudinal analysis.',
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6e9', '60a673c44312c6d8ea7bd6ea' ]
},
{
_id: ObjectId("60a676f24312c6d8ea7bd6ed"),
title: "Mystery Overflow: murder and chaos on the Web's biggest developer Q & A platform.",
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6eb' ],
edition: 2
}
]
> db.authors.find()
[
{
_id: ObjectId("60a673c44312c6d8ea7bd6e9"),
name: 'Jason Filippou',
age: 33,
nationalities: [ 'GRC, CND' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6ea"),
name: 'Mary Chou',
age: 39,
nationalities: [ 'USA' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6eb"),
name: 'Max Schwarz',
age: 42,
job: 'pilot',
books: [ '60a676f24312c6d8ea7bd6ed' ]
}
]
I implement the relationship externally, as can be seen by the authors and books fields. However, I have made the mistake of having the arrays of references be raw strings, instead of ObjectId types. This means that my joins as required by, e.g, $lookup()) fail.
I tried to mass update all the strings to make them ObjectIds using the command:
db.books.find({}).forEach(book => book.authors.forEach(id => ObjectId(id)))
While the command worked, the original data did not change:
> db.books.find({}).forEach(book => book.authors.forEach(id => ObjectId(id)))
> db.books.find()
[
{
_id: ObjectId("60a676f24312c6d8ea7bd6ec"),
title: '300 years of peanut juggling: A longitudinal analysis.',
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6e9', '60a673c44312c6d8ea7bd6ea' ]
},
{
_id: ObjectId("60a676f24312c6d8ea7bd6ed"),
title: "Mystery Overflow: murder and chaos on the Web's biggest developer Q & A platform.",
inPrint: true,
authors: [ '60a673c44312c6d8ea7bd6eb' ],
edition: 2
}
]
> db.authors.find()
[
{
_id: ObjectId("60a673c44312c6d8ea7bd6e9"),
name: 'Jason Filippou',
age: 33,
nationalities: [ 'GRC, CND' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6ea"),
name: 'Mary Chou',
age: 39,
nationalities: [ 'USA' ],
books: [ '60a676f24312c6d8ea7bd6ec' ]
},
{
_id: ObjectId("60a673c44312c6d8ea7bd6eb"),
name: 'Max Schwarz',
age: 42,
job: 'pilot',
books: [ '60a676f24312c6d8ea7bd6ed' ]
}
]
What is my mistake here?
If you decide to update all books string to ObjectId, u can use update-documents-with-aggregation-pipeline
db.authors.updateMany({},[
{
"$addFields": {
"books": {
"$map": {
"input": "$books",
"in": {
"$toObjectId": "$$this"
}
}
}
}
}
])

MongoDB query - unwind and match preserving null OR different value/ add a new field based on a condition

If a have a following structure :
{
_id: 1,
name: 'a',
info: []
},
{
_id: 2,
name: 'b',
info: [
{
infoID: 100,
infoData: 'my info'
}
]
},
{
_id: 3,
name: 'c',
info: [
{
infoID: 200,
infoData: 'some info 200'
},
{
infoID: 300,
infoData: 'some info 300'
}
]
}
I need to query in such a way to obtain the documents where infoID is 100 showing the infoData, or nothing if info is empty, or contains subdocuments with infoID different from 100.
That is, I would want the following output:
{
_id: 1,
name: 'a',
infoData100: null
},
{
_id: 2,
name: 'b',
infoData100: 'my info'
},
{
_id: 3,
name: 'c',
infoData100: null
}
If I $unwind by info and $match by infoID: 100, I lose records 1 and 3.
Thanks for your responses.
Try below query :
Query :
db.collection.aggregate([
/** Adding a new field or you can use $project instead of addFields */
{
$addFields: {
infoData100: {
$cond: [
{
$in: [100, "$info.infoID"] // Check if any of objects 'info.infoID' has value 100
},
{
// If any of those has get that object & get infoData & assign it to 'infoData100' field
$let: {
vars: {
data: {
$arrayElemAt: [
{
$filter: {
input: "$info",
cond: { $eq: ["$$this.infoID", 100] }
}
},
0
]
}
},
in: "$$data.infoData"
}
},
null // If none has return NULL
]
}
}
}
]);
Test : MongoDB-Playground

How to pull array of documents from array of documents?

I have a document like this
{
users: [
{
name: 'John',
id: 1
},
{
name: 'Mark',
id: 2
},
{
name: 'Mike',
id: 3
},
{
name: 'Anna',
id: 4
}
]
}
and I want to remove users from the array with ids 2 and 4. To do that I execute the following code:
const documents = [
{
id: 2
},
{
id: 4
},
]
Model.updateOne({ document_id: 1 }, { $pull: { users: { $in: documents } } });
But it doesn't remove any user.
Could you say me what I'm doing wrong and how to achieve the needed result?
This works if you can redefine the structure of your documents array:
const documents = [2, 4]
Model.updateOne({ document_id: 1 }, { $pull: { users: { id: { $in: documents } } } })

When I need to run mapReduce in MongoDB

I'm newbie with MongoDB.
I have created a mapReduce on my Person collection to group cities.
db.Person.find()
[{
name: 'Bob',
addresses: [
{
street: 'Vegas Street',
neighborhood: {
name: 'Center',
city: {
name: 'Springfield'
}
}
}, {
.....
}
]
}, {
....
}]
And this is my mapReduce:
db.Person.mapReduce(function() {
for (var i = 0; i < this.address.length-1; i++) {
var address = this.address[i];
emit(address.neighborhood.city.name, 1);
}
}, function(k, v) {
return v.length;
}, { out: 'City' });
Then I use this to list my cities:
db.City.find().sort({'_id:', 1})
[{
_id: 'Springfield',
value: 3
}, {
_id: 'City B',
value: 2
}, {
...
}]
My question is about the City data, I need run the mapReduce each time I insert, update or delete on my Personcollection or it runs automatically?