Node.js,Apollo Server v4,GraphQL,Mongoose - Category Tree

9ceoxa92  于 11个月前  发布在  Go
关注(0)|答案(1)|浏览(156)

我正在尝试使用Node.js,Apollo Server v4,GraphQL,Mongoose技术创建类别树。
但是当我想获取创建的类别或子类别数据时,输出不是我想要的。类别既作为引用出现在子类别中,也作为普通类别出现。我还添加了具有此id 655 b105543598 f2 ef 0 ba 0 f69的类别数据,作为具有此id 655 b104 a43598 f2 ef 0 ba 0 f64值的类别的子类别数组中的第2个子类别。我不明白。但是虽然名字不出现空。
GraphQL查询请求;

query Query {
  getCategories {
    success
    response_code
    message
    categories {
      _id
      name
      attributes {
        name
        values {
          value
        }
      }
      products {
        name
      }
      subCategories {
        _id
        name
        attributes {
          name
          values {
            value
          }
        }
        products {
          name
        }
        subCategories {
          _id
          name
          attributes {
            name
            values {
              value
            }
          }
          products {
            name
          }
          subCategories {
            _id
            name
            attributes {
              name
              values {
                value
              }
            }
            products {
              name
            }
            subCategories {
              _id
              name
              attributes {
                name
                values {
                  value
                }
              }
              products {
                name
              }
            }
          }
        }
      }
    }
  }
}

字符串
产出;

{
  "data": {
    "getCategories": {
      "success": true,
      "response_code": "categories-successfully-retrieved",
      "message": "Categories Successfully Retrieved!",
      "categories": [
        {
          "_id": "655b103b43598f2ef0ba0f5e",
          "name": "K 2",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        },
        {
          "_id": "655b103e43598f2ef0ba0f61",
          "name": "K 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": [
            {
              "_id": "655b104a43598f2ef0ba0f64",
              "name": "AK 1",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": [
                {
                  "_id": "655b105543598f2ef0ba0f69",
                  "name": null,
                  "attributes": null,
                  "products": null,
                  "subCategories": null
                }
              ]
            },
            {
              "_id": "655b106643598f2ef0ba0f6f",
              "name": "AK 2",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": []
            }
          ]
        },
        {
          "_id": "655b104a43598f2ef0ba0f64",
          "name": "AK 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": [
            {
              "_id": "655b105543598f2ef0ba0f69",
              "name": "AAK 1",
              "attributes": {
                "name": null,
                "values": {
                  "value": null
                }
              },
              "products": [],
              "subCategories": []
            }
          ]
        },
        {
          "_id": "655b105543598f2ef0ba0f69",
          "name": "AAK 1",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        },
        {
          "_id": "655b106643598f2ef0ba0f6f",
          "name": "AK 2",
          "attributes": {
            "name": null,
            "values": {
              "value": null
            }
          },
          "products": [],
          "subCategories": []
        }
      ]
    }
  }
}


预期产出;

[
  {
  "name": "Category 1",
  "attributes": {
    "name": "Attribute Name",
    "values": [{
      "value": "Attribute Value",
      "products": ["Product Ref", "Product Ref"]
    }]
  },
  "products": ["Product Ref", "Product Ref"],
  "subCategories": [
      {
      "name": "Sub Category 1",
      "attributes": {
        "name": "Attribute Name",
        "values": [{
          "value": "Attribute Value",
          "products": ["Product Ref", "Product Ref"]
        }]
      },
      "products": ["Product Ref", "Product Ref"],
      "subCategories": [
        {
          "name": "Sub Category 2",
          "attributes": {
            "name": "Attribute Name",
            "values": [{
              "value": "Attribute Value",
              "products": ["Product Ref", "Product Ref"]
            }]
          },
          "products": ["Product Ref", "Product Ref"],
          "subCategories": []
        }
      ]
    }
  ]
},
{
  "name": "Category 2",
  "attributes": {
    "name": "Attribute Name",
    "values": [{
      "value": "Attribute Value",
      "products": ["Product Ref", "Product Ref"]
    }]
  },
  "products": ["Product Ref", "Product Ref"],
  "subCategories": [
      {
      "name": "Sub Category 1",
      "attributes": {
        "name": "Attribute Name",
        "values": [{
          "value": "Attribute Value",
          "products": ["Product Ref", "Product Ref"]
        }]
      },
      "products": ["Product Ref", "Product Ref"],
      "subCategories": [
        {
          "name": "Sub Category 2",
          "attributes": {
            "name": "Attribute Name",
            "values": [{
              "value": "Attribute Value",
              "products": ["Product Ref", "Product Ref"]
            }]
          },
          "products": ["Product Ref", "Product Ref"],
          "subCategories": []
        }
      ]
    }
  ]
}
]


范畴 Mongoose 图式;

import mongoose from "mongoose";

const attributeSchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true, unique: true, default: "Default Name" },
  values: [
    {
      value: { type: String, required: true, trim: true, unique: true },
      products: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
    },
  ],
});

const categorySchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }],
  attributes: [attributeSchema],
  products: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
});

export const Category = mongoose.model("Category", categorySchema);


类别GraphQL模式;

