From the database:
_id: "123123123123123"
question: "Question1"
answer: "some answer"
by: "user1"
__v: 0
There is a conditional I am trying to implement here. It does not work. If by is empty, then do not display _id: "$question",
let answers = await Answer.aggregate([
{ $match: { $or: [{ by: user.email }, { by: user2[0].email }] } },
{
$cond: {
if: {
$eq: ["$by", ""],
},
then: 0,
else: 1,
},
},
{
$group: {
_id: "$question",
data: {
$push: "$$ROOT",
},
},
}
]);
Outcome (wrong):
{
"_id": "Question1",
"data": [
{
"answer": "some answer",
"by": "user1"
},
{
"answer": "some answer",
"by": "user2"
}
]
},
{
"_id": "Question2",
"data": [
{
"answer": "some answer",
"by": "user1",
}
]
},
Expected outcome:
I want to display only Question1 because user2 does not have an answer (there is no user2 by). So display Question only if there are 2 by.
{
"_id": "Question1",
"data": [
{
"answer": "some answer",
"by": "user1"
},
{
"answer": "some answer",
"by": "user2"
}
]
},
You have to remove $orcondition and use $all in
{ $match: { $or: [{ by: user.email }, { by: user2[0].email }] } },
changed to
{ $match: { $all: [{ by: user.email }, { by: user2.email }] } },
Related
I am building an api for a kanban task management app. I have this data stored in the 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 tried to return a single object with the id of req.params.id which in this case is 62ff74bfe80b11ade2d34455 however, it returns an empty array instead of returning a single object.
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",
},
},
]);
});
As #yung-shun suggested, I needed to cast req.params.id as an Object ID.
const task = await Board.aggregate([
{
$match: {
"columns.tasks._id": new ObjectId(req.params.id),
},
},
{ $unwind: "$columns" },
{
$match: {
"columns.tasks._id": new ObjectId(req.params.id),
},
},
{
$project: {
task: {
$first: {
$filter: {
input: "$columns.tasks",
cond: { $eq: ["$$this._id", new ObjectId(req.params.id)] },
},
},
},
},
},
{ $replaceWith: "$task" },
]);
res.status(200).json(task);
I have two collections Posts an comments. I am storing comments with postID. I want to show comments count field when fetching all the posts data.
How do I achieve this?
// posts
{
postID: '123',
title: 'abc'
}
// comments
{
postID: '123',
comments: [
{
commentID: 'comment123',
comment: 'my Comment'
}
]
}
// Looking for this
{
postID: '123',
title: 'abc',
commentCount: 1
}
Here's one way you could do it.
db.posts.aggregate([
{
"$lookup": {
"from": "comments",
"localField": "postID",
"foreignField": "postID",
"pipeline": [
{
"$project": {
"_id": 0,
"commentCount": {"$size": "$comments"}
}
}
],
"as": "commentCount"
}
},
{
"$project": {
"_id": 0,
"postID": 1,
"title": 1,
"commentCount": {"$first": "$commentCount.commentCount"}
}
}
])
Try it on mongoplayground.net.
Try This.
pipeline = [{
"$lookup": {
"from": "comments",
"let": {
"postId": "$postId",
},
"pipeline": [
{
"$match": {
"$expr": {
"$eq": ["$postId", "$$postId"]
},
}
},
{
"$group": {
"_id": "$postId",
"comments_count": {"$sum": 1}
}
}
],
"as": "comments"
}
},
{
"$project": {
"_id": 0,
"postId": 1,
"title":1,
"comments_count": "$comments.comments_count"
}
}]
db.posts.aggregate(pipeline)
I've been struggling to get my around how to update a object in a nested array with a particular id. I've attempted to implement $set as shown below. I want to be able to update the task with an _id of 62ff74bfe80b11ade2d34455 with the data from the request body.
{
"_id": "62fa5aa25778ec97bc6ee231",
"user": "62f0eb5ebebd0f236abcaf9d",
"name": "Marketing Plan",
"columns": [
{
"name": "todo",
"_id": "62fa5aa25778ec97bc6ee233",
"tasks": [
{ ====> here
"title": "Task Four",
"description": "This is task four",
"subtasks": [
{
"name": "wash dshes",
"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
}
const updatedTask = await Board.findOneAndUpdate(
{
"columns.tasks._id": req.params.id,
},
{ $set: { "columns.$.tasks": req.body } },
{ new: true }
);
You can use the positional operator in combination with an arrayfilter. Here's an example how you'd update a specific field of the relevant task:
db.collection.update({
"columns.tasks._id": req.params.id
},
{
"$set": {
"columns.$[].tasks.$[t].title": "it works"
}
},
{
"arrayFilters": [
{
"t._id": req.params.id
}
]
})
You can also try this on mongoplayground.
If you're looking for a way to replace the matching task object itself you can do:
db.collection.update({
"columns.tasks._id": req.params.id
},
{
"$set": {
"columns.$[].tasks.$[t]": req.body
}
},
{
"arrayFilters": [
{
"t._id": req.params.id
}
]
})
Im getting random data from my database using the aggregate but apparently some of them have their genre values in string not in array, How can i convert those random data genre from string to array?
const book = await Book.aggregate([
/* This is just for testing which books has genres in specific type
{
$match: { genre: { $type: "string" } }
},
*/
{
$sample: { size: 6 }
}
]);
sample data result(genres that are in type of string)
[
{
"_id": "62710ac63ad1bfc6d17030fe",
"title": "Birth of a Theorem",
"author": "Villani, Cedric",
"genre": "mathematics",
"publisher": "Bodley Head",
"dateOfPublication": "2002-02-28T00:00:00.000Z",
"noOfCopies": 16,
"type": "Book",
"form": "Non-fiction",
"isbn": "979-81202-479229-867673-6",
"dateAdded": "2002-11-28T00:00:00.000Z"
},
{
"_id": "62710ac63ad1bfc6d1703108",
"title": "All the President's Men",
"author": "Woodward, Bob",
"genre": "history",
"publisher": "Random House",
"dateOfPublication": "2018-02-19T00:00:00.000Z",
"noOfCopies": 56,
"type": "Book",
"form": "Non-fiction",
"isbn": "978-41428-6606587-937631-",
"dateAdded": "2011-02-23T00:00:00.000Z"
},
]
sample data result(genres that are in type of array)
[
{
"_id": "62710ac63ad1bfc6d17030be",
"title": "Superfreakonomics",
"author": "Dubner, Stephen",
"genre": [
"economics",
"computer_science"
],
"publisher": "HarperCollins",
"dateOfPublication": "2003-10-06T00:00:00.000Z",
"noOfCopies": 31,
"type": "Thesis",
"form": "Non-fiction",
"isbn": "978-35029-7186192-465859-7",
"dateAdded": "2009-02-12T00:00:00.000Z"
}
]
$sample is for documents instead of embedded array.
Update
db.collection.update({
genre: {
$type: "string"
}
},
[
{
$set: { genre: [ "$genre" ] }
}
],
{
multi: true
})
mongoplayground
Aggregate
db.collection.aggregate([
{
$match: {
genre: {
$type: "string"
}
}
},
{
$set: {
genre: {
$cond: {
if: { $eq: [ { $type: "$genre" }, "string" ] },
then: [ "$genre" ],
else: "$genre"
}
}
}
}
])
mongoplayground
I have a set of documents (posts) which have an array of users mentioned in each post.
{
"title": "Some post title",
[ ... ]
"mentions": ["johnsmith", "johndoe", "paul"]
}
I want to aggregate a list of unique mentions, and the number of times they've been mentioned across all posts. For example:
[{ user: "johnsmith", count: 5 }, { user: "benlewis", count: 9 }, { user: "johndoe", count: 1 }]
With Mongo, I'd do something like:
"mentions": [{
"$unwind": "$mentions"
}, {
"$group": {
"_id": "$mentions",
"count": { "$sum": 1 }
}
}]
What's the equivalent in Elasticsearch?
You can use a Terms aggregation for that. A small (5.x) example:
PUT test
{
"mappings": {
"test" : {
"properties": {
"title": {
"type": "text"
},
"mentions": {
"type": "keyword"
}
}
}
}
}
POST test/test/1
{
"title": "Some post title",
"mentions": [
"johnsmith",
"johndoe",
"paul"
]
}
POST test/test/2
{
"title": "Some post title 2",
"mentions": [
"johnsmith"
]
}
GET test/_search
{
"size": 0,
"aggs": {
"test": {
"terms": {
"field": "mentions",
"size": 10
}
}
}
}
Gives the following response:
"aggregations": {
"test": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "johnsmith",
"doc_count": 2
},
{
"key": "johndoe",
"doc_count": 1
},
{
"key": "paul",
"doc_count": 1
}
]
}
}
}
Hope this helps :)