MongoDB - update property of subdocument where the string starts with certain text - mongodb

I need to update many subdocument array objects where property 'name' starts with 'Test:'
I need the new 'name' to be the current 'name' but updated with the 'Test:' part removed from the string.
I have already achieved it but not in a subdocument, this is how I got it to work in a document:
db.getCollection('test').updateMany(
{ name: { $regex: '^Test :' } },
[{ $set: { name: { $replaceOne: { input: "$name", find: 'Test : ', replacement: "" } } } }]
)
Thanks for any help.

You can use $map to iterate through the array elements and and use $mergeObjects to update the field name
db.collection.update({
"arr.name": {
$regex: "^Test :"
}
},
[
{
"$set": {
"arr": {
"$map": {
"input": "$arr",
"as": "a",
"in": {
"$mergeObjects": [
"$$a",
{
name: {
"$replaceOne": {
"input": "$$a.name",
"find": "Test : ",
"replacement": ""
}
}
}
]
}
}
}
}
}
])
MongoPlayground

Related

How to rename mongodb field using regex expression

I'm trying to update the name of a field in the mongodb document using the regex expression on the name of that field but I can't, can someone help me to do it?
I have a person document that contains a field with the email of the person, the name of that field is like that "firstname_lastname#gmail.com" and I want to replace "_" character in all email fields by "_dot_".
Here is what I did
:
db.getCollection("person").updateMany(
{
{'email.'+'.*_.*': { $exists: true }
},
{
$rename:{'email.'+'.*_.*': 'email.'+'.*_dot_.*'
});
and here the structure of my document:
person {
name: { .... }
email:{
firstname_lastname1#gmail.com : {
.... other fields
},
firstname_lastname2#gmail.com : {
.... other fields
}
}
}
thanks in advance
As #Wernfried Domscheit suggested in the comment, you should change your schema to avoid using dynamic email address as field name.
Nevertheless, you can use $objectToArray to convert the emails into an array of k-v tuples. Then, $split them by _ and rejoin by _dot_. Finally do a $arrayToObject to convert back to original structure.
db.collection.aggregate([
{
"$addFields": {
"email": {
"$objectToArray": "$email"
}
}
},
{
"$addFields": {
"email": {
"$map": {
"input": "$email",
"as": "e",
"in": {
k: {
"$ltrim": {
"input": {
"$reduce": {
"input": {
"$split": [
"$$e.k",
"_"
]
},
"initialValue": "",
"in": {
"$concat": [
"$$value",
"_dot_",
"$$this"
]
}
}
},
"chars": "_dot_"
}
},
v: "$$e.v"
}
}
}
}
},
{
"$addFields": {
"email": {
"$arrayToObject": "$email"
}
}
}
])
Here is the Mongo Playground for your reference.

How to update field name in array in document(Mongo)?

In MongoDB I have collection of users. There I have such document:
{
_id: ObjectId("619365c54a7d53716438933e"),
name: 'Chris',
hobbies: [
{ title: 'Sports', frequency: 5 },
{ title: 'Cooking', fequency: 5 },
{ title: 'Hiking', frequency: 1 }
],
isSporty: true,
cookMaster: true,
age: 35 }
So, it has array value on hobbies and there during insert I made a mistake. When I inserted hobby Cooking, I added key fequeny instead of frequency. Now I want to fix this. In Mong exists such operator as $rename. I tried:
db.users.updateMany({name:"Chris"}, {$rename: {"hobbies.fequency": "frequency"}})
But got error. How can I rich and change this key properly?
Simply do an update with a $set to correct the field name with $map.
db.collection.update({},
[
{
"$set": {
"hobbies": {
"$map": {
"input": "$hobbies",
"as": "h",
"in": {
title: "$$h.title",
frequency: {
"$ifNull": [
"$$h.frequency",
"$$h.fequency"
]
}
}
}
}
}
}
])
Here is the Mongo playground for your reference.
$set - $map hobbies array field, with $mergeObject to add new field frequency.
$unset - Remove field for fequency in hobbies array field.
db.collection.update({
name: "Chris"
},
[
{
$set: {
"hobbies": {
$map: {
input: "$hobbies",
in: {
$mergeObjects: [
"$$this",
{
frequency: "$$this.fequency"
}
]
}
}
}
}
},
{
$unset: "hobbies.fequency"
}
])
Sample Mongo Playground

Mongo addFields get the value from the nested array

