How to return single object (mongoose/mongoDB) - javascript

I have this data stored in database.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v":0
}
I want to be able to return a single object with the id equal to the req.params.id, in this case that would be 62ff74bfe80b11ade2d34455.
{
"title": "Task Four testing 2",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes test",
"completed": false,
"_id": "62ff74bfe80b11ade2d34456"
},
{
"name": "do homework",
"completed": false,
"_id": "62ff74bfe80b11ade2d34457"
}
],
"_id": "62ff74bfe80b11ade2d34455"
}
I researched stackoverflow and came across this potential solution: Mongoose retrieve one document from nested array which implemented the aggregate framework. But when I test this in postman, the request isn't made.
const getTask = asyncHandler(async (req, res) => {
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id,
},
},
{
$project: {
columns: {
$first: {
$filter: {
input: "$columns.tasks",
cond: {
$eq: ["$$this._id", req.params.id],
},
},
},
},
},
},
{
$replaceRoot: {
newRoot: "$columns",
},
},
]);
});

Having an array inside an array complicates the query a bit, but here's one way to retrieve the data you want.
db.Board.aggregate([
{
$match: {
"columns.tasks._id": req.params.id
}
},
{"$unwind": "$columns"},
{
$match: {
"columns.tasks._id": req.params.id
}
},
{
"$project": {
"task": {
"$first": {
"$filter": {
"input": "$columns.tasks",
"cond": {"$eq": ["$$this._id", req.params.id]}
}
}
}
}
},
{"$replaceWith": "$task"}
])
Try it on mongoplayground.net. [The mongoplayground.net example uses "62ff74bfe80b11ade2d34455" rather than req.params.id.]

Related

How to remove deeply nested object (Node.js, mongoose)

I'm making a kanban task management app and I'm trying to remove a task with the _id: req.params.id which has the value of 62fa5ae05778ec97bc6ee23a. I tried the following:
const task = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{ $pull: { "columns.$.tasks.$._id": req.params.id } },
{ new: true }
);
But I get the error Too many positional (i.e. '$') elements found in path'columns.$.tasks.$._id'
I searched for a while and came across arrayFilters from the docs but I'm struggling a lot to understand how to implement it for this particular need.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{
"title": "Task Four",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23b"
},
{
"name": "do homework",
"completed": false,
"_id": "62fa5ae05778ec97bc6ee23c"
}
],
"_id": "62fa5ae05778ec97bc6ee23a"
}
]
},
{
"name": "doing",
"_id": "62fa5aa25778ec97bc6ee234",
"tasks": []
},
{
"name": "done",
"_id": "62fa5aa25778ec97bc6ee235",
"tasks": []
}
],
"__v": 0
}
You need to use $[] positional operator in order to pull from the nested array. Try running this query:
db.Board.updateOne({
"_id" : "62fa5aa25778ec97bc6ee231",
}, {
$pull: { 'columns.$[].tasks': { '_id': '62fa5ae05778ec97bc6ee23a' } }
});

join object from another documnt with localfield key

