如何正确映射 sequelize.js 中关系的属性?

How do I properly map attributes of relations in sequelize.js?

我正在创建一个食谱数据库(通常称为食谱),我需要在其中建立成分和食谱之间的多对多关系,我正在使用 in combination with

当一种成分被添加到食谱中时,我需要声明该成分进入食谱的正确数量。

我已经声明(简化示例)

var Ingredient = sequelize.define('Ingredient', {
    name: Sequelize.STRING
}, {
    freezeTable: true
});

var Recipe = sequelize.define('Recipe', {
    name: Sequelize.STRING
}, {
    freezeTable: true
});

var RecipeIngredient = sequelize.define('RecipeIngredient', {
    amount: Sequelize.DOUBLE
});

Ingredient.belongsToMany(Recipe, { through: RecipeIngredient });
Recipe.belongsToMany(Ingredient, {
    through: RecipeIngredient,
    as: 'ingredients'
});

我的问题是当我的一个 REST 端点执行时如何返回数据

router.get('/recipes', function(req, res) {
    Recipe.findAll({
        include: [{
            model: Ingredient,
            as: 'ingredients'
         }]
    }).then(function(r) {
        return res.status(200).json(r[0].toJSON());
    })
});

发送给客户端的结果 JSON 如下所示(省略时间戳):

{
  "id": 1,
  "name": "Carrots",
  "ingredients": [
    {
      "id": 1,
      "name": "carrot",
      "RecipeIngredient": {
        "amount": 12,
        "RecipeId": 1,
        "IngredientId": 1
      }
    }
  ]
}

虽然我想要的只是

{
  "id": 1,
  "name": "Carrots",
  "ingredients": [
    {
      "id": 1,
      "name": "carrot",
      "amount": 12,
    }
  ]
}

也就是说,我希望关系 table 中的 amount 字段而不是整个 RecipeIngredient 对象包含在结果中。

sequelize生成的数据库是这样的:

Ingredients
id  name
1   carrot

Recipes
id  name
1   Carrots

RecipeIngredients
amount  RecipeId  IngredientId
12      1         1

我试过像这样向 include 提供一个 attributes 数组作为 属性:

include: [{
    model: Ingredient,
    as: 'ingredients',
    attributes: []
 }]

但是将 ['amount']['RecipeIngredient.amount'] 设置为属性值会引发类似

的错误

Unhandled rejection SequelizeDatabaseError: column ingredients.RecipeIngredient.amount does not exist

显然我可以使用 .map 在 JS 中解决这个问题,但肯定有一种方法可以让 sequelize 为我完成这项工作?

我已经查看了文档,但我找不到任何似乎可以让我将 join-table 的属性合并到结果中的东西,所以看起来我一直在做某事像这样:

router.get('/recipes', function(req, res) {
    Recipe.findAll({
        include: [{
            model: Ingredient,
            as: 'ingredients',
            through: {
                attributes: ['amount']
            }
         }]
    }).then(function(recipes) {
        return recipes[0].toJSON();
    }).then(function(recipe) {
        recipe.ingredients = recipe.ingredients.map(function(i) {
            i.amount = i.RecipeIngredient.amount;
            delete i.RecipeIngredient;
            return i;
        });
        return recipe;
    }).then(function(recipe) {
        return res.status(200).json(recipe);
    });
});

through 传递给 include 让我可以从 join-table 中过滤出我想包含的属性,但是对于我来说,我一直找不到进行 sequelize 合并的方法对我来说。

上面的代码将 return 我想要的输出但是增加了循环遍历成分列表的开销这不是我想要的但是除非有人想出更好的解决方案我不能查看另一种方法。

您可以使用 sequelize.literal。使用 Recipe 的 Ingredient 别名,你可以这样写。我不知道这是否是正确的方法。 :)

[sequelize.literal('`TheAlias->RecipeIngredient`.amount'), 'amount'],

我用sqlite3测试过。收到别名 "ir" 的结果是

{ id: 1,
  name: 'Carrots',
  created_at: 2018-03-18T04:00:54.478Z,
  updated_at: 2018-03-18T04:00:54.478Z,
  ir: [ { amount: 10, RecipeIngredient: [Object] } ] }

在此处查看完整代码。

https://github.com/eseom/sequelize-cookbook

我来晚了,但我看到它被浏览了很多所以这是我关于如何合并的答案 属性

这其中的一些随机示例

router.get('/recipes', function(req, res) {
Recipe.findAll({
    include: [{
        model: Ingredient,
        as: 'ingredients',
        through: {
            attributes: ['amount']
        }
     }]
})
.then(docs =>{
    const response = {
        Deal: docs.map(doc =>{
            return{
                cakeRecipe:doc.recipe1,
                CokkieRecipe:doc.recipe2,
                Apples:doc.ingredients.recipe1ingredient
                spices:[
                    {
                     sugar:doc.ingredients.spice1,
                     salt:doc.ingredients.spice2
                    }
                ]

            }
        })
    }

})
res.status(200).json(response)

})