export const categorySchema = `#graphql
  type Category {
    _id: ID
    name: String
    subCategories: [Category]
    attributes: Attributes
    products: [Product]
  }

  input CategoryInput {
    name: String!
    isMainCategory: Boolean!
    parentCategoryID: ID
  }

  type Attributes {
    name: String
    values: AttributesValue
  }

  type AttributesValue {
    value: String
    products: [Product]
  }

  input AttributesInput {
    name: String!
    categoryID: ID!
  }

  input AttributesValueInput {
    value: String!
    attributesID: ID!
  }

  type QueryCategoryResponse {
    success: Boolean!
    response_code: String!
    message: String!
    category: Category
    categories: [Category]
  }

  type Query {
    getCategories: QueryCategoryResponse
    getCategoryByID(id: ID!): QueryCategoryResponse
    getCategoriesByID(ids: [ID]!): QueryCategoryResponse
  }

  type Mutation {
    createCategory(input: CategoryInput!): Response
  }
`;


类别解析器;

import { Category } from "./model.js";

export const categoryResolver = {
  Query: {
    getCategories: async () => {
      try {
        const categories = await Category.find().populate("subCategories attributes products");

        if (categories) {
          return {
            success: true,
            response_code: "categories-successfully-retrieved",
            message: "Categories Successfully Retrieved!",
            categories: categories,
          };
        } else {
          return { success: false, response_code: "categories-not-found", message: "Categories Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
    getCategoryByID: async (_, { id }) => {
      try {
        const category = await Category.findById({ _id: id }).populate("subCategories attributes products");

        if (category) {
          return {
            success: true,
            response_code: "category-successfully-retrieved",
            message: "Category Successfully Retrieved!",
            category: category,
          };
        } else {
          return { success: false, response_code: "category-not-found", message: "Category Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
    getCategoriesByID: async (_, { ids }) => {
      try {
        const categories = await Category.find({ _id: { $in: ids } }).populate("subCategories attributes products");

        if (categories.length !== 0) {
          return {
            success: true,
            response_code: "categories-successfully-retrieved",
            message: "Categories Successfully Retrieved!",
            categories: categories,
          };
        } else {
          return { success: false, response_code: "categories-not-found", message: "Categories Not Found!" };
        }
      } catch (error) {
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
  },
  Mutation: {
    createCategory: async (_, { input }) => {
      try {
        if (input.isMainCategory) {
          const category = await Category.findOne({ name: input.name }).collation({ locale: "en", strength: 2 });

          if (category) {
            return { success: false, response_code: "category-name-exist", message: "Category Name Exist!" };
          } else {
            const newCategory = new Category({
              name: input.name,
            });

            await newCategory.save();

            return {
              success: true,
              response_code: "category-created-successfully",
              message: "Category Created Successfully!",
            };
          }
        } else {
          const category = await Category.findById(input.parentCategoryID).populate("subCategories");

          if (!category) {
            return { success: false, response_code: "sub-category-not-found", message: "Sub Category Not Found!" };
          }

          const categoryNames = category.subCategories.map((category) => category.name.toLowerCase());

          if (categoryNames.includes(input.name.toLowerCase())) {
            return { success: false, response_code: "category-name-exist", message: "Category Name Exists!" };
          }

          const newCategory = new Category({
            name: input.name,
          });

          await newCategory.save();

          category.subCategories.push(newCategory);
          await category.save();

          return {
            success: true,
            response_code: "sub-category-created-successfully",
            message: "Sub Category Created Successfully!",
          };
        }
      } catch (error) {
        console.log(error);
        return { success: false, response_code: "server-error", message: "Server Error!" };
      }
    },
  },
};


我如何改进这段代码并解决我的getCategories输出问题?
显然,我也尝试过mongoose ref,我也尝试过根据categorySchema直接将数据发送到子类别中。我在第二个方法中提到的添加子类别的方法对我来说是一个头痛的问题。

e5njpo68

e5njpo681#

你的模型中似乎缺少了parent类别的概念。当定义一个层次模型时,通常包括对父类别的引用(类似于parentId)。
然后返回所有类别,首先搜索所有具有nullparentId的类别-这些是根节点。
你还需要一个子类别的字段解析器来查找父类别是当前类别的类别。这允许你递归地遍历树。
GraphQL的一个限制是你必须明确你想在你的查询中带回多少层,你不能定义一个递归树,它会向下钻取到每一个可能的叶子。你上面的查询向下钻取5层。
首先,在GraphQL类型中,添加一个parent字段。

type Category {
  _id: ID
  name: String
  subCategories: [Category]
  parent: Category
  attributes: Attributes
  products: [Product]
}

字符串
在Mongoose模型中:

const categorySchema = new mongoose.Schema({
  name: { type: String, required: true, trim: true },
  subCategories: [{ type: mongoose.Schema.Types.ObjectId, ref: "Category" }],
  parentId: { type: mongoose.Schema.Types.ObjectId, required: false },
  attributes: [attributeSchema],
  products: [{ type: mongoose.Schema.Types.ObjectId, ref: "Product" }],
});


然后修改getCategories解析器以搜索parentId为空的类别
然后向Category类型添加一个字段解析器来解析基于parentId的子类别,并添加一个字段解析器来解析父类别。
现在,相对于您的values结构-您定义它的方式,它严格地位于Mongoose模型的内部;如果你想通过value嵌套结果,那么你需要在Category类型中添加一个value字段,并定义一个包含valueproductsValue类型,即

type Value {
  attributes: Attributes
  products: [Product]
}
type Category {
  _id: ID
  name: String
  subCategories: [Category]
  parent: Category
  value: Value
}


定义一个 pluraltype(例如:Attributes)是不常见的。类型通常是单数的,因为如果你想要几个类型,你只需要把它们组成一个数组。你想在一个 field 名称中使用复数形式,而不是一个 type 名称。

相关问题