i have a competition doc with field teams array of object with _id of team and a score doc with teamId field
competitions.teams = [{_id: 100,..}, {..}]
score.teamId = 100
when aggregatig score i want to group it to the competition teams but imm getting all team inside the group innstead of matching id
sample document https://mongoplayground.net/p/yJ34IBnnuf5
db.scores.aggregate([
{
"$match": {
"type": "league"
}
},
{
"$lookup": {
"from": "competitions",
"localField": "competitionId",
"foreignField": "_id",
"as": "comp"
}
},
{
"$unwind": {
"path": "$comp",
"preserveNullAndEmptyArrays": true
}
},
{
"$project": {
"comp.teams": 1,
"teamId": 1
}
},
{
"$group": {
"_id": "$teamId",
"results": {
"$push": "$comp.teams"
}
}
}
])
returns all team in group instead of matched teamid
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 100,
"name": "team 1"
},
{
"_id": 101,
"name": "team 2"
}
]
}
this is the result im trying to accomplish please guide me
{
"_id" : 100
"results" : [
{
"_id": 100,
"name": "team 1"
}
]
}
{
"_id" 101
"results" : [
{
"_id": 101,
"name": "team 2"
}
]
}
what should i do i've read the docs this seems to be the way?
Demo - https://mongoplayground.net/p/ETeroLftcZZ
You have to add $unwind: { "path": "$comp.teams" }
and after that group by { $group: { "_id": "$comp.teams._id" ... }
db.scores.aggregate([
{ $match: { "type": "league" } },
{ $lookup: { "from": "competitions", "localField": "competitionId", "foreignField": "_id", "as": "comp" } },
{ $unwind: { "path": "$comp", "preserveNullAndEmptyArrays": true } },
{ $unwind: { "path": "$comp.teams", "preserveNullAndEmptyArrays": true }},
{ $group: { "_id": "$comp.teams._id", "results": { $push: "$comp.teams" } } }
])
Demo with more data - https://mongoplayground.net/p/b41Ch5ge2Wp

Mongodb $graphLookup aggregation inconsistent ouput order and sorting

I have this aggregation operation, and it's giving me the correct output, but with an inconsistent order. When I reload, the nested output array (posteriorThread) changes the order of the documents, and there seems to be no rhyme or reason!
I'm confused why the order keeps changing, and I would like to know why it's happening, but I figured I would just sort it, which I did, but I'm having trouble grouping it back together.
I'll show you both of my broken solutions below, but essentially I want output 1 but with the correct order. I'm using mongoose, but that shouldn't make a difference.
Thanks.
1: Inconsistent order solution
const posteriorThread = await Comment.aggregate([
{
$match: {
_id: post.threadDescendant,
},
},
{
$graphLookup: {
from: 'comments',
startWith:'$threadDescendant',
connectFromField: 'threadDescendant',
connectToField: '_id',
as: 'posteriorThread',
},
},
]);
OUTPUT: 1
posteriorThread [
{
"_id": "000",
"name": "foo bar",
"text": "testing one",
"threadDescendant": "123",
"posteriorThread": [
{
"_id": "234",
"name": "foo bar",
"text": "testing four",
"threadDescendant": "345"
},
{
"_id": "345",
"name": "foo bar",
"text": "testing three",
},
{
"_id": "123",
"name": "foo bar",
"text": "testing two",
"threadDescendant": "234"
},
]
}
]
2: Correct older but lose root document
const posteriorThread = await Comment.aggregate([
{
$match: {
_id: post.threadDescendant,
},
},
{
$graphLookup: {
from: 'comments',
startWith: '$threadDescendant',
connectFromField: 'threadDescendant',
connectToField: '_id',
as: 'posteriorThread',
},
},
{
$unwind: '$posteriorThread',
},
{
$sort: { 'posteriorThread.depth': 1 },
},
{
$group: { _id: '$_id', posteriorThread: { $push: '$posteriorThread' } },
},
]);
OUTPUT 2:
posteriorThread [
{
"_id": "000",
"posteriorThread": [
{
"_id": "123",
"name": "foo bar",
"text": "testing two",
"threadDescendant": "234"
},
{
"_id": "234",
"name": "foo bar",
"text": "testing four",
"threadDescendant": "345"
},
{
"_id": "345",
"name": "foo bar",
"text": "testing three",
},
]
}
]
The $graphLookup pipeline stage doesn't offer any built-in sorting capability, thus your second approach is correct. You just need to use $first in order to preserve root object's fields. You can use $replaceRoot and special $$ROOT variable to avoid specifying each field explicitly:
{
$group: {
_id: "$_id",
posteriorThread: { $push: "$posteriorThread" },
root: { $first: "$$ROOT" }
}
},
{
$project: {
"root.posteriorThread": 0
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
{ posteriorThread: "$posteriorThread" },
"$root"
]
}
}
}
Mongo Playground

Wrong Result by $lookup mongodb

