Update single sub document in multiple array in mgo - mongodb

I have following mongodb (3.4.x) document, i code in golang with mgo driver
{
"id": "5981d4c2795a1b4a801ee027",
"scenarioId": "59804b10d8ee910085e33865",
"messages": [
{
"id": "5981d4c2795a1b4a801ee028",
"toQueue": [
{
"id": "5981d4c2795a1b4a801ee029",
"to": {
"email": "some#email.com"
},
"channel": "EMAIL",
"toType": "EMAIL",
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
},
{
"id": "5981d4c2795a1b4a801ee02a",
"to": {
"phone": "+381631891245"
},
"channel": "SMS",
"toType": "PHONE",
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
}
],
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
},
{
"id": "5981d4c2795a1b4a801ee02b",
"toQueue": [
{
"id": "5981d4c2795a1b4a801ee02c",
"to": {
"phone": "+123456789"
},
"channel": "SMS",
"toType": "PHONE",
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
}
],
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
}
],
"messageStages": [
{
"id": "5981d4c2795a1b4a801ee033",
"validityPeriod": 5,
"validityPeriodTimeUnit": "MINUTES",
"providerId": 5,
"email": {
"text": "this is the email text",
"subject": "and here the email subject"
},
"status": {
"id": 1,
"groupId": 1,
"groupName": "PROCESSING",
"name": "APPROVED",
"description": "MessageChain approved for processing"
}
}
]
}
and I know the value of messages.$.toQueue.id, where i want to update status.id in the related toQueue Array Item.
I tried to do it that way:
query = bson.M{
"messages.toQueue._id": toQueueId,
}
update = bson.M{
"$set": bson.M{
"messages.$.toQueue.$.status.id": status.Id,
"messages.$.toQueue.$.status.name": status.Name,
"messages.$.toQueue.$.status.groupId": status.GroupId,
"messages.$.toQueue.$.status.groupName": status.GroupName,
"messages.$.toQueue.$.status.description": status.Description,
},
}
err = cr.Update(query,update)
but multiple $ are not allowed. Without it's also not updateable.
Is there any way to update only the subdocument, which i found in query ?

Neil Lunn gave the right answer.
It's not possible to work with nested arrays the way I tried to do. And he is also right, that problem is coming from design mistake.
So I changed it as him and Markus W Mahlberg suggested to move the toQueue part in a separate collection, with messageId as a reference.

Related

Find specific field in array by last date and updating it

I have the document below, and I want to update in exams, the last exam with status "started" (according to date), I need to add to "done" another number
{
"_id": ObjectID("5d2a371cec7eaf00119df614"),
"firstname": "Laura",
"lastname": "Warriner",
"image": "2019-07-14t05:25:23.352z_13.jpg",
"exams": [
{
"examId": "Ba6apmRmz",
"name": "general",
"language": "es",
"version": 2,
"testId": "2000",
"status": [
{
"status": "created",
"date": ISODate("2019-09-08T11:49:42.124Z")
},
{
"status": "started",
"date": ISODate("2019-09-08T11:55:09.873Z"),
"done": [1]
},
{
"status": "started",
"date": ISODate("2019-09-09T12:01:57.886Z"),
"done": [2,3]
},
{
"status": "completed",
"date": ISODate("2019-09-09T12:01:57.886Z")
}
]
}
]
}
Thanks in advance

How to check $exists in one to many relationship mongodb sails js?

Is there any way to find records that collection is empty?
For example please find below array. I want only that records with index "companydata" is empty. and also how can i get data that does not have empty "companydata" data.
Thanks in advance.
[
{
"company_id": {
"company_name": "C2",
"slug": "c2",
"is_organized": 1,
"status": "1",
"id": "5adf158f547f7f0314ca8b56",
"companydata": []
},
"user_id": "5ab889aee74a151b50d04ec1",
"status": "0",
"id": "5ae014e7432e85298081be0b"
},
{
"company_id": {
"company_name": "My Compnay",
"slug": "my-compnay",
"is_organized": 1,
"status": "1",
"id": "5ad442d98a0e0c1358ca93df",
"companydata": [
{
"name": "Bhavesh Amin",
"company_id": "5ad442d98a0e0c1358ca93df",
"status": "0",
"id": "5ad442da8a0e0c1358ca93e0"
}
]
},
"user_id": "5ab889aee74a151b50d04ec1",
"status": "0",
"id": "5ae01388432e85298081bdf8"
},
{
"company_id": {
"company_name": "Organization Name",
"slug": "organization-name",
"is_organized": 1,
"status": "1",
"id": "5ad08f9b938d1131eceea624",
"companydata": [
{
"name": "Helen H. Langley",
"company_id": "5ad08f9b938d1131eceea624",
"status": "1",
"id": "5ad08f9b938d1131eceea625"
}
]
},
"user_id": "5ab889aee74a151b50d04ec1",
"status": "0",
"id": "5ad42a5f52851a2b1449db2d"
},
]

