Create object in aggregation - mongodb

I have this result after group by UserId with Aggregation Framework. I want create a single Document with "merge" array foo and ability
My Aggregation
{
"result" : [
{
"_id" : {
"userId" : ObjectId("53bab268ceee750615240269")
},
"foo" : [
"0.109",
"0.105",
"0.50",
"0.1",
"foo"
],
"ability" : [
"Power",
"Energy",
"ReactiveEnergy",
"Stamina",
"bar"
]
}
],
"ok" : 1
}
I like to have
{
"result" : [
{
"_id" : {
"userId" : ObjectId("53bab268ceee750615240269")
},
"fooFinal" : [
{"0.109",power}
{"0.105",Energy }
{"0.50",ReactiveEnergy }
{"0.1",Stamina }
{"foo",bar}
],
}
],
"ok" : 1
}
I want to use like similar $each, but i can't use it inside aggregate

Try to do This in your $group
$group:{
_id:{'userId':'$userId'},
'fooFinal' : {$push:{
foo:'$foo',
ability:'$ability'}
}
}

Related

Aggregate the arrays in MongoDB

I have a database structured like this:
{
"teams" : [
{
"best_players" : [
{
"contact" : {
"name" : "SomeName1"
},
"characteristic" : {
"skills" : "good"
}
}
],
"teamname" : "SomeTeam1"
},
{
"best_players" : [
{
"contact" : {
"name" : "SomeName2"
},
"characteristic" : {
"skills" : "bad"
}
}
],
"teamname" : "SomeTeam2"
}
]
}
I need to rename arrays and fields, and see the information in a different form.
What i'm expecting with aggregation-framework:
{
"team_players" : [
{
"player_name" : "SomeName1",
"player_skills" : "good" ,
"team_name" : "SomeTeam1"
},
{
"player_name" : "SomeName2",
"player_skills" : "bad" ,
"team_name" : "SomeTeam2"
}
]
}
What is the right way to query my result?
You can use $map within $project to format documents.
Something like
db.colname.aggregate(
{"$project":{
"team_players":{
"$map":{
"input":"$teams",
"in":{
"$let":{
"vars":{"best_player":{"$arrayElemAt":["$$this.best_players",0]}},
"in":{
"player_name":"$$best_player.contact.name",
"player_skills":"$$best_player.characteristic.skills",
"team_name":"$$this.teamname"
}
}
}
}
}
}})

How to check if nested arrays are ALL empty in mongodb?

I have something like below:
{
"_id" : "1",
"firstArray" : [
{
"_id" : "11",
"secondArray" : [ ]
},
{
"_id" : "12",
"secondArray" : [ ]
},
{
"_id" : "13",
"secondArray" : [ { "type" : "somthing" } ]
}
]
},
{
"_id" : "2",
"firstArray" : [
{
"_id" : "21",
"secondArray" : [ ]
},
{
"_id" : "22",
"secondArray" : [ ]
}
]
}
I need a mongodb query to find documents which ALL of the nested secondArrays are empty? the query should return second document and not the first one.
to solve that, we need to check size of arr2, but to enable that we need first to unwind arr1.
Please find below aggregation framework snippet which solves this problem,
db.pmoubed.aggregate([{
$unwind : "$firstArray"
}, {
$project : {
_id : 1,
firstArray : 1,
isNotEmpty : {
$size : "$firstArray.secondArray"
}
}
}, {
$group : {
_id : "$_id",
isNotEmpty : {
$sum : "$isNotEmpty"
},
firstArray : {
$push : "$firstArray"
}
}
}, {
$match : {
"isNotEmpty" : 0
}
}
])
Any comments welcome

Change field name in result Mongo Query

