MongoDB transform array of objects into array of field values - arrays

I got a field which is an Array of objects inside another array of object in below format
{
id: '1234567',
listOfSettings: [{
name: 'setting1',
listOfObjects: [{name: 'object1',id:'111'}, {name: 'object2', id: '112'}]
}, {
name: 'setting2',
listOfObjects: [{name: 'object2',id:'113'}, {name: 'object3', id: '114'}]
}]
}
I want to convert it with a Mongo update query into something like:
{
id: '1234567',
listOfSettings:[{
name: 'settings1',
listOfObjects:['111', '112']
}, {
name: 'settings2',
listOfObjects:['113', '114']
}]
}
I know I can use the $set option to update the content and $fieldname to specify the field to obtain values, but not sure how I can achieve it in case of nested arrays.

Agg pipeline:
[{"$project"=>
{"listOfSettings"=>
{"$map"=>
{"input"=>"$listOfSettings",
"as"=>"x",
"in"=>
{"$mergeObjects"=>
["$$x",
{"listOfObjects"=>
{"$map"=>
{"input"=>"$$x.listOfObjects",
"as"=>"y",
"in"=>"$$y.id"}}}]}}}}}]
[{"_id"=>BSON::ObjectId('60aee7b82c97a61504d9b01a'),
"listOfSettings"=>
[{"name"=>"setting1", "listOfObjects"=>["111", "112"]},
{"name"=>"setting2", "listOfObjects"=>["113", "114"]}]}]

Related

MongoDB - Can't push an item to an array inside an object inside an array

I have this object in MongoDB:
{
_id: ObjectId("1"),
data: {
city: "ccp",
universities: [
{
_id: "2"
name: "universityOne"
students: []
},
{
_id: "3"
name: "universityTwo",
students: []
}
]
}
}
I need to push a Student object inside the students array inside the universityOne object inside the universities array, inside the global object.
I tried documentation and come up with this queries.
Mongo shell returns { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }. And nothing changes.
Here are the queries formatted/unformatted, so you can see them:
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[id].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"id._id": ObjectId("2")}]})
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[id].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"id._id": ObjectId("2")}]})
This second query is with the name of the university on the mongo [<identifier>]. But doesn't work either.
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[name].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"name.name": "universityOne"}]})
db.pautas.updateOne({_id: ObjectId("1")}, {$push: {"data.universities.$[name].students": {name: "aStudentName", age: 22}}}, {arrayFilters: [{"name.name": "universityOne"}]})
Regards.
UPDATE
Real object:
{
_id: ObjectId("5c6aef9bfc1a2693588827d9"),
datosAcademicos: {
internados: [
{
_id: ObjectId("5c6bfae98857df9f668ff2eb"),
pautas: []
},
{
_id: ObjectId("5c6c140f8857df9f668ff2ec"),
pautas: []
}
]
}
}
I need to add a Pauta to the pautas array. I've set pautas to an array of strings for debugging purpose, so just need to push a "hello world" or whatever string.
I tried this with the answers I've been given:
db.pautas.updateOne({"_id":ObjectId("5c6aef9bfc1a2693588827d9"), "datosAcademicos.internados._id": ObjectId("5c6bfae98857df9f668ff2eb")}, { $push: {"datosAcademicos.internados.$.pautas": "hi"}})
db.pautas.updateOne({"_id":ObjectId("5c6aef9bfc1a2693588827d9"), "datosAcademicos.internados._id": ObjectId("5c6bfae98857df9f668ff2eb")}, { $push: {"datosAcademicos.internados.$.pautas": "hi"}})
Update 2:
Mongo version: v4.0.2
Using Robo 3T.
I created a test database
And tried this command
Still not working.
There are 3 issues with your statement. First, the root ID field is "id" and you're querying the "_ID".
Second, you should put the match fields altogether. This update works as expected.
Third, you should use "$" to select the nested array position, not "$[id]".
db.pautas.updateOne(
{ "id": ObjectId("1"), "data.universities._id": "2"},
{$push:
{"data.universities.$.students": {name: "aStudentName", age: 22}}
})
Answer to the question UPDATE:
The update statement worked just fine.
Update statement
Record after update - I ran my update with your data and then the update code you posted, both worked just fine.

how to merge similar values in normalizr function?