Nested grouping of array

There are 3 master collection of category , subcategory and criteria each, i will be building framework with any possible combination of category , subcategory and criteria which will be stored as below-
framework document is added below having list of criteriaconfig as embedded object which further have single object of category , subcategory and criteria. you can refer criteriaconfig as link table that u call in mysql.
[
{
"id": "592bc3059f3ad715002b2331",
"name": "Framework1",
"description": "framework 1 for testing",
"criteriaConfigs": [
{
"id": "592bc3059f3ad715002b232f",
"category": {
"id": "591c2f5faa187956b2d0fb39",
"name": "category1",
"description": "category1",
"deleted": false,
"createdDate": 1495019359558
},
"subCategory": {
"id": "591c2f5faa187956b2d0fb83",
"name": "subCat1",
"description": "subCat1"
},
"criteria": {
"id": "591c2f5faa187956b2d0fbad",
"name": "criteria1",
"measure": "Action"
}
},
{
"id": "592bc3059f3ad715002b232e",
"category": {
"id": "591c2f5faa187956b2d0fb37",
"name": "Process",
"description": "Enagagement"
},
"subCategory": {
"id": "591c2f5faa187956b2d0fb81",
"name": "COMM / BRANDING",
"description": "COMM / BRANDING"
},
"criteria": {
"id": "591c2f5faa187956b2d0fba9",
"name": "Company representative forgets about customer on hold",
"measure": ""
}
} ]
},
{
"id": "592bc3059f3ad715002b2332",
"name": "Framework2",
"description": "framework 2 for testing",
"criteriaConfigs": [
{
"id": "592bc3059f3ad715002b232f",
"category": {
"id": "591c2f5faa187956b2d0fb39",
"name": "category1",
"description": "category1"
},
"subCategory": {
"id": "591c2f5faa187956b2d0fb83",
"name": "subCat1",
"description": "subCat1"
},
"criteria": {
"id": "591c2f5faa187956b2d0fbad",
"name": "criteria1",
"measure": "Action"
}
}
]
}
]
i need a view containing framework that will contain all list of category and inside category there will be list of added subcategory and inside subcategory will have list of criteria for single framework.
expected result -
[
{
"id": "f1",
"name": "Framework1",
"description": "framework 1 for testing",
"categories": [
{
"id": "c2",
"name": "category2",
"description": "category2",
"subCategories": [
{
"id": "sb1",
"name": "subCat1",
"description": "subCat1",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr2",
"name": "criteria2",
"measure": "Action"
},
{
"id": "cr3",
"name": "criteria3",
"measure": "Action"
}]
},
{
"id": "sb2",
"name": "subCat2",
"description": "subCat2",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr4",
"name": "criteria4",
"measure": "Action"
}]
}]
},
{
"id": "c1",
"name": "category1",
"description": "category1",
"subCategories": [
{
"id": "sb3",
"name": "subCat3",
"description": "subCat3",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr2",
"name": "criteria2",
"measure": "Action"
}
]},
{
"id": "sb2",
"name": "subCat2",
"description": "subCat2",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr4",
"name": "criteria4",
"measure": "Action"
}]
}
]
}]
},
{
"id": "f2",
"name": "Framework2",
"description": "framework 2 for testing",
"categories": [
{
"id": "c2",
"name": "category2",
"description": "category2",
"subCategories": [
{
"id": "sb4",
"name": "subCat5",
"description": "subCat5",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr3",
"name": "criteria3",
"measure": "Action"
}]
},
{
"id": "sb2",
"name": "subCat2",
"description": "subCat2",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr4",
"name": "criteria4",
"measure": "Action"
}]
}]
},
{
"id": "c1",
"name": "category1",
"description": "category1",
"subCategories": [
{
"id": "sb3",
"name": "subCat3",
"description": "subCat3",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr2",
"name": "criteria2",
"measure": "Action"
}
]},
{
"id": "sb2",
"name": "subCat2",
"description": "subCat2",
"criterias": [
{
"id": "cr1",
"name": "criteria1",
"measure": "Action"
},
{
"id": "cr4",
"name": "criteria4",
"measure": "Action"
}]
}
]
}]
}
]
Note - Category document doesn't have any reference to subcategory and same way subcategory doesn't have any reference to criteria object currently as they are master data and are generic , framework is created with their combination dynamically.
If you want to try to do all the work in the aggregation, you could group first by subcategory, then by category like:
db.collection.aggregate([
{$unwind:"$criteriaConfigs"},
{$project:{
_id:0,
category:"$criteriaConfigs.category",
subCategory:"$criteriaConfigs.subCategory",
criteria:"$criteriaConfigs.criteria"
}},
{$group:{
_id:{"category":"$category","subCategory":"$subCategory"},
criteria:{$addToSet:"$criteria"}
}},
{$group:{
_id:{"category":"$_id.category"},
subCategories:{$addToSet:{subCategory:"$_id.subCategory",
criteria:"$criteria"}}
}},
{$project:{
_id:0,category:"$_id.category",
subCategories:"$subCategories"
}}
])
Depending on how you plan to us the return data, it may be more efficient to return each unique combination:
db.collection.aggregate([
{$unwind:"$criteriaConfigs"},
{$group:{
_id:{
category:"$criteriaConfigs.category.name",
subCategory:"$criteriaConfigs.subCategory.name",
criteria:"$criteriaConfigs.criteria.name"
}
}},
{$project:{
_id:0,
category:"$_id.category",
subCategory:"$_id.subCategory",
criteria:"$_id.criteria"
}}
])
I'm not sure from your question what shape you are expecting the return data to have, so you may need to adjust for that.