I have this Mongo query:
db.getCollection('Catalogos').aggregate(
{ $match: {Items: {$elemMatch: {'MarIclase': '04'} } } },
{ $unwind : "$Items" },
{ $match: { "Items.MarIclase" : "04" } },
{ $group : {
_id : "$_id",
Items : { $push : { 'MarIclase': "$Items.MarIclase", 'MarCdescrip' : '$Items.MarCdescrip' } }
}}
);
The result of this query is:
{
"result" : [
{
"_id" : "CAT_MARCAS_VU",
"Items" : [
{
"MarIclase" : "04",
"MarCdescrip" : "5500 LARSON"
},
{
"MarIclase" : "04",
"MarCdescrip" : "A LINER"
}
]
}
],
"ok" : 1.0000000000000000
}
I'd like to have this result:
{
"result" : [
{
"_id" : "CAT_MARCAS_VU",
"Items" : [
{
"04" : "5500 LARSON"
},
{
"04" : "A LINER"
}
]
}
],
"ok" : 1.0000000000000000
}
¿Do you know if I can make something in the $push and change the fieldnames for values?
I'd like to have something like this:
{ "04" : "A LINER" }
{ "04" : "5500 LARSON" }
Thank you!

MongoDB pull element from array two levels deep

I have a collection with the following items :
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 } ] } ], "_id" : ObjectId("51ddb6f9b18996be485cba6f") }
{ "Queries" : [ { "Results" : [ { "id" : 0 }, { "id" : 3 } ] } ], "_id" : ObjectId("51ddb701b18996be485cba70") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 } ] } ], "_id" : ObjectId("51ddb705b18996be485cba71") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 }, { "id" : 4 } ] } ], "_id" : ObjectId("51ddb70db18996be485cba72") }
{ "Queries" : [ { "Results" : [ { "id" : 1 }, { "id" : 2 }, { "id" : null } ] } ], "_id" : ObjectId("51ddb7e4b18996be485cba73") }
The "Queries" field on my documents contains an array of subdocuments. These subdocuments contain an array of another subdocument.
I want to remove all entries in the "Results" field where the documents "id" field is 1.
I tried the following without success :
update({}, {$pull :{"Queries.Results": {"id":1}}}, {"multi":true})
update({}, {$pull :{"Queries.Results.id":1}}, {"multi":true})
How would I achieve this in MongoDB?
EDIT: Here is the expected result of find() after the update
{ "Queries" : [ { "Results" : [ { "id" : 2 } ] } ], "_id" : ObjectId("51ddb6f9b18996be485cba6f") }
{ "Queries" : [ { "Results" : [ { "id" : 0 }, { "id" : 3 } ] } ], "_id" : ObjectId("51ddb701b18996be485cba70") }
{ "Queries" : [ { "Results" : [ { "id" : 2 } ] } ], "_id" : ObjectId("51ddb705b18996be485cba71") }
{ "Queries" : [ { "Results" : [ { "id" : 2 }, { "id" : 4 } ] } ], "_id" : ObjectId("51ddb70db18996be485cba72") }
{ "Queries" : [ { "Results" : [ { "id" : 2 }, { "id" : null } ] } ], "_id" : ObjectId("51ddb7e4b18996be485cba73") }
This is the query you have to use:
db.collection.update( { "Queries.Results.id":1 }, { $pull: { "Queries.$.Results": {"id":1} } } )
You need to specify the "where" clause in order to find the document to update.
You are also missing the positional operator $, you have to use it because Queries can have multiple Results.

Aggregate of different subtypes in document of a collection

