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

Node.js GraphQL查询MongoDB嵌套文档数组的Resolver方案

GraphQL嵌套数组关联字段查询报错解决方案

问题背景

GraphQL初学者边学习边搭建应用时,需要编写Resolver适配Schema查询逻辑,核心需求为解析feedformula: [feeditemSchema]数组中存储的关联引用对象,正确获取feeding字段对应的Farmintake集合关联记录。
当前故障现象:

  • 仅查询feeding._id时GraphQL请求可正常返回结果
  • 查询feeding下的name属性时,系统抛出Cannot return null for non-nullable field Farmintake.name错误,返回结果中feedformula数组项全部为null

相关代码与报错信息

可正常执行的查询语句

query{
  feeds{
    _id
    feedname
    feedtime
    feedformula{
      feeding{
        _id
      }
      qty
      mesunit
      description
      remarks
    }
    creator{
      email
    }
  }
} 

触发报错的查询语句及返回

query{
  feeds{
    _id
    feedname
    feedtime
    feedformula{
      feeding{
        _id
        name
      }
      qty
      mesunit
      description
      remarks
    }
    creator{
      email
    }
  }
}

报错返回内容:

{
  "errors": [
    {
      "message": "Cannot return null for non-nullable field Farmintake.name.",
      "locations": [
        {
          "line": 81,
          "column": 8
        }
      ],
      "path": [
        "feeds",
        0,
        "feedformula",
        0,
        "feeding",
        "name"
      ]
    },
    {
      "message": "Cannot return null for non-nullable field Farmintake.name.",
      "locations": [
        {
          "line": 81,
          "column": 8
        }
      ],
      "path": [
        "feeds",
        0,
        "feedformula",
        1,
        "feeding",
        "name"
      ]
    },
    {
      "message": "Cannot return null for non-nullable field Farmintake.name.",
      "locations": [
        {
          "line": 81,
          "column": 8
        }
      ],
      "path": [
        "feeds",
        0,
        "feedformula",
        2,
        "feeding",
        "name"
      ]
    }
  ],
  "data": {
    "feeds": [
      {
        "_id": "62b417a91797e5053c2d58a3",
        "feedname": "Dana",
        "feedtime": "Evening",
        "feedformula": [
          null,
          null,
          null
        ],
        "creator": {
          "email": "auzrimfa@gmail.com"
        }
      }
    ]
  }
}

MongoDB Schema定义

const feeditemSchema = new Schema({
  feeding: {type: Schema.Types.ObjectId,ref: 'Farmintake'},
  mesunit: {type: String},
  qty: { type: Number },
  description:{ type: String },
  remarks:{ type: String }
});

const feedSchema = new Schema({
  feedname:{ type: String },
  feedtime:{ type: String },
  feedformula:[ feeditemSchema ],
  description:{ type: String },
  creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true });

const farmintakeSchema = new Schema({
  name:{type: String,required: true},
  itmtype:{type: String,required: true},
  madein:{type: String,required: true},
  mesunit:{type: String},
  usage:{type: String},
  remarks:{type: String},
  creator: {type: Schema.Types.ObjectId,ref: 'User'}
},{ timestamps: true}
);
module.exports = mongoose.model('Feed', feedSchema );
module.exports = mongoose.model('Farmintake',farmintakeSchema);

现有错误Resolver代码

const transformFeed = feed =>{
  return {
    ...feed._doc,
    _id: feed.id,
    feeding: farmintake.bind(this, feed.feedformula.feeding),
    creator: user.bind(this,feed.creator)
  };
};

const farmintake = async farmintakeId => {
  try{
    const farmintake = await Farmintake.findById(farmintakeId);
    return {
      ...farmintake._doc,
      _id:farmintake.id
    };
  }catch (err) {
    throw err;
  }
};

问题根因

  • 现有Resolver错误在feed根层级定义feeding字段解析逻辑,但feeding实际是feedformula数组下每个子项的字段,不存在于feed根节点
  • feed.feedformula是数组类型,直接访问feed.feedformula.feeding得到的值为undefined,传入Farmintake.findById后无法查询到对应记录,返回null。而Farmintake.name为非空字段,直接触发空值报错
  • 未针对嵌套数组feedformula的每个子项编写字段解析逻辑,导致数组项无法正确返回关联数据

正确Resolver实现方案

严格按照GraphQL Schema的字段层级编写解析逻辑,跨层级绑定字段是这类嵌套关联报错的核心诱因。

// 单条Feed数据转换
const transformFeed = feed => {
  return {
    ...feed._doc,
    _id: feed.id,
    // 遍历处理feedformula数组每一项,根层级无需定义feeding字段
    feedformula: feed.feedformula.map(item => transformFeedItem(item)),
    creator: user.bind(this, feed.creator)
  };
};

// 新增feedformula子项转换方法
const transformFeedItem = feedItem => {
  return {
    ...feedItem,
    _id: feedItem._id.toString(),
    // feeding字段属于feedformula子项,在此处绑定对应ObjectId
    feeding: farmintake.bind(this, feedItem.feeding)
  };
};

// Farmintake关联查询方法,新增空值判断避免无匹配数据时报错
const farmintake = async farmintakeId => {
  try{
    if (!farmintakeId) return null;
    const res = await Farmintake.findById(farmintakeId);
    if (!res) return null;
    return {
      ...res._doc,
      _id: res.id,
      creator: user.bind(this, res.creator)
    };
  }catch (err) {
    throw err;
  }
};

// User关联查询方法,保持原有逻辑即可,此处补全参考实现
const user = async userId => {
  try {
    if (!userId) return null;
    const res = await User.findById(userId);
    if (!res) return null;
    return {
      ...res._doc,
      _id: res.id
    };
  } catch (err) {
    throw err;
  }
};

优化方案(Mongoose环境适用)

使用Mongoose内置的populate方法做关联预填充,可省去手动编写异步绑定函数的步骤,feeds查询Resolver示例:

// feeds根查询Resolver
feeds: async () => {
  try {
    const feeds = await Feed.find()
      .populate('creator')
      .populate('feedformula.feeding');
    return feeds.map(feed => ({
      ...feed._doc,
      _id: feed.id
    }));
  } catch (err) {
    throw err;
  }
}

populate会自动将路径上存储的ObjectId替换为对应集合的匹配文档,大幅简化嵌套关联的解析逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 09:36:17