0

I have a mongoDB database set up with a express server.

I try to access a object which is inside an array in a document.

I have this route :

app.get("/api/" + BLOGS_COLLECTION + "/:blogId" + "/post" + "/:postId", function (req, res) {
db.collection(BLOGS_COLLECTION).aggregate([
    { $match: { _id: req.params.postId } }, {
        $project: {
            posts: {
                $filter: {
                    input: "$posts",
                    as: "posts",
                    cond: { $eq: ["$$posts._id", req.params.postId] }
                }
            }
        }
    }].toArray((err, doc) => {
        console.log(doc)
        if (err) {
            handleError(res, err.message, "Failed to get post");
        } else {
            res.status(200).send(doc);
        }
    }));
});

But it returns an error :

[{(intermediate value)},{(intermediate value)}].toArray is not a function

If I do not use the toArray but a plain function, it can't construct to json to send it to the front.

I do not use Mongoose.

If the code above can't work, how can I query only the object I want inside the array ?

P.S: this is how my database is made :

_id:60336bcc961785586471938b
title:"<p>ttttttttttttttttttttttttttt</p>"
description:"<p>tttttttttttttttttttttttttt</p>"
authorId:"60336bb5961785586471938a"
createDate:2021-02-22T08:31:08.889+00:00
posts:
  0:
   _id:"53394732-d60b-c869-1fed-1fb82c03780f"
   title:"<p>iiiiiiiiiiiiiiiiiiiiiiiiiiiii</p>"
   content:"<p>iiiiiiiiiiiiiiiiiiii</p>"
   date:"2021-02-22T08:31:14.911Z"
Specialka
  • 51
  • 1
  • 10

2 Answers2

0

You need to call toArray on the cursor reference you get back from the aggregation and not on the aggregation array:

db.collection(BLOGS_COLLECTION).aggregate([
  { $match: { _id: req.params.postId } },
  {
    $project: {
      posts: {
        $filter: {
          input: "$posts",
          as: "posts",
          cond: { $eq: ["$$posts._id", req.params.postId] }
        }
      }
    }
  }
],
(err, cursor) => {
  if (err) {
    handleError(res, err.message, "Failed to get post");
  } else {
    cursor.toArray((error, documents) => {
      if (error) { return handleError(res, error.message, "Failed to get post"); }
      console.log(documents)
      res.status(200).send(documents);
    });
  }
});

MongoDB Aggregation Pipeline

ethane
  • 2,329
  • 3
  • 22
  • 33
  • Thank you for you input ! So now, I get an empty array, so I think my query is wrong somehow. What should I change in it to make it work ? I added the structure of the database in the question. – Specialka Feb 22 '21 at 09:01
  • 1
    I believe your 1st stage $match is wrong, it should be: `$match: { _id: req.params.blogId }` (currently you are filtering with `postId`) – Boris Kukec Feb 22 '21 at 09:07
  • I did try to change it to blogId, but it still returns an empty array. – Specialka Feb 22 '21 at 09:10
  • I changed it to _id:new ObjectID( req.params.blogId) and now I get [ { _id: 60336bcc961785586471938b, posts: [ [Object] ] } ], so it is better. But can't I get only the posts part ? – Specialka Feb 22 '21 at 09:12
  • @Specialka See this post [How can I access and process nested objects, arrays or JSON?](https://stackoverflow.com/questions/11922383/how-can-i-access-and-process-nested-objects-arrays-or-json). – prasad_ Feb 22 '21 at 09:22
  • Thank you but I am aware to access an array and object with JS. I was asking how to return only that from the mongodb database. – Specialka Feb 22 '21 at 09:25
0

Your stages between square prackets should look like this:

[
  { $match: { _id: ObjectId( req.params.blogId) } },
  {
    $project: {
      posts: {
        $filter: {
          input: "$posts",
          as: "posts",
          cond: { $eq: ["$$posts._id", ObjectId(req.params.postId)] }
        }
      }
    }
  },
 {$unwind : "$posts" },
 {$replaceRoot :{ newRoot:"$posts"}
]

After filtering and projecting, unwind array objects and then simply replace the root. And of course, follow the instructions from the previous answer about the usage of toArray on aggregate cursor result.

Boris Kukec
  • 611
  • 3
  • 8