How to group nasted array in mongo db - mongodb

In my mongodb collection I have next records
{ "_id" : ObjectId("5d0dfb68264b2d01a3237a3e"), "name" : "lexa", "cat" : 2, "gender" : "male", "date" : ISODate("2019-06-22T09:56:56.070Z") }
{ "_id" : ObjectId("5d0dfb6c264b2d01a3237a3f"), "name" : "dima", "cat" : 2, "gender" : "male", "date" : ISODate("2019-06-22T09:57:00.925Z") }
{ "_id" : ObjectId("5d0dfb75264b2d01a3237a40"), "name" : "lena", "cat" : 2, "gender" : "female", "date" : ISODate("2019-06-22T09:57:10.003Z") }
{ "_id" : ObjectId("5d0dfb7a264b2d01a3237a41"), "name" : "nina", "cat" : 2, "gender" : "female", "date" : ISODate("2019-06-22T09:57:14.941Z") }
{ "_id" : ObjectId("5d0dfb8f264b2d01a3237a42"), "name" : "nina", "cat" : 1, "gender" : "female", "date" : ISODate("2019-06-22T09:57:35.128Z") }
{ "_id" : ObjectId("5d0dfb93264b2d01a3237a43"), "name" : "lena", "cat" : 1, "gender" : "female", "date" : ISODate("2019-06-22T09:57:39.789Z") }
{ "_id" : ObjectId("5d0dfb9b264b2d01a3237a44"), "name" : "dima", "cat" : 1, "gender" : "male", "date" : ISODate("2019-06-22T09:57:47.150Z")
Then I use aggregation mongo framework to group that records by cat.
db.foo.aggregate([{'$group':
{
'_id': '$cat',
'users':
{'$push':
{
'name':'$name',
'gender': '$gender'
}
}
}
}])
That query returns me next result
{
"_id" : 1,
"users" : [
{
"name" : "nina",
"gender" : "female"
},
{
"name" : "lena",
"gender" : "female"
},
{
"name" : "dima",
"gender" : "male"
}
]
}
{
"_id" : 2,
"users" : [
{
"name" : "lexa",
"gender" : "male"
},
{
"name" : "dima",
"gender" : "male"
},
{
"name" : "lena",
"gender" : "female"
},
{
"name" : "nina",
"gender" : "female"
}
]
}
And the question is what I need to add to my query to group by my users array. I want to get something like this
{
"_id" : 1,
"users" : [
{
"gender": "male",
"names": ["dima"]
},
{
"gender": "female",
"names": ["lena", "nina"]
}
]
}
{
"_id" : 2,
"users" : [
{
"gender": "male",
"names": ["lexa", "dima"]
},
{
"gender": "female",
"names": ["lena", "nina"]
}
]
}
I need to have my nested array been grouped too without loosing first group result

Would be easier to first group by cat X gender and then restructure the data like so :
db.foo.aggregate([
{'$group':
{
'_id': {cat: '$cat', gender: "$gender"},
'names':
{'$push':
{
'name':'$name',
}
}
}
},
{
$group: {
'_id': "$_id.cat",
users: { $push: { gender: "$_id.gender", names: "$names" }
}
}
])

Related

Requests in mongodb

I have three objects. I am trying to fing the parents who haven't got a job. I am writing this code:
db.getCollection('students').find({
'parents.profession':{$exists: false}
})
I have no mistakes, but it is loking for me users who haven't got a parents. What am i doing wrong
My Objects:
{
"_id" : ObjectId("60c1bd314ed90f98fbbf9d5b"),
"name" : "Ivan",
"class" : 2.0,
"lessons" : [
"basic"
],
"avgScore" : 4.2,
"parents" : [
{
"gender" : "male",
"name" : "Ivan",
"profession" : "trainer"
},
{
"gender" : "female",
"name" : "Vika"
}
]
}
{
"_id" : ObjectId("60c1bd314ed90f98fbbf9d5d"),
"name" : "Kostya",
"class" : 2.0,
"lessons" : [
"basic"
],
"avgScore" : 4.24,
"parents" : [
{
"gender" : "male",
"name" : "Ivan",
"profession" : "blogger"
},
{
"gender" : "male",
"name" : "Andriy",
"profession" : "blogger"
}
]
}
Use $elemMatch:
db.collection.find({
"parents": {
"$elemMatch": {
profession: {
$exists: false
}
}
}
})
Here is the working example: https://mongoplayground.net/p/XT8JJdZ9L5H

Map aggregation results in Mongo

Here is the data set:
{ "_id" : "1", "key" : "111", "payload" : 100, "type" : "foo", "createdAt" : ISODate("2016-07-08T11:59:18.000Z") }
{ "_id" : "2", "key" : "111", "payload" : 100, "type" : "bar", "createdAt" : ISODate("2016-07-09T11:59:19.000Z") }
{ "_id" : "3", "key" : "222", "payload" : 100, "type" : "foo", "createdAt" : ISODate("2016-07-10T11:59:20.000Z") }
{ "_id" : "4", "key" : "222", "payload" : 100, "type" : "foo", "createdAt" : ISODate("2016-07-11T11:59:21.000Z") }
{ "_id" : "5", "key" : "222", "payload" : 100, "type" : "bar", "createdAt" : ISODate("2016-07-12T11:59:22.000Z") }
I have to group them by key:
db.items.aggregate([{$group: {_id: {key: '$key'}}}])
that produces the next set:
{ "_id" : { "key" : "111" } }
{ "_id" : { "key" : "222" } }
And after that I have to retrieve the most recent values of foo and bar per each group record.
My question is what is the most optimal way to do it? I can iterate the items in javascript and perform additional roundtrip to DB per each group result. But I'm not sure if it's time-efficient.
I am not sure about the most optimal way to do it, but the easy one will be to expand your aggregation pipeline like
db.items.aggregate([
{
$group:
{
_id: { key: "$key", type: "$type" },
last: { $max: "$createdAt" }
}
},
{
$group:
{
_id: { key: "$_id.key" },
mostRecent: { $push: { type: "$_id.type", createdAt: "$last" } }
}
}
]);
that for your collection of documents will result into
{ "_id" : { "key" : "222" }, "mostRecent" : [ { "type" : "bar", "createdAt" : ISODate("2016-07-12T11:59:22Z") }, { "type" : "foo", "createdAt" : ISODate("2016-07-11T11:59:21Z") } ] }
{ "_id" : { "key" : "111" }, "mostRecent" : [ { "type" : "bar", "createdAt" : ISODate("2016-07-09T11:59:19Z") }, { "type" : "foo", "createdAt" : ISODate("2016-07-08T11:59:18Z") } ] }

How to find count of a key in one document using mongoDB?

I have following structure in my collection:
users:[
{
"name":"ABC",
"address":{
"city":"London",
"country":"UK",
}
},
{
"name":"XYZ",
"address":{
"city":"London",
"country":"UK",
}
},
{
"name":"PQR",
"address":{
"city":"NewYork",
"country":"US",
}
}
]
I want count of number of occurrences of 'city' key in 'address' and 'name' as a result.
I want to query above collection and want following output:
[{
"name":"ABC",
"city":"London",
"count":2
},{
"name":"XYZ",
"city":"London",
"count":2
}, {
"name":"PQR",
"city":"NewYork",
"count":1
}
]
I simulated your collection
{
"_id" : ObjectId("547c30ae371ea419f07b9550"),
"users" : [
{
"name" : "ABC",
"address" : {
"city" : "London",
"country" : "UK"
}
},
{
"name" : "XYZ",
"address" : {
"city" : "London",
"country" : "UK"
}
},
{
"name" : "PQR",
"address" : {
"city" : "NewYork",
"country" : "US"
}
}
]
}
And then I use aggregate framework
db.coll.aggregate([
{
$unwind:"$users"
},
{
$group:{
_id:"$users.address.city",
name:{$push:"$users.name"},
city:{$first:"$users.address.city"},
count:{$sum:1}
}
},{
$unwind:"$name"
},{
$project:{
_id:0,
"city":"$_id",
"name":1,
"city":1,
"count":1
}
}])
result:
{
"result" : [
{
"name" : "PQR",
"city" : "NewYork",
"count" : 1
},
{
"name" : "ABC",
"city" : "London",
"count" : 2
},
{
"name" : "XYZ",
"city" : "London",
"count" : 2
}
],
"ok" : 1
}
UPDATE AFTER QUESTION
I added a new Document
{
"_id" : ObjectId("547c394c371ea419f07b9551"),
"users" : [
{
"address" : {
"city" : "Livorno",
"country" : "LI"
}
},
{
"address" : {
"city" : "Livorno",
"country" : "LI"
}
},
{
"address" : {
"city" : "NewYork",
"country" : "US"
}
}
]
}
and new Query
db.coll.aggregate([
{
$unwind:"$users"
},
{
$group:{
_id:"$users.address.city",
"name": {
$push:{"$ifNull": ["$users.name","$_id"]}
},
city:{$first:"$users.address.city"},
count:{$sum:1}
}
},{
$unwind:"$name"
},{
$project:{
_id:0,
"city":"$_id",
"name":1,
"city":1,
"count":1
}
}])
Result:
{
"result" : [
{
"name" : "PQR",
"city" : "NewYork",
"count" : 2
},
{
"name" : ObjectId("547c394c371ea419f07b9551"),
"city" : "NewYork",
"count" : 2
},
{
"name" : ObjectId("547c394c371ea419f07b9551"),
"city" : "Livorno",
"count" : 2
},
{
"name" : ObjectId("547c394c371ea419f07b9551"),
"city" : "Livorno",
"count" : 2
},
{
"name" : "ABC",
"city" : "London",
"count" : 2
},
{
"name" : "XYZ",
"city" : "London",
"count" : 2
}
],
"ok" : 1
}

Ask update and delete multiple array in MongoDb

I have a real case in my project:
> db.foo.insert({a:'1',
... province: [{id:'1',name:'Yogyakarta',state:[{id:'1',name:'bantul'}]}]
... })
Then I find()...
> db.foo.find();
> { "_id" : ObjectId("5279ef4c6cfd9d5c0e19bbe0"),
"a" : "1",
"province" : [
{"id" : "1",
"name" : "Yogyakarta",
"state" : [
{"id" : "1","name" : "bantul" }
]
}
]
}
how to remove and update state with id='1'
REMOVE
To remove the documents that match a deletion criteria, call the remove() method with the <query> parameter.
db.foo.remove({'province.state.id': '1'})
Example
First, insert data Yogyakarta - Bantul
db.foo.insert({a:'1', province: [{id:'1',name:'Yogyakarta',state:[{id:'1',name:'bantul'}]}] })
Insert data Jakarta - Jakarta Selatan
db.foo.insert({a:'1', province: [{id:'2',name:'Jakarta',state:[{id:'2',name:'Jakarta Selatan'}]}] })
Now, you have two documents
db.foo.find();
Result
[
{ "a" : "1", "_id" : { "$oid" : "527b54c6cc937439340367f9" }, "province" : [ { "name" : "Yogyakarta", "id" : "1", "state" : [ { "name" : "bantul", "id" : "1" } ] } ] },
{ "a" : "1", "_id" : { "$oid" : "527b54d3cc937439340367fa" }, "province" : [ { "name" : "Jakarta", "id" : "2", "state" : [ { "name" : "Jakarta Selatan", "id" : "2" } ] } ] }
]
Now, delete document where the subdocument province contains a field state whose value 1.
db.foo.remove({'province.state.id': '1'})
Check
db.foo.find();
Now, you have one document
[
{ "a" : "1", "_id" : { "$oid" : "527b54d3cc937439340367fa" }, "province" : [ { "name" : "Jakarta", "id" : "2", "state" : [ { "name" : "Jakarta Selatan", "id" : "2" } ] } ] }
]
UPDATE
By default, the update() method updates a single document. If the multi option is set to true, the method updates all documents that match the query criteria.
db.foo.update({'province.state.id': '2'}, { $set: {'a': '2'} })
Check
db.foo.find();
Result
[
{ "a" : "2", "_id" : { "$oid" : "527b54d3cc937439340367fa" }, "province" : [ { "name" : "Jakarta", "id" : "2", "state" : [ { "name" : "Jakarta Selatan", "id" : "2" } ] } ] }
]

MongoDB groupby query

I have colletions containing records like
{ "type" : "me", "tid" : "1" }
{ "type" : "me", "tid" : "1" }
{ "type" : "me", "tid" : "1" }
{ "type" : "you", "tid" : "1" }
{ "type" : "you", "tid" : "1" }
{ "type" : "me", "tid" : "2" }
{ "type" : "me", "tid" : "2"}
{ "type" : "you", "tid" : "2"}
{ "type" : "you", "tid" : "2" }
{ "type" : "you", "tid" : "2"}
I have want result like below
[
{"tid" : "1","me" : 3,"you": 2},
{"tid" : "2","me" : 2,"you": 3}
]
I have tried group and; aggregate queries doesn't get required result format.
below is the group query.
db.coll.group({
key: {tid : 1,type:1},
cond: { tid : { "$in" : [ "1","2"]} },
reduce: function (curr,result) {
result.total = result.total + 1
},
initial: { total : 0}
})
it result is like
[
{"tid" : "1", "type" : "me" ,"total": 3 },
{"tid" : "1","type" : "you" ,"total": 2 },
{"tid" : "2", "type" : "me" ,"total": 2 },
{"tid" : "2","type" : "you" ,"total": 3 }
]
following is aggregate query
db.coll.aggregate([
{$match : { "tid" : {"$in" : ["1","2"]}}},
{$group : { _id : {tid : "$tid",type : "$type"},total : {"$sum" : 1}}}
])
gives following result
{
"result" :
[
{"_id" : {"tid" : "1","type" : "me"},"total" : 3},
{"_id" : {"tid" : "2","type" : "me" },"total" : 2},
{"_id" : {"tid" : "2","type" : "you"},"total" : 3}
]
"ok" : 1
}
it is possible to obtain I specified result or I have to do some manipulation in my code.
Thanks
If you change your aggregation to this:
db.so.aggregate([
{ $match : { "tid" : { "$in" : ["1", "2"] } } },
{ $group : {
_id : { tid : "$tid", type : "$type" },
total : { "$sum" : 1 }
} },
{ $group : {
_id : "$_id.tid",
values: { $push: { type: "$_id.type", total: '$total' } }
} }
])
Then your output is:
{
"result" : [
{
"_id" : "1",
"values" : [
{ "type" : "you", "total" : 2 },
{ "type" : "me", "total" : 3 }
]
},
{
"_id" : "2",
"values" : [
{ "type" : "me", "total" : 2 },
{ "type" : "you", "total" : 3 }
]
}
],
"ok" : 1
}
Although that is not the same as what you want, it is going to be the closest that you can get. And in your application, you can easily pull out the values in the same was as with what you would like to get out of it.
Just keep in mind, that in general you can not promote a value (you, me) to a key — unless your key is of a limited set (3-4 items max).