Mongodb query inside array with single ObjectID

Here is the json data i am trying to process. I am trying to get messages between two dates. The data is already imported to mongodb.
{
"items": [
{
"date": "2017-04-06T09:46:20.387420+00:00",
"from": {
"id": 4624534,
"links": {
"self": "https://api.hipchat.com/v2/user/4624534"
},
"mention_name": "holy",
"name": "holy god",
"version": "Y1ML0DRJ"
},
"id": "38f90558-2a23-458b-b87b-88dbdf997f7a",
"mentions": [],
"message": "ping",
"type": "message"
},
{
"date": "2017-04-08T04:30:44.240163+00:00",
"from": {
"id": 4624534,
"links": {
"self": "https://api.hipchat.com/v2/user/4624534"
},
"mention_name": "holy",
"name": "holy god",
"version": "Y1ML0DRJ"
},
"id": "822b81e0-8077-41d7-bc50-fc9e4eba7d9e",
"mentions": [],
"message": "https://twitter.com/",
"type": "message"
},
{
"attach_to": "822b81e0-8077-41d7-bc50-fc9e4eba7d9e",
"card": "{\"style\": \"link\", \"description\": \"From breaking news and entertainment to sports and politics, get the full story with all the live commentary.\", \"format\": \"medium\", \"url\": \"https://twitter.com/i/hello\", \"title\": \"Twitter. It's what's happening.\", \"id\": \"https://twitter.com/i/hello\", \"validation\": {\"safehtmls\": [\"activity.html\"], \"safeurls\": [\"url\", \"images.image\", \"images.image-small\", \"images.image-big\", \"icon.url\", \"icon.url#2x\", \"icon\", \"thumbnail.url#2x\", \"thumbnail.url\"]}, \"type\": \"link\", \"thumbnail\": {\"url\": \"https://pbs.twimg.com/ext_tw_video_thumb/850335753108324353/pu/img/T8cV-7bGbbguiRGV.jpg\", \"width\": 599, \"type\": \"image/jpeg\", \"height\": 337}, \"icon\": {\"url\": \"https://abs.twimg.com/a/1491551685/img/t1/favicon.svg\", \"type\": \"image\"}}",
"color": "gray",
"date": "2017-04-08T04:30:44.825185+00:00",
"from": "Link",
"id": "7ccaf2b9-09bb-45ac-a025-c93f1f7df745",
"mentions": [],
"message": "\n\n\n<p><b>Twitter. It's what's happening.</b></p>\n\n\n<p>From breaking news and entertainment to sports and politics, get the full story with all the live commentary.</p>\n\n",
"message_format": "html",
"notification_sender": {
"client_id": "888aec94-afee-45d8-89f7-ae077fcc4a7c",
"id": "hipchat-clinky",
"type": "addon"
},
"type": "notification"
},
{
"date": "2017-04-08T09:39:00.468858+00:00",
"from": {
"id": 4624534,
"links": {
"self": "https://api.hipchat.com/v2/user/4624534"
},
"mention_name": "abcholy",
"name": "holy god",
"version": "Y1ML0DRJ"
},
"id": "8a0de0e0-c312-490e-afcc-b0b16404cd67",
"mentions": [],
"message": "second message",
"type": "message"
},
{
"date": "2017-04-11T15:32:39.367744+00:00",
"from": {
"id": 4624534,
"links": {
"self": "https://api.hipchat.com/v2/user/4624534"
},
"mention_name": "abcholy",
"name": "holy god",
"version": "Y1ML0DRJ"
},
"id": "4c1a090c-cb71-4548-8f96-a03ec2f3fb3b",
"mentions": [],
"message": "https://gist.github.com/abcholy/6d86352e73eab21cdd4fe78b37bd5aa0",
"type": "message"
},
{
"date": "2017-04-11T15:33:42.730696+00:00",
"from": {
"id": 4624534,
"links": {
"self": "https://api.hipchat.com/v2/user/4624534"
},
"mention_name": "abcholy",
"name": "holy god",
"version": "Y1ML0DRJ"
},
"id": "a42b5267-937b-4de5-8c51-de4625742a4a",
"mentions": [],
"message": "hello",
"type": "message"
}
],
"links": {
"self": "https://api.hipchat.com/v2/room/3452990/history"
},
"maxResults": 100,
"startIndex": 0
}
Now, I entered this mongo query:
db.data.find( {"_id" : ObjectId("58ee59f7f35120aaba26cff0")},{ items: { $elemMatch: { "date": {$gte:"2017-04-08T04:30:44.240163+00:00"} } } } )
But it returns just a single item which is the first one. If I try $lte, it also returns single item but I want all the items which fall under the specification of date. How to achieve that?
Your date field is a string, it has no concept of what's greater than that string. You'll need to convert it to a ISODate or another date format for you to use the $gte or $lte operation.