I am using $lookup to get the data by joining data from two or three collections, Below is my aggregate query.
let condition = {status:{$ne:config.PROJECT_STATUS.completed}, assignId:mongoose.Types.ObjectId(req.params.id)};
Project.aggregate([
{
"$match": condition
},
{
"$group": { "_id": "$_id" }
},
{
"$lookup": {
"from": "worksheets",
"let": { "projectId": "$_id" },
"pipeline": [
{
"$match": { "$expr": { "$eq": ["$projectId", "$$projectId"] } }
},
{
"$group": { "_id": "$projectId", "totalHours": { "$sum": "$hours" } }
},
{
"$lookup": {
"from": "projects",
"let": { "projectId": "$_id" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$projectId"] } } },
{
"$lookup": {
"from": "users",
"let": { "developers": "$developers" },
"pipeline": [
{ "$match": { "$expr": { "$in": ["$_id", "$$developers"] } } },
{ "$project":{"firstName":1,"lastName":1}}
],
"as": "developers"
}
},
{
"$lookup": {
"from": "billing_accounts",
"let": { "upworkId": "$upworkId" },
"pipeline": [
{ "$match": { "$expr": { "$eq": ["$_id", "$$upworkId"] } } },
{"$project":{"name":1,"username":1}}
],
"as": "upworkId"
}
},
{
"$project": {
"projectName": 1, "upworkId": 1, "status": 1, "developers": 1, "hoursApproved": 1
}
}
],
"as": "project"
}}
],
"as": "projects"
}
}
])
And it is giving me the below result:
[
{
"_id": "5c188a9959f6cf1258f4cb01",
"projects": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"totalHours": 8,
"project": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910aa",
"lastName": "kumar",
"firstName": "Amit"
}
],
"projectName": "Jims fitness",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
},
{
"_id": "5c17a253c1a7681f7c54bb2f",
"projects": []
}
]
But what i want to get is:
[
{
"_id": "5c188a9959f6cf1258f4cb01",
"projects": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"totalHours": 0,
"project": [
{
"_id": "5c188a9959f6cf1258f4cb01",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910aa",
"lastName": "kumar",
"firstName": "Amit"
}
],
"projectName": "Project1",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
},
{
"_id": "5c17a253c1a7681f7c54bb2f",
"projects": [
{
"_id": "5c17a253c1a7681f7c54bb2f",
"totalHours": 0,
"project": [
{
"_id": "5c17a253c1a7681f7c54bb2f",
"hoursApproved": 192,
"developers": [
{
"_id": "5c0a29e597e71a0d28b910a9",
"lastName": "kumar",
"firstName": "Rajat"
}
],
"projectName": "project2",
"status": "ongoing",
"upworkId": [
{
"_id": "5c17a1cec1a7681f7c54bb2d",
"name": "Heena Ln",
"username": "heena_ln"
}
]
}
]
}
]
}
]
As you can see that now i have totalHours equals to 0 instead of empty array and have the project details.
Actually I have four collections: projects, worksheets, users and billings and i am executing aggregate query on the projects collection to get the projects of a project manager and for this i am also joining worksheets collection to get the data for how many hours the employees worked on this project, because worksheets collection contains the projectId, userId and hours.
Query: You can see in the result that, i am getting the empty array of projects, this is because i don't have any record of second project projectId into the worksheet collection, so for this it is giving me empty array, but i want to get the projects details as it is and totalHours equals to 0.

Elasticsearch v6.0.1 Nodejs boost match with one of arrays elements

In my project I have user objects like this.
{
"_id": "1",
"username": "RAggro",
"name": "Vardan Tadevosyan"
},
{
"_id": "2",
"username": "XACHIK",
"name": "XACHIK"
},
{
"_id": "3",
"username": "vardar",
"name": "Vardan Gukoyan"
},
{
"_id": "4",
"username": "Gordey",
"name": "Gordey Gordeev"
},
{
"_id": "5",
"username": "id220107973",
"name": "Vardan Ayvazyan"
},
{
"_id": "6",
"username": "vvardanyan4",
"name": "Vardan Vardanyan"
},
{
"_id": "7",
"username": "svardan",
"name": "Vardan Sargsyan"
}
And I have list of _id-s, like [51,3,9,11,6, 2].
I whant to query users by 'name' and 'username', orderid like first comes users that contains in ids array then others
query: {
multi_match: {
query: "vardan",
fields: ["name", "username"],
operator: "or"
},
boosting: {
positive: {
term: {
_id: [51,3,9,11,6, 2]
}
},
positive_boost: 2.0
}
}
So the expected result is:
{
"_id": "3",
"username": "vardar",
"name": "Vardan Gukoyan"
},
{
"_id": "6",
"username": "vvardanyan4",
"name": "Vardan Vardanyan"
},
{
"_id": "1",
"username": "RAggro",
"name": "Vardan Tadevosyan"
},
{
"_id": "5",
"username": "id220107973",
"name": "Vardan Ayvazyan"
},
{
"_id": "7",
"username": "svardan",
"name": "Vardan Sargsyan"
}
But I'm fetching empty array,
Please, help how can I modify my query to reach expected ordered result.
You can do it easily using a bool/should clause that will boost the documents whose IDs are within the specified group:
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "vardan",
"fields": [
"name",
"username"
],
"operator": "or"
}
}
],
"should": [
{
"ids": {
"values": ["51","3","9","11","6","2"]
}
}
]
}
}
}
It works using this query:
{
query:{
"bool": {
"must": [{
"multi_match": {
"query": "vardan",
"fields": [
"name",
"username"
],
"operator": "or"
}
}],
"should": [{
"terms": {
"_id": ["51","3","9","11","6","2"],
"boost": 100
}
}]
}
}
}

Categories

Resources