使用 Mongoose 和 GraphQL 从填充模型有条件地返回值的最有效方法?

Posted

技术标签:

【中文标题】使用 Mongoose 和 GraphQL 从填充模型有条件地返回值的最有效方法?【英文标题】:Most efficient way to return values conditionally from populated Models using Mongoose and GraphQL? 【发布时间】:2020-09-16 08:42:25 【问题描述】:

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

这是我目前的解决方案:

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
  
,

utility.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 中的每个数组,我认为这远不是实现这一目标的最有效方法。

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

【问题讨论】:

对我来说似乎是Code Review 的问题。 提示:带有下划线 _ 前缀的属性主要表示库使用的内部值,当库决定更改实现时,它可能会破坏您的代码。而不是._doc,最好使用.toObject().toJSON() 【参考方案1】:

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

例如


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的值。

【讨论】:

以上是关于使用 Mongoose 和 GraphQL 从填充模型有条件地返回值的最有效方法?的主要内容,如果未能解决你的问题,请参考以下文章

使用 graphql 和 nodejs 填充猫鼬模式

Mongoose + GraphQL 无法填充模型 [重复]

Mongoose 虚拟填充返回 null

Graphql 结果不使用猫鼬填充关联类型的结果

Graphql 查询数组

返回突变结果的正确方法? (GraphQL 与 Apollo 和 Mongoose