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

Mongoose用户与收藏点赞库关联模型实现问题咨询

User & Library Collection Association: Fixes & Optimizations

Hey there! Let's walk through your current implementation, identify the issues causing your empty query results, and explore better approaches for your user-library setup.

1. Model Design: Correctness & Optimization Points

First, let's break down your schema definitions and highlight areas for improvement:

Current User Schema

const userSchema = Schema({ 
  username: {type: String, minlength: 4, maxlength: 10, required: true, unique: true}, 
  email: {type: String, required: true, unique: true}, 
  password: {type: String, required: true}, 
  isVerified: { type: Boolean, default: false }, 
  library: {type: Schema.Types.ObjectId, ref: 'Library'} 
}, { timestamps: true});

Current Library Schema

const librarySchema = new Schema({ 
  likes: [{ likeId: {type: String}, mediaType: {type: String} }], 
  favourites: [{ favId: {type: String}, mediaType: {type: String} }], 
  user: {type: Schema.Types.ObjectId, ref: 'User'} 
});

Observations & Optimizations:

  • One-to-One Association: Your setup assumes a 1:1 relationship (each user has exactly one library), which is valid—but we need to ensure the link is properly established when a user is created.
  • ID Field Types: If likeId/favId reference documents from another internal collection (e.g., a Media collection), swap String for Schema.Types.ObjectId—this lets you use populate() on those fields later if needed. If they're external API IDs, String works fine.
  • Alternative: Embedded Document: For a 1:1 relationship where library data is always accessed with the user, embedding the library directly into the User schema is more efficient (eliminates separate collections and populate calls). Example:
    const userSchema = Schema({ 
      // ... existing fields
      library: {
        likes: [{ likeId: String, mediaType: String }],
        favourites: [{ favId: String, mediaType: String }]
      }
    }, { timestamps: true });
    

2. Why Your populate('library') Query Returns No Results

The core issue is you never link the User and Library when creating a new user. Your current signup flow only creates a User document, but doesn't:

  1. Generate a corresponding Library for the new user
  2. Assign the Library's _id to the User's library field
  3. Save the updated User with the library reference

Without this step, the User's library field stays null, so populate('library') has nothing to fetch.

3. Fixing Your Code

A. Fix the Signup Flow

Update your signup route to create and link the Library during user registration:

router.post('/signup', function (req, res) {
  const {errors, isValid} = validateSignupInput(req.body);
  if (!isValid) {
    return res.status(400).json(errors);
  }

  // Check for existing email (fixed error handling and async logic)
  User.findOne({email: req.body.email.toLowerCase()}, function(err, existingUser) {
    if (err) {
      return res.status(500).json({ title: 'Error checking email', error: err });
    }
    if (existingUser) {
      return res.status(400).json({ title: 'Email already exists' });
    }

    // Create new user
    let user = new User({
      username: req.body.username.toLowerCase(),
      email: req.body.email.toLowerCase(),
      password: bcrypt.hashSync(req.body.password, 10)
    });

    // Create linked library first, then save user with reference
    const library = new Library({ user: user._id });
    library.save()
      .then(savedLibrary => {
        user.library = savedLibrary._id;
        return user.save();
      })
      .then(savedUser => {
        res.status(201).json({ title: 'User created', obj: savedUser });
      })
      .catch(err => {
        res.status(500).json({ title: 'Signup error', error: err });
      });
  });
});

B. Fix the Library POST Request

Your /favourites route has two key issues:

  1. Library.update() returns the old document by default—add {new: true} to get the updated version
  2. $push allows duplicate entries—use $addToSet to prevent adding the same favourite multiple times

Here's the fixed version:

router.post('/favourites', passport.authenticate('jwt', {session: false}), function (req, res) {
  const favouritesFields = {};
  if (req.body.favId) favouritesFields.favId = req.body.favId;
  if (req.body.mediaType) favouritesFields.mediaType = req.body.mediaType;

  Library.findOne({user: req.user._id})
    .then(library => {
      if (library) {
        // Use $addToSet to avoid duplicates, {new: true} to return updated doc
        return Library.findOneAndUpdate(
          {user: req.user._id},
          {$addToSet: {favourites: favouritesFields}},
          {new: true}
        );
      } else {
        // Edge case: create library if it doesn't exist (signup should handle this normally)
        return new Library({user: req.user._id, favourites: [favouritesFields]}).save();
      }
    })
    .then(updatedLibrary => res.json(updatedLibrary))
    .catch(err => res.status(500).json({error: err}));
});

C. Fix the User Query

Now that users have linked libraries, your populate query will work. Fix the misleading parameter name and add error handling:

User.findOne({email: 'xxx@xxx.com'})
  .populate('library')
  .exec(function (err, user) {
    if (err) {
      console.error('Error fetching user:', err);
      return;
    }
    console.log('User with library:', user);
    // Access user.library.likes and user.library.favourites here
  });

4. Final Recommendations

  • Embedded vs. Separate Collection: If your library data is small and always accessed with the user, embedding it directly into the User schema is simpler and faster. If you anticipate large library sizes or need to query libraries independently, keep the separate collection.
  • Consistent Error Handling: Add error catching to all routes to debug issues early.
  • Validation: Consider adding required field validation to the Library schema (e.g., likeId/favId if they're mandatory).

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:24