abstract document in collection md given:
{
vals : [{
uid : string,
val : string|array
}]
}
the following, partially correct aggregation is given:
db.md.aggregate(
{ $unwind : "$vals" },
{ $match : { "vals.uid" : { $in : ["x", "y"] } } },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
that may lead to the following result:
"result" : [
{
"_id" : {
"uid" : "x"
},
"vals" : [
[
"24ad52bc-c414-4349-8f3a-24fd5520428e",
"e29dec2f-57d2-43dc-818a-1a6a9ec1cc64"
],
[
"5879b7a4-b564-433e-9a3e-49998dd60b67",
"24ad52bc-c414-4349-8f3a-24fd5520428e"
]
]
},
{
"_id" : {
"uid" : "y"
},
"vals" : [
"0da5fcaa-8d7e-428b-8a84-77c375acea2b",
"1721cc92-c4ee-4a19-9b2f-8247aa53cfe1",
"5ac71a9e-70bd-49d7-a596-d317b17e4491"
]
}
]
as x is the result aggregated on documents containing an array rather than a string, the vals in the result is an array of arrays. what i look for in this case is to have a flattened array (like the result for y).
for me it seems like that what i want to achieve by one aggegration call only, is currently not supported by any given operation as e.g. a type conversion cannot be done or unwind expectes in every case an array as input type.
is map reduce the only option i have? if not ... any hints?
thanks!
You can use the aggregation to do the computation you want without changing your schema (though you might consider changing your schema simply to make queries and aggregations of this field easier to write).
I broke up the pipeline into multiple steps for readability. I also simplified your document slightly, again for readability.
Sample input:
> db.md.find().pretty()
{
"_id" : ObjectId("512f65c6a31a92aae2a214a3"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a4"),
"uid" : "x",
"val" : "string"
}
{
"_id" : ObjectId("512f65c6a31a92aae2a214a5"),
"uid" : "y",
"val" : "string2"
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a6"),
"uid" : "y",
"val" : [
"string3",
"string4"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a7"),
"uid" : "z",
"val" : [
"string"
]
}
{
"_id" : ObjectId("512f65e8a31a92aae2a214a8"),
"uid" : "y",
"val" : [
"string1",
"string2"
]
}
Pipeline stages:
> project1 = {
"$project" : {
"uid" : 1,
"val" : 1,
"isArray" : {
"$cond" : [
{
"$eq" : [
"$val.0",
[ ]
]
},
true,
false
]
}
}
}
> project2 = {
"$project" : {
"uid" : 1,
"valA" : {
"$cond" : [
"$isArray",
"$val",
[
null
]
]
},
"valS" : {
"$cond" : [
"$isArray",
null,
"$val"
]
},
"isArray" : 1
}
}
> unwind = { "$unwind" : "$valA" }
> project3 = {
"$project" : {
"_id" : 0,
"uid" : 1,
"val" : {
"$cond" : [
"$isArray",
"$valA",
"$valS"
]
}
}
}
Final aggregation:
> db.md.aggregate(project1, project2, unwind, project3, group)
{
"result" : [
{
"_id" : "z",
"vals" : [
"string"
]
},
{
"_id" : "y",
"vals" : [
"string1",
"string4",
"string3",
"string2"
]
},
{
"_id" : "x",
"vals" : [
"string"
]
}
],
"ok" : 1
}
If you modify your schema using always "vals.val" field as an array field (even when the record contains only one element) you can do it easily as follows:
db.test_col.insert({
vals : [
{
uid : "uuid1",
val : ["value1"]
},
{
uid : "uuid2",
val : ["value2", "value3"]
}]
});
db.test_col.insert(
{
vals : [{
uid : "uuid2",
val : ["value4", "value5"]
}]
});
Using this approach you only need to use two $unwind operations: one unwinds the "parent" array and the second unwinds every "vals.val" value. So, querying like
db.test_col.aggregate(
{ $unwind : "$vals" },
{ $unwind : "$vals.val" },
{
$group : {
_id : { uid : "$vals.uid" },
vals : { $addToSet : "$vals.val" }
}
}
);
You can obtain your expected value:
{
"result" : [
{
"_id" : {
"uid" : "uuid2"
},
"vals" : [
"value5",
"value4",
"value3",
"value2"
]
},
{
"_id" : {
"uid" : "uuid1"
},
"vals" : [
"value1"
]
}
],
"ok" : 1
}
And no, you can't execute this query using your current schema, since $unwind fails when the field isn't an array field.