I have unusual response from server like this
[
{
id: 1,
name: "Alexandr",
children: [
{
id: 2,
name: "Stephan"
},
{
id: 3,
name: "Nick"
}
]
},
{
id: 4,
name: "David",
children: [
{
id: 3,
name: "Nick"
},
{
id: 6,
name: "Paul"
}
]
}
]
i would like to normalize this response to receive a diction with all people. So, i use normalizr go flat this
const people= new Schema('people');
people.define({
Children: arrayOf(people),
NotOwnChildren: arrayOf(people)
});
let normalized = normalize(response.data, arrayOf(people));
but doing like this i get an error
"When merging two people, found unequal data in their "Children" values. Using the earlier value."
How can i adjust normalizr to merge people with same id (update entities with newest data)?
It looks like you're getting two people that have differing values for one of their keys (I'm assuming your example input is truncated).
For Normalizr#2:
You can use a custom mergeIntoEntity function to resolve the issue manually.
For Normalizr#>=3.0.0, you'll need use mergeStrategy.

MongoDB query into an array of arrays

I have the following schema into my DB:
{ name: 'AFG',
documents: [
{ name: 'doc1',
templates: [
{ name: 'templ1',
lastModified: <Date>},
...]
},
...]
}
I am trying to make a query to look for the template with name 'templ1'. If I find it, I have to compare the last modification date with another and update the value. If I don't find it, I have to push it into the array.
I have tried to do it with the following query:
Country.findOne({name : countrySelected, documents : {$elemMatch: {name: documentSelected}}, 'documents.templates' : {$elemMatch: {name: templateSelected}}}, function(err, templateFound){...}
But I get the following error:
MongoError: cannot use the part (documents of documents.templates) to traverse the element ({documents: [ { name: "Passport", templates: [] }, { name: "Visa", templates: [] }, { name: "Driver's Licence", templates: [] }, { name: "Id Card", templates: [] }, { name: "Other", templates: [] } ]})
Someone knows how can I do it? Thanks!!
You have to use the $in operator to search in arrays.
Country.findOne({
name : countrySelected,
documents: {
$elemMatch: {
$and: [{
name: {
$in: documentSelected //<-- must be array like ['doc1']
}
}, {
'templates.name': {
$in: documentSelected //<-- must be array like ['tmpl1']
}
}]
}
}
}, function(err, templateFound){
//do something with err and templateFound
});
To update the LastModified date you need to update it in the callback function and save the document.
See this answer for nested array updates

Filter array elements by nested array of objects

First, let me say that this question is slightly different to others I've seen regarding nested arrays in mongo.
Most of them ask how to get a specific element in a nested array. I want to get all elements of an array that has itself another array containing a given value.
I have documents, that look like this:
{
_id: something,
value: something,
items: [{
name: someItem,
price: somePrice,
stores:[
{name: storeName, url: someUrl },
{name: anotherStoreName, url: someOtherUrl}
]
},
{
name: someOtherItem,
price: someOtherPrice,
stores:[
{name: storeName, url: someUrl },
{name: yetAnotherStoreName, url: someOtherUrl}
]
}]
}
What I want is to get only the items elements that have a given store in the stores array.
This is, if I ask storeName, I should get both items in the example. But if I ask for anotherStoreName, I should only get the first element of the array items.
Searching for similar questions and trying the solutions I can only get the first matching element of items, but I want all the matching elements.
I'd appreciate any help.
You should use mongo aggregation to get result in following way.
First use $unwind to separate array of items and then add match condition.
db.collection.aggregate([{
$unwind: "$items"
}, {
$match: {
"items.stores.name": "storeName"
}
}, {
$project: {
_id: 0,
items: "$items" //you can add multiple fields here
}
}]);
You should use the aggregation framework to unwind elements on the items array and then treat them as individual documents.
db.data.aggregate([
{
$unwind: "$items"
},
{
$project: {
_id: 0,
items: "$items"
}
},
{
$match: {
"items.stores.name": "yetAnotherStoreName"
}
}
]);
$project just let you work with the important part of the document.
This works on the mongoshell be carefully when using your driver.
The result.

Find list of collections by tags filter "OR"

I want make a filter tag system with mongoose.
The principle is simple, I have a model that has an array of string and an array in input.
I want select elements which contains tags in the input array.
Example:
game_console [{
name: "Playsation",
tags: ["old", "sony"]
}, {
name: "xBox",
tags: ["nintendo", "powerfull"]
}, {
name: "megadrive",
tags: ["old", "sega"]
}, {
name: "Gameboy",
tags: ["prehistory", "sega"]
}];
If I put in array input ["old", "sega"], I must get the elements: Playstation and Megadrive.
After searching I found $in and I tested that:
Preambule.find({
'tags': {
$in: tags_array_input
}
}).sort('-commentaires_size')
.exec(function(err, things) {
// [...]
});
It works but only if my field tags was not an array of String..
I don't know what to search in documentation.
My model :
var PreambuleSchema = new Schema({
titre: String,
description: String,
content: String,
auteur: String,
tags: [String],
votes: [],
commentaires: [{
"authorMail": String,
"content": String
}],
commentaires_size: Number,
date: String
});
I'm using yeoman fullstacks, Angularjs, mangoose, nodeJs.

Resources