How to add new field on array item - mongodb

I have the following json structure:
{
"_id" : ObjectId("5203af83396d285ea2ecff8f"),
"brand" : "LG",
"comments" : [{
"user_id" : ObjectId("521b2785eda03d0f9cab3566"),
"text" : "Nice TV"
}],
"model" : "47LS5600",
"price" : 499.0,
"thumbnail" : "lg-47LS5600"
}
I need to insert a new field "datetime" in the array "comments" like this:
{
"_id" : ObjectId("5203af83396d285ea2ecff8f"),
"brand" : "LG",
"comments" : [{
"user_id" : ObjectId("521b2785eda03d0f9cab3566"),
"text" : "Nice TV",
"datetime": <value>
}],
"model" : "47LS5600",
"price" : 499.0,
"thumbnail" : "lg-47LS5600"
}
I tried with bellow instruction:
db.tvs.update({ _id: ObjectId("5203af83396d285ea2ecff8f") }, { $addToSet: { "comments.1": { "datetime": Date() } } } )
But it doesn´t inserts the field in the item, it creates another object separate from that item

Check out the link:
http://www.mongodb.org/display/DOCS/Updating#Updating-The%24positionaloperator
Modify Documents: http://docs.mongodb.org/manual/tutorial/modify-documents
Check Out this Code.
db.bios.update(
{ _id: 3 },
{ $set: {
mbranch: 'Navy',
'name.aka': 'Amazing Grace'
}
}
)

Related

How to upsert nested array object mongodb

I have a document which looks like this
{
_id:'asasasasa23sdsdsd',
source:'page',
url:[]
}
I need to upsert some values/objects to the url array. the objects that need to be upserted looks like this.
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
Then it would look like this
{
_id:'asasasasa23sdsdsd',
source:'page',
url:[{
"type" : "admin",
"value" : "hello admin",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
},
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
]
}
Here the id and the type fields are unique. I need to insert them if they do not exist or update them if they do.
This is the code that I have tried
db.collection(TABLE_NAME).update(
{ source: data.source },
{
source: data.source,
url: [data.urls]
},
{ upsert: true }
);
With this, it just replaces the array object with a new object. How to upsert instead of replacing the object?
I think this code will help.
let arr = [
{
"type" : "admin",
"value" : "hello admin",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
},
{
"type" : "blog",
"value" : "hello blog",
"id" : "1815f620-b45c-4230-85bb-7ba90ac330ed",
"datetime" : "2019-12-26 15:58:33"
}
];
db.users.update(
{ _id: doc._id },
{
$set: {
"url": arr
}
}
);

Should I choose mongo aggregation or should do it at application level service