Grouping select with partial

Hello i have here this code:
public function getConversations($user, $limit, $page)
{
$offset = ($page - 1) * $limit;
$qb = $this->createQueryBuilder('message');
$qb
->select('message, partial o.{id}, partial userFrom.{id, username, avatar}, partial userTo.{id, username, avatar}, partial availability.{id, timeFrom}')
->leftJoin('message.from', 'userFrom')
->leftJoin('message.to', 'userTo')
->leftJoin('message.order', 'o')
->leftJoin('o.availabilities', 'availability')
->where($qb->expr()->orX(
$qb->expr()->eq('message.from', ':user'),
$qb->expr()->eq('message.to', ':user')
))
->setParameter('user', $user)
->setFirstResult($offset)
->setMaxResults($limit)
->addOrderBy('message.created', Criteria::DESC);
return $qb->getQuery()->getArrayResult();
}
I want to get only last message connected to each order. If i have 2 messages to order it gives me 2 rows but i need only one (the last one). I tried to use GROUP BY but it didn't work well. It wanted me to put all foreign ids to group by and then the result was really bad.
Result for this is:
{
"id": "813fa986-f89b-411d-8324-727e427cbc01",
"isRead": false,
"created": {
"date": "2016-12-12 18:01:00.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"content": "Hello to you too",
"contentType": "text",
"from": {
"id": "3e1806d6-f47e-48b4-afdb-1081d9555a3f",
"avatar": "",
"username": "User two"
},
"to": {
"id": "52ead9fa-b498-400b-967d-b49886ab3118",
"avatar": "",
"username": "User one"
},
"order": {
"id": "8349e718-405a-4e88-8155-3896e9a89fdf",
"availabilities": [
{
"id": "9ddc07e0-be3d-453c-b12f-0ee31bfce444",
"timeFrom": {
"date": "2016-12-03 12:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
]
}
},
{
"id": "813fa986-f89b-411d-8324-727e427cbc00",
"isRead": false,
"created": {
"date": "2016-12-12 18:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"content": "Helllooooooo",
"contentType": "text",
"from": {
"id": "52ead9fa-b498-400b-967d-b49886ab3118",
"avatar": "",
"username": "User one"
},
"to": {
"id": "3e1806d6-f47e-48b4-afdb-1081d9555a3f",
"avatar": "",
"username": "User two"
},
"order": {
"id": "8349e718-405a-4e88-8155-3896e9a89fdf",
"availabilities": [
{
"id": "9ddc07e0-be3d-453c-b12f-0ee31bfce444",
"timeFrom": {
"date": "2016-12-03 12:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
]
}
and should be just like this:
{
"id": "813fa986-f89b-411d-8324-727e427cbc01",
"isRead": false,
"created": {
"date": "2016-12-12 18:01:00.000000",
"timezone_type": 3,
"timezone": "UTC"
},
"content": "Hello to you too",
"contentType": "text",
"from": {
"id": "3e1806d6-f47e-48b4-afdb-1081d9555a3f",
"avatar": "",
"username": "User two"
},
"to": {
"id": "52ead9fa-b498-400b-967d-b49886ab3118",
"avatar": "",
"username": "User one"
},
"order": {
"id": "8349e718-405a-4e88-8155-3896e9a89fdf",
"availabilities": [
{
"id": "9ddc07e0-be3d-453c-b12f-0ee31bfce444",
"timeFrom": {
"date": "2016-12-03 12:00:00.000000",
"timezone_type": 3,
"timezone": "UTC"
}
}
]
}
}