Mongoose & Express: How to properly Remove, Create & Store data that are reference

心不动则不痛 提交于 2021-02-10 20:10:20

问题


The first problem I am having is that whenever I try to delete the Comment, I also try to find the index of that specific comment inside post.comments as well as inside user.comments, it consistently returns -1, the reason why I am trying to find it, is so that I can splice it from the comments array that user and post do have. The second problem I am having is that whenever I create a comment, I try to store it in the comments array that user and post have, but it stores it only as a string, although I think it is supposed to be stored as an object right?, So I can access it later by populating?

I have been struggling for days now being very frustrated why it does not work. Please help me!

Below will be my two routes, for deleting and creating comments, and my Schemas, Thank You for all the help!

Creating Comments

// @route    POST api/posts/comment/:id
// @desc     Comment on a post
// @access   Private
router.post(
  '/comment/:id',
  [
    auth,
    [
      check('text', 'Text is required')
        .not()
        .isEmpty()
    ]
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    try {
      const post = await Post.findById(req.params.id);
      const user = await User.findById(req.user.id)
      const newComment = {
        text: req.body.text,
        post: post._id,
        user: req.user.id
      };
      const comment = new Comment(newComment);
      post.comments.unshift(comment._id);
      user.comments.unshift(comment._id)
      console.log(user.comments);
      console.log(post.comments);
      console.log(comment)
      await post.save();
      await comment.save();
      await user.save();
      res.json(comment);
    } catch (err) {
      console.error(err.message);
      res.status(500).send('Server Error');
    }
  }
);

Deleting comments

// @route    DELETE api/posts/comment/:id/:comment_id
// @desc     Delete comment
// @access   Private
router.delete('/comment/:id/:comment_id', auth, async (req, res) => {
  try {
    const post = await Post.findById(req.params.id);
    const user = await User.findById(req.user.id);

    // Pull out comment by finding it through its id
    const comment = await Comment.findById(req.params.comment_id);

    // Make sure comment exists
    if (!comment) {
      return res.status(404).json({ msg: 'Post do not have this comment' });
    }

    // Check user
    if (comment.user.toString() !== req.user.id) {
      return res.status(401).json({ msg: 'User not authorized' });
    }

      // Get The value to be removed
      const postCommentIndex = post.comments.findIndex(postComment => postComment === comment._id);


      const userCommentIndex = user.comments.findIndex(userComment => userComment === comment._id);

      console.log(`This is the post comment index ${postCommentIndex}`);
      console.log(`This is the user comment index ${userCommentIndex}`);

      post.comments.splice(postCommentIndex, 1);
      user.comments.splice(userCommentIndex, 1);

    // save user and post
    await post.save();
    await user.save();
    await comment.remove();

    // resend the comments that belongs to that post
    res.json(post.comments);
  } catch (err) {
    console.error(err.message);
    res.status(500).send('Server Error');
  }
});

Schemas:

const mongoose = require('mongoose');

const UserSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  email: {
    type: String,
    required: true,
    unique: true
  },
  password: {
    type: String,
    required: true
  },
  avatar: {
    type: String
  },
  posts: [{type: mongoose.Schema.Types.ObjectId, ref: "Post"}],
  comments: [{type: mongoose.Schema.Types.ObjectId, ref: "Comment"}],
  date: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('User', UserSchema);
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const PostSchema = new Schema({
  user: {
    type: Schema.Types.ObjectId,
    ref: 'User'
  },
  text: {
    type: String,
    required: true
  },
  likes: [
    {
      user: {
        type: Schema.Types.ObjectId,
        ref: 'User'
      }
    }
  ],
  dislikes: [
    {
        user: {
            type: Schema.Types.ObjectId,
            ref: "User"
        }
    }
  ],
  comments: [{type: Schema.Types.ObjectId, ref: "Comment"}],
  date: {
    type: Date,
    default: Date.now
  }
});

module.exports = mongoose.model('Post', PostSchema);
const mongoose = require("mongoose")
const Schema = mongoose.Schema;


const CommentSchema = new Schema({
        user: {
            type: Schema.Types.ObjectId,
            ref: 'User'
        },
        post: {
            type: Schema.Types.ObjectId,
            ref: "Post"
        },
        text: {
            type: String,
            required: true
        },
        date: {
            type: Date,
            default: Date.now
        }
})

module.exports = mongoose.model("Comment", CommentSchema);

回答1:


I think you need to redesign your schemas in a simpler way, there are too many references between the models, and this causes issues, for example you have 5 db access when you want to create a comment, and 6 db access when you want to delete a comment.

