You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Mongoose嵌套Map字段异常:评论likes无法转为Map实例

Mongoose嵌套评论Map字段无法解析为Map实例问题

问题详情

定义postSchema和commentSchema分别对应帖子与嵌套评论,两者均用Map字段存储点赞数据。帖子的likes字段可正常调用set/get方法,但评论的likes调用时会报错,提示不是函数。具体现象:

  • 创建评论时,comment.likes是Map实例
  • 更新帖子后查询结果,评论的likes变为普通对象(显示为{}),instanceof Map返回false
  • 帖子点赞功能(likePost)正常,评论点赞功能(likeComment)无法调用Map方法

相关代码

Post模型

import mongoose from 'mongoose';

const postSchema = mongoose.Schema(
  {
    userId: {
      type: String,
      required: true,
    },
    userName: {
      type: String,
      required: true,
    },
    picturePath: {
      type: String,
      default: '',
    },
    title: {
      type: String,
      required: true,
    },
    description: {
      type: String,
      required: true,
    },
    likes: {
      type: Map,
      of: Boolean,
      default: new Map(),
    },
    comments: {
      type: Array,
      default: [{ type: mongoose.Schema.Types.ObjectId, ref: 'Comment' }],
    },
  },
  { timestamps: true }
);

const Post = mongoose.model('Post', postSchema);
export default Post;

Comment模型

import mongoose from 'mongoose';

const commentSchema = mongoose.Schema(
  {
    postId: {
      type: String,
      required: true,
    },
    userId: {
      type: String,
      required: true,
    },
    userName: {
      type: String,
      required: true,
    },
    picturePath: {
      type: String,
      default: '',
    },
    description: {
      type: String,
      required: true,
    },
    likes: {
      type: Map,
      of: Boolean,
      default: new Map(),
    },
  },
  { timestamps: true }
);

const Comment = mongoose.model('Comment', commentSchema);
export default Comment;

添加评论接口

export const addComment = async (req, res) => {
  try {
    const { id } = req.params;
    const { userId, picturePath, description } = req.body;
    const user = await User.findById(userId);
    const newComment = new Comment({
      id,
      userId,
      userName: user.userName,
      picturePath,
      //likes: {},
      description,
    });
    newComment.set('likes', new Map());

    console.log(newComment.likes instanceof Map);
    const upDatedPost = await Post.findByIdAndUpdate(
      id,
      { $push: { comments: newComment } },
      { new: true }
    );

    const allPost = await Post.find();
    console.log(allPost[0].comments[2].likes instanceof Map);
    res.status(200).json(allPost);
  } catch (err) {
    console.log('err');
    res.status(404).json({ message: err.message });
  }
};

帖子点赞接口

export const likePost = async (req, res) => {
  try {
    const { id } = req.params;
    const { userId } = req.body;
    const post = await Post.findById(id);
    const isLiked = post.likes.get(userId);

    if (isLiked) {
      post.likes.delete(userId);
    } else {
      post.likes.set(userId, true);
    }

    const upDatedPost = await Post.findByIdAndUpdate(
      id,
      { likes: post.likes },
      { new: true }
    );

    res.status(200).json(upDatedPost);
  } catch (err) {
    res.status(404).json({ message: err.message });
  }
};

评论点赞接口

export const likeComment = async (req, res) => {
  try {
    const { id } = req.params;
    const { postId, userId } = req.body;
    let post = await Post.findById(postId);
    let comments = post.comments;
    console.log('comments: ', comments);
    console.log('likes: ', comments[0].likes);
    console.log(
      'Is likes an instanceof Map: ',
      post.comments[0].likes instanceof Map
    );
    //comments[0].likes.set(userId, true);
    //post.comments[0].set('likes', new Map());
    //console.log(comments[6].likes);
    // comments.forEach((element) => {
    //   if (element._id.toString() === id) {
    //     element.likes.set(userId, true);
    //   }
    // });

    res.status(200).json(post);
  } catch (err) {
    res.status(404).json({ message: err.message });
  }
};

控制台输出

comments:  [
  {
    userId: '63dc0274bd8c03b1e417cfc4',
    userName: 'dummyUserThree',
    picturePath: '',
    description: 'Likes still not working',
    _id: new ObjectId("63e13f26603a052fc8f16b09"),
    likes: {}
  }
]
likes:  {}
Is likes an instanceof Map:  false

问题原因

postSchema中comments字段定义为普通数组,即便声明了ref: 'Comment',Mongoose也不会自动将数组内的对象转换为Comment文档实例,只会以普通JSON对象存储,导致其中的Map字段无法被解析为Map实例,仅保留为普通对象。

解决方法

方法1:使用子文档数组(推荐)

修改postSchema的comments字段,直接嵌入commentSchema作为子文档类型,让Mongoose自动处理嵌套文档的字段解析:

import mongoose from 'mongoose';
import commentSchema from './commentSchema.js'; // 导入评论Schema

const postSchema = mongoose.Schema(
  {
    // 其他字段保持不变
    comments: {
      type: [commentSchema], // 直接使用评论Schema作为数组元素类型
      default: []
    },
  },
  { timestamps: true }
);

修改后,Mongoose会自动将数组内的每个评论的likes字段解析为Map实例,可直接调用set/get方法。

方法2:手动转换为Map

若要保留现有模式,查询帖子后需手动将评论的likes转换为Map实例:

export const likeComment = async (req, res) => {
  try {
    const { id } = req.params;
    const { postId, userId } = req.body;
    let post = await Post.findById(postId);
    
    // 手动转换每个评论的likes为Map
    post.comments.forEach(comment => {
      comment.likes = new Map(Object.entries(comment.likes));
    });

    // 找到目标评论并处理点赞
    const targetComment = post.comments.find(c => c._id.toString() === id);
    if (targetComment) {
      const isLiked = targetComment.likes.get(userId);
      if (isLiked) {
        targetComment.likes.delete(userId);
      } else {
        targetComment.likes.set(userId, true);
      }
      await post.save(); // 保存更新后的帖子
    }

    res.status(200).json(post);
  } catch (err) {
    res.status(404).json({ message: err.message });
  }
};

额外优化:添加评论逻辑调整

使用子文档模式后,无需单独创建Comment模型实例,直接创建符合Schema的对象即可:

export const addComment = async (req, res) => {
  try {
    const { id } = req.params;
    const { userId, picturePath, description } = req.body;
    const user = await User.findById(userId);
    
    // 直接创建子文档对象
    const newComment = {
      userId,
      userName: user.userName,
      picturePath,
      description
      // likes会自动使用Schema中定义的默认值new Map()
    };

    const updatedPost = await Post.findByIdAndUpdate(
      id,
      { $push: { comments: newComment } },
      { new: true }
    );

    res.status(200).json(updatedPost);
  } catch (err) {
    console.log('err');
    res.status(404).json({ message: err.message });
  }
};

内容的提问来源于stack exchange,提问作者Zachary Freiter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 17:50:37