Mongoose - get single field from array of subdocuments [duplicate] - node.js

This question already has answers here:
Getting a list of unique embedded/nested objects in a MongoDB document
(4 answers)
Closed 5 years ago.
I have a document, Model like this:
{
name: String,
user: String,
components: [{
alias: String,
name: String,
...etc...
}]
}
I'd like to formulate a reponse that just returns an array of component.alias for a given document.
E.g, if I have:
{
name: "doc1",
components: [{alias: "alias1", ...}, {alias: "alias2", ...}]
}
I'd like to just end up with ["alias1", "alias2"]
So far I have:
Model.findById(
modelId,
{ "components.alias": 1, _id: 0 },
(err, docs) => {
console.log(docs)
}
);
But this gives me:
{
"components": [
{
"alias": "alias1"
}
]
}
Any ideas?

Use aggregate for that, MongoDB query for that.
db.getCollection('Model').aggregate([
{ "$unwind": "$components" },
{ "$group": { "_id": "$components.alias" }}
]).map(function(el) { return el._id })
Node.js code
Model.aggregate([
{ "$match": { "name": "doc1" } }, // any condition
{ "$unwind": "$components" },
{ "$group": { "_id": "$name", components: { $push: "$components.alias" } } }
]).then((data)=>console.log(data))
Output:
{
"_id": "doc1",
"emails": [
"alias1",
"alias2"
]
}

Related

How to get particular details from nested object from MongoDB

I'm saving data for a NestJs based web app in MongoDB.
My MongoDB Data looks like this
"gameId": "1a2b3c4d5e"
"rounds": [
{
"matches": [
{
"match_id": "1111abc1111",
"team1": {
"team_id": "team8",
"score": 0
},
"team2": {
"team_id": "team2",
"score": 0
}
},
{
"match_id": "2222abc2222",
"team1": {
"team_id": "team6",
"score": 0
},
"team2": {
"team_id": "team5",
"score": 0
}
},
]
}
]
Here we have gameId for each game and inside each game, there are many rounds and many matches. Each match has match_id. How can I get a particular match info and edit it based on gameId & match_id?
(N.B: I'm willing to update score based on match_id)
I've tried something like this
const matchDetails = await this.gameModel.findOne({
gameId: gameId,
rounds: { $elemMatch: { match_id: match_id } },
});
But this doesn't work and returns null. How to do this correctly?
The problem is that you're applying the elemMatch on the rounds array, but it should be on rounds.matches. Changing your query to the following will fix the problem:
const matchDetails = await this.gameModel.findOne({
gameId: gameId,
"rounds.matches": { $elemMatch: { match_id: match_id } },
});
EDIT:
To only get a specific matching element, you can use a simple aggregation with $unwind and $filter:
db.collection.aggregate([
{
"$match": {
"gameId": gameId,
"rounds.matches": { $elemMatch: { match_id: match_id } }
}
},
{
"$unwind": "$rounds"
},
{
$project: {
match: {
$filter: {
input: "$rounds.matches",
as: "match",
cond: {
$eq: [
"$$match.match_id",
match_id
]
}
}
},
_id: 0
}
}
])
Example on mongoplayground.

MongoDB (Mongoose) - Get only records where nested item is equal to a value

I searched a lot, tried several ways, but nothing works.
I have this in mongo:
{
id: ObjectId("1234567890"),
answers: [{
id: 111,
deleted:0
},
{
id: 222,
deleted:0
},
{
id: 333,
deleted:1
}]
},
{
id: ObjectId("0987654321"),
answers: [{
id: 111,
deleted:0
},
{
id: 222,
deleted:1
},
{
id: 333,
deleted:1
}]
}
I want the document with ObjectId("1234567890"), and only the answers with delete = 1 ( only the id 333).
I have tryied this:
var query = chatbotMongoModel.find(
{ _id: "1234567890" },
{ answers: {$elemMatch: {deleted: "1"}}}
)
but returns all the answers.. Could you give me some help plase?
thanks!
Rafael
One way of doing this is using mongodb aggregation framework.
var result = await chatbotMongoModel.aggregate([
{
$match: {
id: "1234567890"
}
},
{
"$unwind": {
path: "$answers"
}
},
{
$match: {
"answers.deleted": 1
}
},
{
$group: {
_id: "$id",
answers: {
$push: "$answers"
},
allFields: {
$first: "$$ROOT"
}
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$allFields",
{
"answers": "$answers"
}
]
}
}
}
])
Note that in your sample documents you have id but you use _id in your query, they must match. Also deleted data type is number in sample documents, but you use string value in your query.
Running mongoplayground.
Another alternative solution:
var result = await chatbotMongoModel.aggregate([
{
$match: {
id: "1234567890"
}
},
{
$addFields: {
"answers": {
$filter: {
input: "$answers",
as: "answer",
cond: {
$eq: [
"$$answer.deleted",
1
]
}
}
}
}
}
])
mongoplayground
Thanks for the answers! I found a solution, and im posting here if someone needs this too...
var query = model.aggregate([
{ $match: { '_id': mongoose.Types.ObjectId("1234567890") } },
{
$project: {
answer: {
$filter: {
input: "$answer",
as: "f",
cond: { $eq: ["$$f.deleted", 1] }
}
}
}
}
])
regards!

MongoDB find by Id in nested array [duplicate]

