首页 > 解决方案 > 使用 Mongoose 和 GraphQL 从填充模型有条件地返回值的最有效方法?

问题描述

我的目标是根据填充后从模型本身检索到的数据有条件地返回模型值。

这是我目前的解决方案:

const bcrypt = require("bcryptjs")
const jwt = require("jsonwebtoken")
const moment = require("moment")

const User = require("../../models/user")
const Post = require("../../models/post")

const { checkAuthorSettings, checkFollowingAuthorSettings } = require('../../shared/utility')

login: async ({ email, password }) => {
  try {
    const user = await User.findOne({ email }).populate([
      {
        path: 'posts',
        model: 'Post',
        populate: [
          {
            path: 'author',
            model: 'User',
          },
          {
            path: 'comments',
            model: 'Comment',
            populate: {
              path: 'author',
              model: 'User',
            },
          },
        ],
      },
      {
        path: 'following',
        model: 'User',
        populate: {
          path: 'posts',
          model: 'Post',
          populate: [
            {
              path: 'author',
              model: 'User',
            },
            {
              path: 'comments',
              model: 'Comment',
              populate: {
                path: 'author',
                model: 'User',
              },
            },
          ],
        },
      },
      {
        path: 'favourites',
        model: 'Post',
        populate: [
          {
            path: 'author',
            model: 'User',
          },
          {
            path: 'comments',
            model: 'Comment',
            populate: {
              path: 'author',
              model: 'User',
            },
          },
        ],
      },
    ])

    if (!user) throw new Error("An Account by that Email was not found!")
    if (!password) throw new Error("Please enter your password")

    const passIsValid = bcrypt.compareSync( password, user.password )
    if (!passIsValid) throw new Error("Incorrect Password")

    const token = jwt.sign(
      { 
        _id: user._id, 
        email: user.email,
      }, 
      `${process.env.JWT_SECRET}`, 
      { expiresIn: "1h" }
    )

    user.status = "online"
    user.logged_in_at = moment().format()
    await user.save()

    return {
      ...user._doc,
      token,
      token_expiry: 1,
      email: user.settings.display_email ? user.email : "",
      website: user.settings.display_website ? user.website : "",
      password: null,
      posts: await checkAuthorSettings(user.posts),
      following: await checkFollowingAuthorSettings(user.following),
      favourites: await checkAuthorSettings(user.favourites),
      info: JSON.stringify(user._doc.info),
      geolocation: JSON.stringify(user._doc.geolocation),
      settings: JSON.stringify(user._doc.settings),
    }
  } catch (err) {
    throw err
  }
},

实用程序.js:

const checkAuthorSettings = array => {
  return array.map(post => {
    return {
      ...post._doc,
      author: {
        ...post._doc.author._doc,
        email: post._doc.author._doc.settings.display_email ? post._doc.author._doc.email : "",
        website: post._doc.author._doc.settings.display_website ? post._doc.author._doc.website : "",
      }
    }
  })
}

const checkFollowingAuthorSettings = array => {
  return array.map(followed => {
    return {
      ...followed._doc,
      posts: checkAuthorSettings(followed.posts)
    }
  })
}

exports.checkAuthorSettings = checkAuthorSettings
exports.checkFollowingAuthorSettings = checkFollowingAuthorSettings

我目前正在遍历 User 中的每个数组,我认为这远不是实现这一目标的最有效方法。

有人有比这更好的解决方案吗?

标签: javascriptnode.jsmongodbmongoosegraphql

解决方案


可以使用virtuals,virtuals的实现方式有很多种,如果想email直接替换字段,可以使用getter

例如


const AuthorSchema = new Schema({
// other fields
  email: String,
}, {
  toObject: { getters: true }
})

AuthorSchema.path('email').get(function(email) {
  return this.get('settings.display_email') ? email : ''
})

然后,当您调用.toObject()文档时,您将获得一个virtual值。


推荐阅读