For Examples,
{
"_id":ObjectId("6245131fdbcda3639d75c951"),
"username": "joy"
"data" : [
{
"_id" : ObjectId("6245131fdbcda3639d75c953"),
"value_1" : "hello1"
"value_2" : "thank you1"
},
{
"_id" : ObjectId("6245131fdbcda3639d75c954"),
"value_1" : "hello2"
"value_2" : "thank you2"
},
]
}
I want to edit one of data object and delete old one and push edited one
because i want to the edited object will be last index of array.
i want to get this result.
{
"_id":ObjectId("6245131fdbcda3639d75c951"),
"username": "joy"
"data" : [
{
"_id" : ObjectId("6245131fdbcda3639d75c954"),
"value_1" : "hello2"
"value_2" : "thank you2"
},
{
"_id" : ObjectId("6245131fdbcda3639d75c953"),
"value_1" : "change data from hello1"
"value_2" : "change data from thank you1"
},
]
}
I tried it but i got error.
db.getCollection('profile').update(
{username:"joy"},
{
{
"$pull": {"data":
{"_id": ObjectId("6245131fdbcda3639d75c953")}
},
},
{
"$push": {"data":
{
"_id" : ObjectId("6245131fdbcda3639d75c953"),
"value_1" : "change data from hello1"
"value_2" : "change data from thank you1"
}
},
}
})
How can i get the value?
thank you. :)
MongoDB will not allow multiple operations/operators in a single field, you can use update with aggregation pipeline starting from 4.2,
$filter to iterate loop of data array and filter the documents that are not equal to your input _id,
$concatArrays to concat filtered data array and new object that you want to add in the last index of data array
db.getCollection('profile').update(
{ username: "joy" },
[{
$set: {
data: {
$concatArrays: [
{
$filter: {
input: "$data",
cond: {
$ne: ["$$this._id", "6245131fdbcda3639d75c953"]
}
}
},
[
{
_id: "6245131fdbcda3639d75c953",
value_1: "change data from hello1",
value_2: "change data from thank you1"
}
]
]
}
}
}]
)
Playground
Related
I have a bunch of docs that look like below:
{
"_id" : ObjectId("8f30b453c2ece001364dc04d"),
"SessionId" : "awkuTQjj53kgqAZ4J",
"StartDate" : ISODate("2020-02-24T11:51:36.918+0000"),
"EndDate" : ISODate("2020-02-24T11:51:36.918+0000"),
"List1" : "X",
"List2" : "Y",
"rating" : [
{
"ObjectId" : "5d09e98380c5d5eb89ac5069",
"List" : "List 2",
"Rate" : NumberInt(5),
"RatedDate" : ISODate("2020-02-24T11:55:47.774+0000")
},
{
"ObjectId" : "5d09e98380c5d5eb89ac5069",
"List" : "List 2",
"Rate" : NumberInt(4),
"RatedDate" : ISODate("2020-02-24T11:55:48.408+0000")
},
{
"ObjectId" : "5d09e98380c5d5eb89ac505b",
"List" : "List 2",
"Rate" : NumberInt(3),
"RatedDate" : ISODate("2020-02-24T11:55:49.520+0000")
},
{
"ObjectId" : "5d09e98380c5d5eb89ac505c",
"List" : "List 2",
"Rate" : NumberInt(3),
"RatedDate" : ISODate("2020-02-24T11:55:51.787+0000")
},
{
"ObjectId" : "5d09e98380c5d5eb89ac5057",
"List" : "List 1",
"Rate" : NumberInt(4),
"RatedDate" : ISODate("2020-02-24T11:55:53.865+0000")
},
{
"ObjectId" : "5d09e98380c5d5eb89ac5058",
"List" : "List 1",
"Rate" : NumberInt(4),
"RatedDate" : ISODate("2020-02-24T11:55:53.865+0000")
},
],
"Answers" : {
"SelectedList" : "1",
},
}
I need to sum up all the rating.Rate where rating.List:'List 1' and respectively sum up all rating.Rate where rating.List:'List 2', also exclude duplicate records (by rating.ObjectId) and count only the ones with latest rating.RatedDate. I suppose this is a group aggregation.
Also they should match the criteria
List1:'X' ,
Answers.selectedList:1
What I have written looks like below so far:
[
{
"$match" : {
"List1" : "X",
"Answers.SelectedList" : "1"
}
},
{
"$unwind" : {
"path" : "$rating"
}
},
{
"$group" : {
"_id" : null,
"sum" : {
"$sum" : "$Rate"
}
}
}
]
can you please help me?
I was a little confused around the List1/List2 however I think this will get you most of the way to your required aggregation query.
db.test.aggregate([
{
$match: {
"List1": "X",
"Answers.SelectedList": "1"
}
},
{
"$unwind" : "$rating"
},
{
$group:{
_id: {
id: "$rating.ObjectId",
list: "$rating.List"
},
maxRatedDate: { $max: "$rating.RatedDate" },
ratings: { $push: "$rating" }
}
},{
$addFields: {
ratings: {
$filter: {
input: "$ratings",
as: "item",
cond: { $eq: [ "$$item.RatedDate", "$maxRatedDate" ] }
}
}
}
},
{
$unwind: "$ratings"
},
{
$group:{
_id: "$ratings.List",
sum : {
$sum : "$ratings.Rate"
}
}
}
])
This will output the following
{ "_id" : "List 1", "sum" : 8 }
{ "_id" : "List 2", "sum" : 10 }
However, let's try to break it down.
To start with we've got a simple match, the same as yours in your question. this just limits the number of documents we pass back
$match: {
"List1": "X",
"Answers.SelectedList": "1"
}
Then we unwind all the array items so we get a document for each rating, this allows us to do some extra querying on the data.
{
"$unwind" : "$rating"
}
Next, we've got a group by, here we're a group on the ObjectId of the rating so we can later remove duplicates, we're also finding out in the group which rating we've group has the highest date so we can take that one later in a projection. we're then pushing all the rating back in the array for later.
$group:{
_id: {
id: "$rating.ObjectId",
list: "$rating.List"
},
maxRatedDate: { $max: "$rating.RatedDate" },
ratings: { $push: "$rating" }
}
Next we want to project the ratings array in to a single element in which it only contains the latest rating, for this we use a $filter on the array and filter them all out that don't match our max date we calculated in our previous step.
$addFields: {
ratings: {
$filter: {
input: "$ratings",
as: "item",
cond: { $eq: [ "$$item.RatedDate", "$maxRatedDate" ] }
}
}
}
The next two steps are fairly simple and are just unwinding the array again (we've only got one element, then grouping them to get the total sum for the lists.
{
$unwind: "$ratings"
},
{
$group:{
_id: "$ratings.List",
sum : {
$sum : "$ratings.Rate"
}
}
}
At this point you only need to provide the $group stage with the field that you're actually grouping on as the _id field and reference the fields properly as they are still inside of the rating array:
"$group" : {
"_id" : "$rating.List",
"sum" : {
"$sum" : "$rating.Rate"
}
}
I'm working on a project where I'm trying to return a document, but exclude some child fields based on status. For example if the status is disabled then I don't want that child returned. But all the other records returned if they don't contain disabled.
The request includes the _id of the document that I want to find and return, without the 'disabled' child records.
How do I select the document by _id then, exclude records from the child array based on a value.
Thanks
My document look like this:
{
"_id" : ObjectId("5e7bb266071f9601b6ad8f4e"),
"name" : "Test Document",
"postcode" : "90210",
"colors" : [
{
"_id" : ObjectId("5e7d276a05674f0cf49bdcec"),
"color" : "blue",
"status": "active"
},
{
"_id" : ObjectId("5e7d276a05674f0cf49bdceg"),
"color" : "red",
"status": "active"
},
{
"_id" : ObjectId("5e7d276a05674f0cf49bdceh"),
"color" : "green",
"status" : "disabled"
}
]
}
How do I return:
{
"_id" : ObjectId("5e7bb266071f9601b6ad8f4e"),
"name" : "Test Document",
"postcode" : "90210",
"colors" : [
{
"_id" : ObjectId("5e7d276a05674f0cf49bdcec"),
"color" : "blue",
"status": "active"
},
{
"_id" : ObjectId("5e7d276a05674f0cf49bdceg"),
"color" : "red",
"status": "active"
}
]
}
I have been trying variations of:
findr.aggregate([
{
$match: {
$and: [{
_id: mongodb.ObjectId(_id)
}, {
'color.status': 'active'
}]
}
},
{
$project: {
_id
name: 1,
postcode: 1,
colors: {
$filter: {
input: '$colors',
as: 'color',
cond: {
$eq: ['$$color.status', 'active']
}
}
}
}
}
])
Here is the code for filteration.
db.collection.aggregate([
{
$match: {
"_id": ObjectId("5e7bb266071f9601b6ad8f4e")
}
},
{
$project: {
items: {
$filter: {
input: "$colors",
as: "item",
cond: {
$eq: [
"$$item.status",
"active"
]
}
}
}
}
}
])
Playground
I'm having a MongoDB collection which looks same as below document and I wanted to find only one field values count which presents within the embedded array object.
I tried below query to fetch the data but doesn't work
db.mycollection.find({'quizzes':{skill:'html'}}).pretty()
below is the mongo document structure with sample value. the structure is the same as my original document
{
"user": "values",
"date": "234-234-234-234",
"quizzes":[
{
"skill": "html",
"score": "12"
}
]
}
From the above document, I wanted to fetch only the skill field values which present within quizzes array which is an embedded document.
my output should be like
{
"html": 10,
"php": 20,
"C#": 15,
"java": 18,
.
.
.
.
.
}
You can use the distinct() method. The following query can get us the expected output:
db.mycollection.distinct("quizzes.skill");
For more information, please check https://docs.mongodb.com/manual/reference/method/db.collection.distinct/
Edit I: Calculating count of skills too
db.collection.aggregate([
{
$unwind:"$quizzes"
},
{
$group:{
"_id":"$quizzes.skill",
"k":{
$first:"$quizzes.skill"
},
"v":{
$sum:1
}
}
},
{
$project:{
"_id":0
}
},
{
$group:{
"_id":null,
"data":{
$push:"$$ROOT"
}
}
},
{
$project:{
"data":{
$arrayToObject:"$data"
}
}
},
{
$replaceRoot:{
"newRoot":"$data"
}
}
]).pretty()
Data set:
{
"_id" : ObjectId("5d66ad357d0ab652c42315f7"),
"user" : "values",
"date" : "234-234-234-234",
"quizzes" : [
{
"skill" : "html",
"score" : "12"
},
{
"skill" : "css",
"score" : "10"
}
]
}
{
"_id" : ObjectId("5d66ad357d0ab652c42315f8"),
"user" : "values2",
"date" : "234-234-234-234",
"quizzes" : [
{
"skill" : "Java",
"score" : "12"
},
{
"skill" : "html",
"score" : "10"
}
]
}
Output:
{ "Java" : 1, "css" : 1, "html" : 2 }
Explanation: We are creating a key and value pair (k,v) where 'k' is the skill and 'v' is the count of skill occurrence. The reason behind taking field names as 'k' and 'v' is because $arrayToObject only takes these fields only. Later on, all keys and values are merged to prepare the final document.
You can use aggregates to get the desired results.
db.myCollection.aggregate({ $group: { "skill": "$quizzes.skill","count":{ "$sum":1 }} })
I have the following document of collection "user" than contains two nested arrays:
{
"person" : {
"personId" : 78,
"firstName" : "Mario",
"surname1" : "LOPEZ",
"surname2" : "SEGOVIA"
},
"accounts" : [
{
"accountId" : 42,
"accountRegisterDate" : "2018-01-04",
"banks" : [
{
"bankId" : 1,
"name" : "Bank LTD",
},
{
"bankId" : 2,
"name" : "Bank 2 Corp",
}
]
},
{
"accountId" : 43,
"accountRegisterDate" : "2018-01-04",
"banks" : [
{
"bankId" : 3,
"name" : "Another Bank",
},
{
"bankId" : 4,
"name" : "BCT bank",
}
]
}
]
}
I'm trying to get a query that will find this document and get only this subdocument at output:
{
"bankId" : 3,
"name" : "Another Bank",
}
I'm getting really stucked. If I run this query:
{ "accounts.banks.bankId": "3" }
Gets the whole document. And I've trying combinations of projection with no success:
{"accounts.banks.0.$": 1} //returns two elements of array "banks"
{"accounts.banks.0": 1} //empty bank array
Maybe that's not the way to query for this and I'm going in bad direction.
Can you please help me?
You can try following solution:
db.user.aggregate([
{ $unwind: "$accounts" },
{ $match: { "accounts.banks.bankId": 3 } },
{
$project: {
items: {
$filter: {
input: "$accounts.banks",
as: "bank",
cond: { $eq: [ "$$bank.bankId", 3 ] }
}
}
}
},
{
$replaceRoot : {
newRoot: { $arrayElemAt: [ "$items", 0 ] }
}
}
])
To be able to filter accounts by bankId you need to $unwind them. Then you can match accounts to the one having bankId equal to 3. Since banks is another nested array, you can filter it using $filter operator. This will give you one element nested in items array. To get rid of the nesting you can use $replaceRoot with $arrayElemAt.
I am using below aggregate query to get the list of restaurant matches keyword search "chinese" within the list of ids passed,
db.business.aggregate([
{
$match:{
$text:{
$search:"chinese"
}
}
},
{
$match:{
"_id":{
$in:[
ObjectId("571453a82ece1392240f7b91"),
ObjectId("5714537b2ece1392240f7b8c"),
ObjectId("5714539a2ece1392240f7b8e"),
ObjectId("571453962ece1392240f7b8d")
]
}
}
},
])
Below is the sample data in mongodb.
{
"_id" : ObjectId("571453b32ece1392240f7b93"),
"_class" : "com.halal.sa.data.entities.Business",
"name" : "Chillies",
"description" : "nice restaurant",
"cuisine" : [
"veg-nonveg",
"chinese",
"kabab"
],
"address" : {
"streetAddress" : "1000 bentley road",
"city" : "marietta",
"pincode" : 30067,
"landmark" : "near delk road",
"location" : {
"type" : "Point",
"coordinates" : [
-84.4774305,
33.9202151
]
}
},
"phone" : 123,
"email" : "my#email.com",
"ownerEmail" : "test#email",
"status" : "2",
"website" : "test.com",
"authenticity" : "1"
}
please let me know the exact modified aggregate query which will only return list of _ids instead of returning all the documents from the collection. Thanks in advance
The way I see to have really only an array of ids (like you do with distinct in find), would be by using $group
db.business.aggregate([
{
$match:{
$text:{
$search:"chinese"
}
}
},
{
$match:{
"_id": {
$in: [
ObjectId("571453a82ece1392240f7b91"),
ObjectId("5714537b2ece1392240f7b8c"),
ObjectId("5714539a2ece1392240f7b8e"),
ObjectId("571453962ece1392240f7b8d")
]
}
}
},
{ $group: { _id: null, ids: { $push: "$$ROOT._id" } } }
]);
The result would give you basically something like
[
{
_id: null,
ids: [/* Your ids */],
}
]
I got it just used the $project in the query, thats it :-)
db.business.aggregate([
{ $match: { $text: { $search: "chinese" } } },
{ $match: { "_id": {$in : [ObjectId("571453a82ece1392240f7b91"), ObjectId("5714537b2ece1392240f7b8c"),ObjectId("5714539a2ece1392240f7b8e"),ObjectId("571453962ece1392240f7b8d")]} } },
{$project: {"_id": "$_id"}}
])