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

时间:2015-12-28 16:25:39

标签: javascript node.js postgresql sequelize.js

我正在创建一个食谱数据库(通常称为食谱),我需要在成分和食谱之间建立多对多关系,并且我使用组合与

当一种成分添加到配方中时,我需要声明配方中正确含量的成分。

我已宣布(缩减示例)

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,
    }
  ]
}

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

sequelize生成的数据库如下所示:

Ingredients
id  name
1   carrot

Recipes
id  name
1   Carrots

RecipeIngredients
amount  RecipeId  IngredientId
12      1         1

我尝试将attributes数组作为属性提供给include,如下所示:

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

但是将['amount']['RecipeIngredient.amount']设置为属性值会引发错误,例如

  

未处理拒绝SequelizeDatabaseError:列成分.RecipeIngredient.amount不存在

显然我可以使用.map在JS中修复此问题,但肯定有办法让sequelize为我做这项工作吗?

3 个答案:

答案 0 :(得分:1)

您可以使用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

答案 1 :(得分:0)

我已经浏览了文档,但我找不到任何似乎可以让我将连接表的属性合并到结果中的内容,所以看起来我似乎陷入困境做这样的事情:

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让我可以过滤掉我想要包含在连接表中的属性,但是对于我的生活,我找不到让sequelize为我合并的方法。

上面的代码将返回我想要的输出,但是增加了循环遍历成分列表的开销,这不是我想要的,但除非有人提出更好的解决方案,否则我无法看到另一种方式这样做。

答案 2 :(得分:0)

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

这是一些随机的例子

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)

})

相关问题