I'm trying to select only some fields in document while also using $elemmatch. It turned out the field selection is getting ignored.
Given a document like this:
{
"author": "Robin Sharma",
"data": [
{
"translation": "en",
"content": "foo",
"unwanted_content": "huyu1",
},
{
"translation": "id",
"content": "bar",
"unwanted_content": "huyu2",
{
],
}
I'm using find() with this projection config:
const projection = {
author: 1,
data: {
$elemMatch: {
translation: "en"
},
},
}
Expected
{
"author": "Robin Sharma",
"data": [
{
"translation": "en",
"content": "foo"
},
],
}
Actual
{
"author": "Robin Sharma",
"data": [
{
"translation": "en",
"content": "foo",
"unwanted_content": "huyu1",
},
],
}
I'm still not sure how to get rid of the "unwanted_content" field.
You can achieve your required result using aggregate as:
db.collection.aggregate([
{
$unwind: "$data"
},
{
$match: {
"data.translation": "en"
}
},
{
$group: {
_id: "$author",
data: {
$push: {
translation: "$data.translation",
content: "$data.content"
}
}
}
}
])
I think you can do something like this to get only selected fields from subdocument.
db.collection.find({"data.translation": "en"},{author: 1, "data.content": 1, "data.translation": 1})
Related
im trying to update a value for uid in solts.slots but nothing works.
i want to iterate two level of array to modify the document
{
"_id": {
"$oid": "638455dee12f0122c9812697"
},
"bid": "637b0fdd3d9a96eb913805d3",
"name": "sucess",
"slots": [
{
"date": "2022-11-28T00:00:00.000Z",
"slots": [
{
"uid": null,
"available": true,
"status": null,
"_id": {
"$oid": "638455dee12f0122c98126a0"
}
},
{
"uid": null,
"available": true,
"status": null,
"_id": {
"$oid": "638455dee12f0122c98126a1"
}
}
],
"_id": {
"$oid": "638455dee12f0122c9812698"
}
}
]}]}
im trying to update the slots for id 638455dee12f0122c98126a0 properties like
uid:'234235'
available:false
status:'booked'
can anyone help me to query this
i tried
const result = await Event.findOne({
'slots.slots': {
$elemMatch: {
_id: req.body.id
}
}
})
is there is any way to query this type of documents.
You can use $[] and $[<identifier>]:
db.collection.update({
"slots.slots": {
$elemMatch: {
_id: ObjectId("638455dee12f0122c98126a0")
}
}
},
{
$set: {
"slots.$[].slots.$[item].available": false,
"slots.$[].slots.$[item].uid": "234235",
"slots.$[].slots.$[item].status": "booked"
}
},
{
arrayFilters: [
{
"item._id": ObjectId("638455dee12f0122c98126a0")
}
]
})
See how it works on the playground example
I am struggling to find some examples of using the mongo aggregation framework to process documents which has an array of items where each item also has an array of other obejects (array containing an array)
In the example document below what I would really like is an example that sums the itemValue in the results array of all cases in the document and accross the collection where the result.decision was 'accepted'and group by the document locationCode
However, even an example that found all documents where the result.decision was 'accepted' to show or that summmed the itemValue for the same would help
Many thanks
{
"_id": "333212",
"data": {
"locationCode": "UK-555-5566",
"mode": "retail",
"caseHandler": "A N Other",
"cases": [{
"caseId": "CSE525666",
"items": [{
"id": "333212-CSE525666-1",
"type": "hardware",
"subType": "print cartridge",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "acme print cartridge",
"quantity": 2,
"weight": "1.5"
},
"result": {
"decision": "rejected",
"decisionDate": "2019-02-02"
},
"isPriority": true
},
{
"id": "333212-CSE525666-2",
"type": "Stationery",
"subType": "other",
"targetDate": "2020-06-15",
"itemDetail": {
"description": "staples box",
"quantity": 3,
"weight": "1.66"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-03",
"itemValue": "23.01"
},
"isPriority": true
}
]
},
{
"caseId": "CSE885655",
"items": [{
"id": "333212-CSE885655-1",
"type": "marine goods",
"subType": "fish food",
"targetDate": "2020-06-04",
"itemDetail": {
"description": "fish bait",
"quantity": 5,
"weight": "0.65"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-03-02"
},
"isPriority": false
},
{
"id": "333212-CSE885655-4",
"type": "tobacco products",
"subType": "cigarettes",
"deadlineDate": "2020-06-15",
"itemDetail": {
"description": "rolling tobbaco",
"quantity": 42,
"weight": "2.25"
},
"result": {
"decision": "accepted",
"decisionDate": "2020-02-02",
"itemValue": "48.15"
},
"isPriority": true
}
]
}
]
},
"state": "open"
}
You're probably looking for $unwind. It takes an array within a document and creates a separate document for each array member.
{ foos: [1, 2] } -> { foos: 1 }, { foos: 2}
With that you can create a flat document structure and match & group as normal.
db.collection.aggregate([
{
$unwind: "$data.cases"
},
{
$unwind: "$data.cases.items"
},
{
$match: {
"data.cases.items.result.decision": "accepted"
}
},
{
$group: {
_id: "$data.locationCode",
value: {
$sum: {
$toDecimal: "$data.cases.items.result.itemValue"
}
}
}
},
{
$project: {
_id: 0,
locationCode: "$_id",
value: "$value"
}
}
])
https://mongoplayground.net/p/Xr2WfFyPZS3
Alternative solution...
We group by data.locationCode and sum all items with this condition:
cases[*].items[*].result.decision" == "accepted"
db.collection.aggregate([
{
$group: {
_id: "$data.locationCode",
itemValue: {
$sum: {
$reduce: {
input: "$data.cases",
initialValue: 0,
in: {
$sum: {
$concatArrays: [
[ "$$value" ],
{
$map: {
input: {
$filter: {
input: "$$this.items",
as: "f",
cond: {
$eq: [ "$$f.result.decision", "accepted" ]
}
}
},
as: "item",
in: {
$toDouble: {
$ifNull: [ "$$item.result.itemValue", 0 ]
}
}
}
}
]
}
}
}
}
}
}
}
])
MongoPlayground
I'm trying to make a query to mongodb. I want to get an array containing [location, status] of every document.
This is how my collection looks like
{
"_id": 1,
"status": "OPEN",
"location": "Costa Rica",
"type": "virtual store"
},
{
"_id": 2,
"status": "CLOSED",
"location": "El Salvador"
"type": "virtual store"
},
{
"_id": 3,
"status": "OPEN",
"location": "Mexico",
"type": "physical store"
},
{
"_id": 4,
"status": "CLOSED",
"location": "Nicaragua",
"type": "physical store"
}
I made a query, using the aggregate framework, trying to get all documents that match that specific type of store.
{
{'$match': {
'type': { '$eq': "physical store"}
}
}
What I want is something like this:
{
{
'stores': [
["Mexico", "OPEN"],
["Nicaragua", "CLOSED"]
]
},
}
I tried with the $push but couldn't make it.
Could someone please guide me on how to do it.
Since { $push: ["$location", "$status"] } would give you the error The $push accumulator is a unary operator. You would have to work around it a bit by passing to it a single object that output your desired array. One way to do it would be:
[
{
"$match": {
"type": {
"$eq": "physical store"
}
}
},
{
"$group": {
"_id": null,
"stores": {
"$push": {
"$slice": [["$location", "$status"], 2]
}
}
}
}
]
If the given documents are not sub-documents, then below is the approach:
db.collection.find({
type: {
$eq: "physical store"
}
},
{
location: 1,
status: 1
})
MongoPlayGround link for the above
If, they are the part of a field (means they are sub-documents), then below is the approach:
db.collection.aggregate([
{
$project: {
stores: {
$filter: {
input: "$stores",
as: "store",
cond: {
$eq: [
"$$store.type",
"physical store"
]
}
}
}
}
},
{
$unwind: "$stores"
},
{
$project: {
location: "$stores.location",
status: "$stores.status",
_id: "$stores._id"
}
}
])
MongoPlayGround link for the above
let's say I have docs such as
{
"nickname": "my nickname",
"comments": [
{
"id": 1
},
{
"id": 1
}
]
}
how do I update it to look like
{
"nickname": "my nickname",
"comments": [
{
"id": 1,
"nickname": "my nickname"
},
{
"id": 1,
"nickname": "my nickname"
}
]
}
This does not seem to be working
db.getCollection('users').update(
{
"comments.nickname": null
},
{ "$set": { "comments.$.nickname": "$nickname" } });
This is just an example to represent my problem.
I would not like to hear about re-structuring and optimizing the fields.
Thanks!
Try this (v4.2):
db.users.updateMany(
{"comments.nickname":null},
[
{"$set": {"comments.nickname": "$nickname"}}
]
)
Note: It will override if any comments.nickname already exists
db.users.aggregate([
{
$match: {
"comments.nickname": null
}
},
{
$addFields: {
comments: {
$map: {
input: "$comments",
in: {
id: "$$this.id",
nickname: {
$cond: [
"$$this.nickname",
"$$this.nickname",
"$nickname"
]
}
}
}
}
}
},
{
$out: "users"
}
])
Note: It will keep already existing values
I have a MongoDB document that looks something like this:
{
"_id": 000000000001,
"sample": [
{
"_id": {
"$oid": "12345678910"
},
"Phone": 5555555555,
"LastName": "Musk",
"FirstName": "Elon",
"responses": {
"question1": "hello",
"question2": "world"
}
},
{
"_id": {
"$oid": "12345678911"
},
"Phone": "1111111111",
"LastName": "Jobs",
"FirstName": "Steve",
"responses": {
"question1": "goodbye",
"question2": "world"
}
}
]
}
I want to get a $sum of all the people who have given the response hello to question1 and all the people who have given the response world to question2.
For example. I want a response that looks something like this:
{ 'question1': 1, 'question2': 2 }
My query looks like this, but it isn't working. It's only returning 0 for any given sum when I know that's not the case.
db.collection(collection).aggregate([
{ $match : { } },
{ $group: {
_id: "responses",
"question1": { $sum: { $cond: [ { $eq: [ "$sample.responses.question1", "hello"] }, 1, 0 ] } },
"question2": { $sum: { $cond: [ { $eq: [ "$sample.responses.question2", "world"] }, 1, 0 ] } }
}
}
])
Can anyone help me out?