I would create the user schema like this removing the posts and comment references, but later when we want to access the posts from users, I set up virtual populate.

const UserSchema = new Schema(
  {
    name: {
      type: String,
      required: true
    },
    email: {
      type: String,
      required: true,
      unique: true
    },
    password: {
      type: String,
      required: true
    },
    avatar: {
      type: String
    },
    date: {
      type: Date,
      default: Date.now
    }
  },
  {
    toJSON: { virtuals: true }
  }
);

UserSchema.virtual("posts", {
  ref: "Post",
  localField: "_id",
  foreignField: "user"
});

And in the posts schema, I removed the comments references. (For simplicity I removed likes and dislikes fields.)

const PostSchema = new Schema(
  {
    user: {
      type: Schema.Types.ObjectId,
      ref: "User"
    },
    text: {
      type: String,
      required: true
    },
    date: {
      type: Date,
      default: Date.now
    }
  },
  {
    toJSON: { virtuals: true }
  }
);

PostSchema.virtual("comments", {
  ref: "Comment",
  localField: "_id",
  foreignField: "post"
});

Comment schema can stay as it is.

Now to add a comment to a post, we only need 2 db access, one for checking if post exists, and one for creating the post.

router.post(
  "/comment/:id",
  [
    auth,
    [
      check("text", "Text is required")
        .not()
        .isEmpty()
    ]
  ],
  async (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      return res.status(400).json({ errors: errors.array() });
    }

    try {
      const post = await Post.findById(req.params.id);
      if (!post) {
        return res.status(404).json({ msg: "Post not found" });
      }

      let comment = new Comment({
        text: req.body.text,
        post: req.params.id,
        user: req.user.id
      });

      comment = await comment.save();

      res.json(comment);
    } catch (err) {
      console.error(err.message);
      res.status(500).send("Server Error");
    }
  }
);

Let's say we have these 2 users:

{
    "_id" : ObjectId("5e216d74e7138b638cac040d"),
    "name" : "user1"
}
{
    "_id" : ObjectId("5e217192d204a26834d013e8"),
    "name" : "user2"
}

User1 with _id:"5e216d74e7138b638cac040d" has this post.

{
    "_id": "5e2170e7d204a26834d013e6",
    "user": "5e216d74e7138b638cac040d",
    "text": "Post 1",
    "date": "2020-01-17T08:31:35.699Z",
    "__v": 0,
    "id": "5e2170e7d204a26834d013e6"
}

Let's say user2 with _id:"5e217192d204a26834d013e8" commented on this post two times like this:

{
    "_id" : ObjectId("5e2172a4957c02689c9840d6"),
    "text" : "User2 commented on user1 post1",
    "post" : ObjectId("5e2170e7d204a26834d013e6"),
    "user" : ObjectId("5e217192d204a26834d013e8"),
    "date" : ISODate("2020-01-17T11:39:00.396+03:00"),
    "__v" : 0
},
{
    "_id": "5e21730d468bbb7ce8060ace",
    "text": "User2 commented again on user1 post1",
    "post": "5e2170e7d204a26834d013e6",
    "user": "5e217192d204a26834d013e8",
    "date": "2020-01-17T08:40:45.997Z",
    "__v": 0
}

To remove a comment we can use the following route, as you see we decreased the db access from 6 to 3, and code is shorter and cleaner.

router.delete("/comment/:id/:comment_id", auth, async (req, res) => {
  try {
    const comment = await Comment.findById(req.params.comment_id);

    if (!comment) {
      return res.status(404).json({ msg: "Post do not have this comment" });
    }

    if (comment.user.toString() !== req.user.id) {
      return res.status(401).json({ msg: "User not authorized" });
    }

    await comment.remove();

    // resend the comments that belongs to that post
    const postComments = await Comment.find({ post: req.params.id });
    res.json(postComments);
  } catch (err) {
    console.error(err.message);
    res.status(500).send("Server Error");
  }
});

Now you may ask, how will access the posts from an user? Since we setup virtual populate in our user schema, we can populate the posts like this:

router.get("/users/:id/posts", async (req, res) => {
  const result = await User.findById(req.params.id).populate("posts");

  res.send(result);
});



回答2:


You can try this code snipet :

Comment.deleteOne({
_id: comment.id
}, function (err) {
if (err) {
  console.log(err);
  return res.send(err.message);
}
res.send('success');
});


来源:https://stackoverflow.com/questions/59781221/mongoose-express-how-to-properly-remove-create-store-data-that-are-referen

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!