MongoDB find by Id in nested array [duplicate] - node.js

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})

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.

I will like to return the previous object and the next object of a matching object id in node

Please i am new to node js and MongoDB.
When i want to retrieve a post by id, i want to be able to retrieve the previous post and next post also.
this is my post, it only retrieves the current post by id.
Post.findById(req.params.postId)
.then((existingpost) => {
console.log(Post.find(req.params.postId))
if (existingpost) {
res.send(existingpost);
}
return res.status(404).send({
message: "Post does not exist with id " + req.params.postId,
});
})
.catch((err) => {
if (err.kind === "ObjectId") {
return res.status(404).send({
message: "Post does not exist with id " + req.params.postId,
});
}
return res.status(500).send({
message:
"Some error occurred while retrieving the post with postId " +
req.params.postId,
});
});
};
I currently receive the object with the id like this which is fine.
{
"_id": "6009f3e294d8a033402a76e7",
"title": "Covid 19 in Italy",
"author": "John Doe",
"createdAt": "2021-01-21T21:36:34.514Z",
"updatedAt": "2021-01-21T21:36:34.514Z",
"__v": 0
}
But i will love to receive the object of the current id, the previous object and the next object.
something like this.
[{
"_id": "3230g5e382d8a033402a76e7",
"title": "Effect of Covid on the Economy",
"author": "John Doe",
"createdAt": "2021-01-21T21:36:34.514Z",
"updatedAt": "2021-01-21T21:36:34.514Z",
"__v": 0
},
{
"_id": "6009f3e294d8a033402a76e7",
"title": "Covid 19 in Italy",
"author": "John Doe",
"createdAt": "2021-01-21T21:36:34.514Z",
"updatedAt": "2021-01-21T21:36:34.514Z",
"__v": 0
},
{
"_id": "4567hye294d8a033402a76e7",
"title": "Life after Covid",
"author": "John Doe",
"createdAt": "2021-01-21T21:36:34.514Z",
"updatedAt": "2021-01-21T21:36:34.514Z",
"__v": 0
}]
Since its a UUID, this approach might help you..
$sort to sort the documents by asc
$group and $unwind to get the index
$facet to categorize the incoming data into current and allDocs
We know current is only one object, so we do $unwind to deconstruct the array
We already know the index, so we use $filter to get prev, current and next using index
$unwind to deconstruct the array
$replaceRoot to make the objects to the root
Here is the script
db.collection.aggregate([
$sort: { createdAt: 1 } },
{
$group: {
_id: null,
data: { $push: "$$ROOT"}
}
},
{ $unwind: { path: "$data", includeArrayIndex: "index" } },
{
$facet: {
current: [
{ $match: { "data._id": "3230g5e382d8a033402a76e7" } }
],
allDocs: [
{ $match: {} }
]
}
},
{
$unwind: "$current"
},
{
$project: {
docs: {
$filter: {
input: "$allDocs",
cond: {
$or: [
{ $eq: [ "$$this.index", { $subtract: [ "$current.index", 1 ] } ] },
{ $eq: [ "$$this.index", "$current.index" ] },
{ $eq: [ "$$this.index", { $add: [ "$current.index", 1 ] } ] }
]
}
}
}
}
},
{ "$unwind": "$docs" },
{ "$replaceRoot": { "newRoot": "$docs.data" } }
])
Working Mongo playground
There are many ways to do this, this is one of the way. If you feel you have a lot of document, then try to avoid $unwind which is expensive, in that case you can try using createdDate instead of index
I am not sure is there any straight way to do this, you can try aggregation,
Using UUID and CreatedAt:
$facet to get all documents in all after sorting in ascending order by createdAt
$let to define vars states with start and total documents,
$cond check condition if index of input uuid is zero then return start: 0 and total: 2 documents we have to slice from all array, else get current index and subtract minus 1 and total: 3
in to return slice documents on the base of start and total
Post.aggregate([
{ $facet: { all: [{ $sort: { createdAt: 1 } }] } },
{
$project: {
result: {
$let: {
vars: {
states: {
$cond: [
{ $eq: [{ $indexOfArray: ["$all._id", req.params.postId] }, 0] },
{ start: 0, total: 2 },
{
start: {
$subtract: [{ $indexOfArray: ["$all._id", req.params.postId] }, 1]
},
total: 3
}
]
}
},
in: { $slice: ["$all", "$$states.start", "$$states.total"] }
}
}
}
}
])
Playground
Using ObjectID:
convert your string input id req.params.postId to object id using mongoose.Types.ObjectId
$facet to separate result,
first, $match to get current and next documents, $sort _id in descending order, $limit 2
second, $match to get previous document, $sort _id in descending order, $limit 1
$project to get result after concat both array first and second using $concatArrays
req.params.postId = mongoose.Types.ObjectId(req.params.postId);
Post.aggregate([
{
$facet: {
first: [
{ $match: { _id: { $gte: req.params.postId } } },
{ $sort: { _id: 1 } },
{ $limit: 2 }
],
second: [
{ $match: { _id: { $lt: req.params.postId } } },
{ $sort: { _id: -1 } },
{ $limit: 1 }
]
}
},
{ $project: { result: { $concatArrays: ["$first", "$second"] } } }
])
Playground

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")
}
]
}
]

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

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"
]
}

Sub group Mongoose

I'm trying to get a sub gruop from a query using nodejs and mongoose.
The thing I'm trying to do is the following:
I have this collection:
I Need to count and group all the documents with the same 'intent' and make a subgroup with the 'entity' value, so far I have this running:
try {
//We first get the total interactions from all workspace
let workspace = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: "$workspace", data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec();
//We then get the total results from conversations
let results = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: '$intent', data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec()
//workspaceItems = workspace.map(function (Interaction) { return Interaction._id; });
return res.json({
total: workspace,
result: results
})
} catch (err) {
console.log(err);
return res.status(500).send(err)
}
The result look like this:
{
"total": [
{
"_id": "Business",
"data": 23
}
],
"result": [
{
"_id": "N/A",
"data": 2
},
{
"_id": "PRODUCTO_BENEFICIOS",
"data": 3
},
{
"_id": "PRODUCTO_DESCRIPCION",
"data": 10
},
{
"_id": "REPORTE_TARJETA_PERDIDA",
"data": 1
},
{
"_id": "REQUISITOS",
"data": 7
}
]
}
I need the result in this way :
{
"total": [
{
"_id": "Business",
"data": 23
}
],
"result": [
{
"_id": "N/A",
"data": 2
},
{
"_id": "PRODUCTO_BENEFICIOS",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 3
},
{
"_id": "PRODUCTO_DESCRIPCION",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 10
},
{
"_id": "REPORTE_TARJETA_PERDIDA",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 1
},
{
"_id": "REQUISITOS",
"entities: [{"TARJETAS","PROMOCIONES"."ETC..."}],
"data": 7
}
]
}
I Hope to be clear, please let me know if you know how to do this using mongoose.
Thank you in advance.
try changing the 2nd query to following
let results = await Interaction.aggregate([
{ $match: { dateAdded: { $gte: todayStart, $lt: todayEnd }, workspace: workspaceID } },
{ $group: { _id: '$intent', entities: {$push: "$entity"}, data: { $sum: 1 } } },
{ $sort: { _id: 1 } }
]).exec()
if you want a unique list of entities you can use $addToSet instead of $push

Resources