I want to sort the collection by the fieldValue based on the given fieldName.
For example:
sort the collection by fieldName = 'Author'
My problem:
I am unable to get the value from the collection, like I want to add a field for authorValue.
{
....
author: 'John'
}, {
author: 'Hengry'}
What I have tried:
.addFields({
author: {
$filter: {
input: "$sections.fieldData",
cond: {
$eq: ["$$this.fieldName", true],
},
},
},
The structure
[
{
"sections": [
{
name: "section1",
fields: [
{
fieldName: "Author",
fieldValue: "John"
},
{
fieldName: "Movie",
fieldValue: "Avenger"
}
]
}
]
},
{
"sections": [
{
name: "section1",
fields: [
{
fieldName: "Author",
fieldValue: "Hengry"
},
{
fieldName: "Movie",
fieldValue: "Test"
}
]
}
]
}
]
You can use $reduce to iterate your array and extract out the fieldValue for comparison.
db.collection.aggregate([
{
"$addFields": {
"sortField": {
"$reduce": {
"input": "$sections",
"initialValue": null,
"in": {
"$reduce": {
"input": "$$this.fields",
"initialValue": null,
"in": {
"$cond": {
"if": {
$eq: [
"$$this.fieldName",
"Author"
]
},
"then": "$$this.fieldValue",
"else": "$$value"
}
}
}
}
}
}
}
},
{
$sort: {
sortField: 1
}
},
{
"$project": {
sortField: false
}
}
])
Here is the Mongo playground for your reference.

Retrieve the field from the array

I have a document like this
[
{
"empno": "×325007",
"vehicle": [
{
"valdate": "2020-08-02T13:17z",
"Inspectvalue": {
"price": "2000"
}
}
]
}
]
But I want document like this
{
"empno":"x325007",
"valdate":"2020-08-02T13:17z",
"price":"2000"
}
keeping them in the array, and including the keys to each element.
Example
db.collection.aggregate([
{
$addFields: {
newData: {
"$map": {
"input": "$vehicle",
"as": "v",
"in": {
valdate: "$$v.valdate",
price: "$$v.Inspectvalue.price",
empno: "$empno"
}
}
}
}
},
{
$unset: [
"vehicle",
"empno"
],
}
])
(you can't have repeated top level fields in JSON).
Or if you want them as top level keys, unwind the elements:
db.collection.aggregate([
{
$unwind: "$vehicle"
},
{
$addFields: {
valdate: "vehicle.valdate",
price: "vehicle.Inspectvalue.price"
}
},
{
$unset: "vehicle"
}
])
Example

MongoDB > extract collection from nested array

I've been trying every method I found on SO with no success. Trying
to accomplish a seemingly simple task (very easy with json/lodash for example) in MongoDB..
I have a collection:
db.users >
[
{
_id: 'userid',
profile: {
username: 'abc',
tests: [
{
_id: 'testid',
meta: {
category: 'math',
date: '9/2/2017',
...
}
questions: [
{
type: 'add',
correct: true,
},
{
type: 'subtract',
correct: true,
},
{
type: 'add',
correct: false,
},
{
type: 'multiply',
correct: false,
},
]
},
...
]
}
},
...
]
I want to end up with an array grouped by question type:
[
{
type: 'add',
correct: 5,
wrong: 3,
},
{
type: 'subtract',
correct: 4,
wrong: 9
}
...
]
I've tried different variations of aggregate, last one is:
db.users.aggregate([
{ $match: { 'profile.tests.meta.category': 'math' }},
{
$project: {
tests: {
$filter: {
input: "$profile.tests",
as: "test",
cond: { $eq: ['$$test.meta.category', 'math'] }
}
}
}
},
{
$project: {
question: "$tests.questions"
}
},
{ $unwind: "$questions"},
])
Also tried adding $group at the end of the pipeline:
{
$group:
{
_id: '$questions.type',
res: {
$addToSet: { correct: {$eq:['$questions.chosenAnswer', '$questions.answers.correct'] }
}
}
}
No variation gave me what I'm looking for, I'm sure I'm missing a core concept, I've looked over the documentation and couldn't figure it out.. what I'm basically looking for is a flatMap to extract away all the questions of all users and group them by type.
If anyone can lead me in the right direction, I'll greatly appreciate it :) thx. (Also, I'm using Meteor, so any query has to work in Meteor mongo)
You can try below aggregation in 3.4.
$filter to filter math categories with $map to project questions array in each matching category followed by $reduce and $concatArrays to get all questions into single array for all matching categories.
$unwind questions array and $group by type and $sum to compute correct and wrong count.
db.users.aggregate([
{
"$match": {
"profile.tests.meta.category": "math"
}
},
{
"$project": {
"questions": {
"$reduce": {
"input": {
"$map": {
"input": {
"$filter": {
"input": "$profile.tests",
"as": "testf",
"cond": {
"$eq": [
"$$testf.meta.category",
"math"
]
}
}
},
"as": "testm",
"in": "$$testm.questions"
}
},
"initialValue": [],
"in": {
"$concatArrays": [
"$$value",
"$$this"
]
}
}
}
}
},
{
"$unwind": "$questions"
},
{
"$group": {
"_id": "$questions.type",
"correct": {
"$sum": {
"$cond": [
{
"$eq": [
"$questions.correct",
true
]
},
1,
0
]
}
},
"wrong": {
"$sum": {
"$cond": [
{
"$eq": [
"$questions.correct",
false
]
},
1,
0
]
}
}
}
}
])