I have the below collections.
Company:
{
"_id" : ObjectId("5a7848e8ca70273218e9d743"),
"name" : "Google",
"departments" : [
{
"name" : "IT",
"_id" : "1234567890"
},
{
"name" : "Sales",
"_id" : "1234567891"
}
]
}
User:
{
"_id" : ObjectId("5a784977ca70273218e9d759"),
"name" : "Sankarshan",
"company" : ObjectId("5a7848e8ca70273218e9d743"),
"department" : "1234567890"
}
topicCategories
{
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
topics
{
"_id" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicOne",
"order" : 1,
"label" : "oneTopic",
"color" : "red"
}
dimentions
{
"_id" : ObjectId("5a784fdcca70273218e9d869"),
"name" : "dimentionOne"
}
queries
{
"_id" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"order" : 1,
"label" : "queryLabelOne",
"statement" : "This is one question - Top1"
}
User_responses
{
"_id" : ObjectId("5a7859f5ca70273218e9da7d"),
"user" : ObjectId("5a784977ca70273218e9d759"),
"company" : ObjectId("5a7848e8ca70273218e9d743"),
"department" : "1234567890",
"response" : {
"question" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 5,
"color" : "red"
}
}
What is happening above
Each User belongs to a company.
Each Topic belongs to a Topic category.
Each Query belongs to a Topic
Each query has a dimention.
A user can answer a Query in terms of rating.
The user response to each question will be stored in User_responses.
What I want to achieve
An aggregated result of responses based on Topic category and Dimension
Expected aggregated result format
The query is based on one topic category.
All questions on each topic is aggregated based on Topic
Each Topic will have calculated average ratings based on dimension.
Response
{
"topicCategory": "topic category",
"topics": [
{
"topicLabel": "label of the topic one",
"dimentions": [
{
"name": "dimention name one",
"avgValue": 5.6
}
{
"name": "dimention name two",
"avgValue": 4.5
}
]
},
{
"topicLabel": "label of the topic two",
"dimentions": [
{
"name": "dimention name one",
"avgValue": 6.6
}
{
"name": "dimention name two",
"avgValue": 9.5
}
]
}
]
}
Can I implement this using Mongo aggregation or should I do it at service level?
What I have tried doing
db.getCollection('collResp').aggregate([
{
$match:
{
company: ObjectId("5a7848e8ca70273218e9d743"),
department: "1234567890"
}
},
{
$group: {
_id: {
topic: "$response.topic",
category: "$response.topicCategory"
},
responses: {
$push: "$response"
}
}
},
{
$lookup: {
from: 'topics',
localField: '_id.topic',
foreignField: '_id',
as: "topic"
}
},
{
$unwind: {
path: "$topic"
}
},
{
$lookup: {
from: 'topicCategories',
localField: '_id.category',
foreignField: '_id',
as: "category"
}
},
{
$unwind: {
path: "$category"
}
}
])
What I got
/* 1 */
{
"_id" : {
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"category" : ObjectId("5a784a10ca70273218e9d76f")
},
"responses" : [
{
"question" : ObjectId("5a7851a6ca70273218e9d8d9"),
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 8,
"color" : "red"
},
{
"question" : ObjectId("5a78580eca70273218e9da10"),
"topic" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a7851d5ca70273218e9d8ee"),
"rating" : 7,
"color" : "red"
}
],
"topic" : {
"_id" : ObjectId("5a78512cca70273218e9d8bd"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicTwo",
"order" : 2,
"label" : "twoTopic",
"color" : "green"
},
"category" : {
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
}
/* 2 */
{
"_id" : {
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"category" : ObjectId("5a784a10ca70273218e9d76f")
},
"responses" : [
{
"question" : ObjectId("5a78519aca70273218e9d8d7"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a784fdcca70273218e9d869"),
"rating" : 5,
"color" : "red"
},
{
"question" : ObjectId("5a785807ca70273218e9da0e"),
"topic" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"dimention" : ObjectId("5a7851d5ca70273218e9d8ee"),
"rating" : 3,
"color" : "red"
}
],
"topic" : {
"_id" : ObjectId("5a784cc3ca70273218e9d7d3"),
"topicCategory" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "TopicOne",
"order" : 1,
"label" : "oneTopic",
"color" : "red"
},
"category" : {
"_id" : ObjectId("5a784a10ca70273218e9d76f"),
"name" : "topicCtegoryOne",
"order" : 1
}
}
What more is required
Each response had a question. And each question has a dimention
I need to get average rating based on dimention.
Thank you.

Mongo aggregation, project a subfield of the first element in the array

I have a sub collection of elements and I want to project a certain subfield of the FIRST item in this collection. I have the following but it only projects the field for ALL elements in the array.
Items is the subcollection of Orders and each Item object has a Details sub object and an ItemName below that. I want to only return the item name of the FIRST item in the list. This returns the item name of every item in the list.
How can I tweak it?
db.getCollection('Orders').aggregate
([
{ $match : { "Instructions.1" : { $exists : true }}},
{ $project: {
_id:0,
'UserId': '$User.EntityId',
'ItemName': '$Items.Details.ItemName'
}
}
]);
Updated:
{
"_id" : "order-666156",
"State" : "ValidationFailed",
"LastUpdated" : {
"DateTime" : ISODate("2017-09-26T08:54:16.241Z"),
"Ticks" : NumberLong(636420128562417375)
},
"SourceOrderId" : "666156",
"User" : {
"EntityId" : NumberLong(34450),
"Name" : "Bill Baker",
"Country" : "United States",
"Region" : "North America",
"CountryISOCode" : "US",
},
"Region" : null,
"Currency" : null,
"Items" : [
{
"ClientOrderId" : "18740113",
"OrigClientOrderId" : "18740113",
"Quantity" : NumberDecimal("7487.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 1",
"ItemCost" : 1495.20
}
},
{
"ClientOrderId" : "18740116",
"OrigClientOrderId" : "18740116",
"Quantity" : NumberDecimal("241.0"),
"TransactDateTime" : {
"DateTime" : Date(-62135596800000),
"Ticks" : NumberLong(0)
},
"Text" : null,
"LocateRequired" : false,
"Details" : {
"ItemName" : "Test Item 2",
"ItemCost" : 2152.64
}
}
]
}
In case your are using at least MongoDB v3.2 you can use the $arrayElemAt operator for that. The below query does what you want. It will, however, not return any data for the sample you provided because the "Instructions.1": { $exists: true } filter removes the sample document.
db.getCollection('Orders').aggregate([{
$match: {
"Instructions.1": {
$exists: true
}
}
}, {
$project: {
"_id": 0,
"UserId": "$User.EntityId",
"ItemName": { $arrayElemAt: [ "$Items.Details.ItemName", 0 /* first item! */] }
}
}])

Mongodb aggregate match array item with child array item

I would like to find documents that contains specific values in a child array.
This is an example document:
{
"_id" : ObjectId("52e9658e2a13df5be22cf7dc"),
"desc" : "Something somethingson",
"imageurl" : "http://",
"tags" : [
{
"y" : 29.3,
"brand" : "52d2cecd0bd1bd844d000018",
"brandname" : "Zara",
"type" : "Bow Tie",
"x" : 20,
"color" : "52d50c19f8f8ca8448000001",
"number" : 0,
"season" : 0,
"cloth" : "52d50d57f8f8ca8448000006"
},
{
"y" : 29.3,
"brand" : "52d2cecd0bd1bd844d000018",
"brandname" : "Zara",
"type" : "Bow Tie",
"x" : 20,
"color" : "52d50c19f8f8ca8448000001",
"number" : 0,
"season" : 0,
"cloth" : "52d50d57f8f8ca8448000006"
}
],
"user_id" : "52e953942a13df5be22cf7af",
"username" : "Thompson",
"created" : 1386710259971,
"occasion" : "ID",
"sex" : 0
}
The query I would like to do should look something like this:
db.posts.aggregate([
{$match: {tags.color:"52d50c19f8f8ca8448000001", tags.brand:"52d2cecd0bd1bd844d000018", occasion: "ID"}},
{$sort:{"created":-1}},
{$skip:0},
{$limit:10}
])
my problem is that I dont know how to match anything inside an array in the document like "tags". How can I do this?
You could try to do it without aggregation framework:
db.posts.find(
{
occasion: "ID",
tags: { $elemMatch: { color:"52d50c19f8f8ca8448000001", brand:"52d2cecd0bd1bd844d000018" } }
}
).sort({created: -1}).limit(10)
And if you want to use aggregation:
db.posts.aggregate([
{$match:
{
tags: { $elemMatch: { color:"52d50c19f8f8ca8448000001", brand: "52d2cecd0bd1bd844d000018" } },
occasion: "ID"
}
},
{$sort:{"created":-1}},
{$limit:10}
])

Group by specific element of array with mongo aggregation framework

Is it possible to use the aggregation framework to group by a specific element of an array?
Such that with documents like this:
{
name: 'Russell',
favourite_foods: [
{ name: 'Pizza', type: 'Four Cheeses' },
{ name: 'Burger', type: 'Veggie'}
],
height: 6
}
I could get a distinct list of top favourite foods (ie. foods at index 0) along with the height of the tallest person who's top favourite food that is?
Something like this (although it doesn't work as the array index access dot notation doesn't seem to work in the aggregation framework):
db.people.aggregate([
{ $group : { _id: "$favourite_foods.0.name", max_height: { $max : "$height" } } }
])
Seems like you are relying on the favorite food for each person being first in the array. If so, there is an aggregation framework operator you can take advantage of.
Here is the pipeline you can use:
db.people.aggregate(
[
{
"$unwind" : "$favourite_foods"
},
{
"$group" : {
"_id" : {
"name" : "$name",
"height" : "$height"
},
"faveFood" : {
"$first" : "$favourite_foods"
}
}
},
{
"$group" : {
"_id" : "$faveFood.name",
"height" : {
"$max" : "$_id.height"
}
}
}
])
On this sample dataset:
> db.people.find().pretty()
{
"_id" : ObjectId("508894efd4197aa2b9490741"),
"name" : "Russell",
"favourite_foods" : [
{
"name" : "Pizza",
"type" : "Four Cheeses"
},
{
"name" : "Burger",
"type" : "Veggie"
}
],
"height" : 6
}
{
"_id" : ObjectId("5088950bd4197aa2b9490742"),
"name" : "Lucy",
"favourite_foods" : [
{
"name" : "Pasta",
"type" : "Four Cheeses"
},
{
"name" : "Burger",
"type" : "Veggie"
}
],
"height" : 5.5
}
{
"_id" : ObjectId("5088951dd4197aa2b9490743"),
"name" : "Landy",
"favourite_foods" : [
{
"name" : "Pizza",
"type" : "Four Cheeses"
},
{
"name" : "Pizza",
"type" : "Veggie"
}
],
"height" : 5
}
{
"_id" : ObjectId("50889541d4197aa2b9490744"),
"name" : "Augie",
"favourite_foods" : [
{
"name" : "Sushi",
"type" : "Four Cheeses"
},
{
"name" : "Pizza",
"type" : "Veggie"
}
],
"height" : 6.2
}
You get these results:
{
"result" : [
{
"_id" : "Pasta",
"height" : 5.5
},
{
"_id" : "Pizza",
"height" : 6
},
{
"_id" : "Sushi",
"height" : 6.2
}
],
"ok" : 1
}
Looks like it isn't currently possible to extract a specific element from an array in aggregation:
https://jira.mongodb.org/browse/SERVER-4589
JUST add more information about the result after using "$wind":
DOCUMENT :
> db.people.find().pretty()
{
"_id" : ObjectId("508894efd4197aa2b9490741"),
"name" : "Russell",
"favourite_foods" : [
{
"name" : "Pizza",
"type" : "Four Cheeses"
},
{
"name" : "Burger",
"type" : "Veggie"
}
],
"height" : 6
},
...
AGGREAGATION :
db.people.aggregate([{
$unwind: "$favourite_foods"
}]);
RESULT :
{
"_id" : ObjectId("508894efd4197aa2b9490741"),
"name" : "Russell",
"favourite_foods" :{
"name" : "Pizza",
"type" : "Four Cheeses"
},
"height" : 6
},
{
"_id" : ObjectId("508894efd4197aa2b9490741"),
"name" : "Russell",
"favourite_foods" : {
"name" : "Burger",
"type" : "Veggie"
},
"height" : 6
}
In Addition:
If there are more than two array fields in one collection record,
we can use "$project" stage to specify the array field.
db.people.aggregate([
{
$project:{
"favourite_foods": 1
}
},
{
$unwind: "$favourite_foods"
}
]);
I think you can make use of the $project and $unwind operators (let me know if this isn't what you're trying to accomplish):
> db.people.aggregate(
{$unwind: "$favourite_foods"},
{$project: {food : "$favourite_foods", height: 1}},
{$group : { _id: "$food", max_height: { $max : "$height" } } })
{
"result" : [
{
"_id" : {
"name" : "Burger",
"type" : "Veggie"
},
"max_height" : 6
},
{
"_id" : {
"name" : "Pizza",
"type" : "Four Cheeses"
},
"max_height" : 6
}
],
"ok" : 1
}
http://docs.mongodb.org/manual/applications/aggregation/
Since mongoDB version 3.2 You can simply use $arrayElemAt and $max:
db.collection.aggregate([
{
$set: {favourite_foods: {$arrayElemAt: ["$favourite_foods", 0]}}
},
{
$group: {
_id: "$favourite_foods.name",
maxHeight: {$max: "$height"}
}
}
])
Playground example