This question already has answers here:
Find in Double Nested Array MongoDB
(2 answers)
Closed 3 years ago.
My model called Residence
{
"_id": { "$oid": "5d88dfe45feb4c06a5cfb762" },
"spaces": [{
"_id": { "$oid": "5d88dfe45feb4c06a5cfb76f" },
"name": "Building 2",
"subSpace": [
{
"_id": { "$oid": "5d88dfe45feb4c06a5cfb771" },
"name": "Basement"
},
{
"_id": { "$oid": "5d88dfe45feb4c06a5cfb770" },
"name": "Floors" // Get only the name by Id
}
]
}
}
To find a spaceName by Id (OK)
exports.getSpaceNameById = (spaceId) => {
return Residence.find({ 'spaces._id': spaceId }, { _id: 0, spaces: { $elemMatch: { _id: spaceId } } })
}
Now I want to have the subSpace name requested by Id.
But my dream would be to have both (query by subSpace Id, for this example : 5d88dfe45feb4c06a5cfb770) : spaceName / subSpaceName only with 1 request.
Thanks for your help.
UPDATE 1
I try this method but the response is an empty array
exports.getSubSpaceNameById = (spaceId) => {
return Residence.aggregate([
{ $match: {'spaces.subSpace._id': spaceId}},
{ $project: {
'spaces.subSpace': { $filter: {
input: '$spaces.subSpace',
as: 'mySubSpace',
cond: { $eq: ['$$mySubSpace._id', spaceId]}
}},
_id: 0
}}
])
}
The following should work for you. This query would return the whole document.
By name:
db.collection.find({"spaces.subSpace.name": "Basement"})
By _id: db.collection.find({"spaces.subSpace._id": "YOUR_ID"})
Try this query
db.testers.aggregate([
{
$addFields:{
"spaces":{
$map:{
"input":"$spaces",
"as":"doc",
"in":{
$mergeObjects:[
"$$doc",
{
"subSpace":{
$filter:{
"input":"$$doc.subSpace",
"as":"sn",
"cond": {
"$and": [
{ "$eq": [ "$$sn._id", "5d88dfe45feb4c06a5cfb770" ] },
]
}
}
}
}
]
}
}
}
}
}
]).pretty()
Try something like this:
find({spaces._id:id})

The field must be an accumulator object in mongo [duplicate]

This question already has answers here:
MongoDB SELECT COUNT GROUP BY
(9 answers)
Closed 3 years ago.
I am performing an aggregation query.My requirement is that I have docs in following format:
{
"name":"a","age":20},
{
"name":"b","age":23},
{
"name":"a","age":26}
I need to sum the ages of all names who have "a" in their name field.
I am doing the following but it gives me an error that "The field 'name' must be an accumulator object":
db.collection('students').aggregate({
$group: {"name": "a", "sum": {$sum: '$age'}}
}, {
$project: {sum: '$sum'}
}, (e, d) => {
if (!e) {
var e = d.stream({transform: JSON.stringify});
console.log("answer")
console.log(e);
deferred.resolve(e);
} else {
console.log(e)
}
})
I altered your aggregation query.Try as below:
db.collection('students').aggregate(
[
{
$match: {
name: "a"
}
},
{
$group: {_id: "$name",
"sum": {
$sum: '$age'
}
}
},
{
$project: {
sum: 1
}
], (e, d) => {
if (!e) {
var e = d.stream({transform: JSON.stringify});
console.log("answer")
console.log(e);
deferred.resolve(e);
} else {
console.log(e)
}
})
Here, you are missing match filter properly. We should use $match filter to find out specific results and "_id" for attribute on which we are performing operation in $group.
Structure of your query would be -
db.students.aggregate(
[
{
$match: {
"name": "a"
}
},
{
$group: {
"_id": "$name",
totalTte: {
$sum: "$age"
}
}
},
{
$project: {
sum: 1
}
}
])

Return only the matched objects from document [duplicate]

This question already has answers here:
Retrieve only the queried element in an object array in MongoDB collection
(18 answers)
Closed 4 years ago.
I am building Node-Js application using Mongoose my question is: Is there a way to return the matched Objects from document instead of the entire object to be more specific I want to return the company_report that contains a date < at 2018-06-10
here is my the example with my code:
[
{
companyName: "example",
"history": [
{
"company_report_result": [
{
"attribut": 1111,
}
],
"date": ISODate("2018-06-06T08:11:00.000Z")
},
{
"company_report_result": [
{
"attribut": 22222,
}
],
"date": ISODate("2018-06-12T08:11:00.000Z")
},
{
"company_report_result": [
{
"attribut": 3333,
}
],
"date": ISODate("2018-06-07T08:11:00.000Z")
}
]
}
]
query:
Campaign.find({ 'table.history.date': { $gt: new Date('2018-06-10') } })
You need to use $filter aggregation operator which gives only the matched element from the array and escapes the other elements
db.collection.aggregate([
{
$match: {
"history.date": {
$gte: new Date('2018-06-10')
}
}
},
{
$project: {
companyName: 1,
history: {
$filter: {
input: "$history",
as: "hist",
cond: {
$gte: [
"$$hist.date",
new Date('2018-06-10')
]
}
}
}
}
}
])
Above query will return
[
{
companyName: "example",
"history": [
{
"company_report_result": [
{
"attribut": 22222,
}
],
"date": ISODate("2018-06-12T08:11:00.000Z")
}
]
}
]

Resources