【问题标题】:Why is this nested relation in LoopBack returning duplicate results?为什么 LoopBack 中的这种嵌套关系会返回重复的结果?
【发布时间】:2019-01-24 03:48:09
【问题描述】:

当我查询包含嵌套模型时 - 例如GET /api/Widgets/1?filter={include: {"foos": "bars"}} - 我的结果中有重复的 foos。我认为这是由于 LEFT JOIN 或类似的原因,因为我正在使用 MySQL,但是当我在 loopback:connector:mysql 调试模式下运行 LoopBack 时,我可以看到初始小部件的查询运行一次,但是对 foo 的查询运行两次,对 bar 的查询运行两次。为什么会发生这种行为,我可以改变什么(我的模型、我的代码或我的期望)?

型号:

{
  "name": "Widget",
  ...
  "relations": {
    "foos": {
      "type": "hasMany",
      "model": "Foo",
      "foreignKey": "widgetId"
    }
  }
}

{
  "name": "Foo",
  ...
  "relations": {
    "bars": {
      "type": "hasMany",
      "model": "Bar",
      "foreignKey": "fooId"
    },
    "widget": {
      "type": "belongsTo",
      "model": "Widget",
      "foreignKey": ""
    }
  }
}

{
  "name": "Bar"
  ...
  "relations": {
    "foo": {
      "type": "belongsTo",
      "model": "Foo",
      "foreignKey": ""
    }
  }
}

结果:

{
  id: 1
  foos: [
    {
      id: 2,
      bars: [
        {
          id: 3
        }
      ]
    },
    {
      id: 2,
      bars: [
        {
          id: 3
        }
      ]
    }
  ]
}

期待:

{
  id: 1
  foos: [
    {
      id: 2,
      bars: [
        {
          id: 3
        }
      ]
    }
  ]
}

这是我看到正在为此请求运行的转述 SQL:

SELECT `...` FROM `Widget` WHERE `id`=1 ORDER BY `id` LIMIT 1
SELECT `...` FROM `Foo` WHERE `widget_id` IN (1) ORDER BY `id`
SELECT `...` FROM `Foo` WHERE `widget_id` IN (1) ORDER BY `id`
SELECT `...` FROM `Bar` WHERE `foo_id` IN (2) ORDER BY `id`
SELECT `...` FROM `Bar` WHERE `foo_id` IN (2) ORDER BY `id`

我正在使用 Loopback 3.x。

更新:虽然GET /api/Widgets/1?filter={include: {"foos": "bars"}} 的请求表现出这种行为,但Widgets.findById(id, {include: {"foos": "bars"}}) 的服务器端执行却可以完美运行。因此,目前我将创建一个远程方法来执行此操作,并可能使用 LoopBack 提交错误报告。

【问题讨论】:

  • 如果它在服务器端执行时运行良好,而您只是通过 API 调用获得了重复,那么可能是 Loopback routes 存在问题;双路由或某些嵌套路由可能会导致此问题。
  • 你解决了吗?
  • 查看我的回答,@Casy,如果您遇到类似问题,它可能会帮助您指出正确的方向。谢谢克里斯托斯,这帮助我解决了问题。

标签: loopbackjs


【解决方案1】:

我使用this mixin 将查询的limit 限制为在定义的值处最大化。当include 出现在查询中时,mixin 还会对包含范围设置限制,如下所示:

"include": {"foo":"bar","scope":{"limit":1}}

似乎 mixin 假设作为对象的所有包含都将以{"relation":"foo", "scope":{"include:"bars"}} 的形式编写,因此包含被添加了两次。

为了它的价值,我写了这个简单的 mixin 来限制结果的最大数量,除非指定并停止使用上面链接的那个:

common/models/model.json:

"mixins": {
    "ResultsetLimit": {
        "limit": 100
    }
}

common/mixins/resultset-limit.js:

const _ = require('lodash');

module.exports = (Model, options) => {

    /**
     * Modify incoming query to apply appropriate limit filters.
     */
    Model.beforeRemote('**', (ctx, unused, next) => {

        // Get the limit from the request, defaulting to the max limit.
        const request_limit = _.toNumber(_.get(ctx, 'args.filter.limit', options.limit));

        // Set the limit.
        _.set(ctx, 'args.filter.limit', request_limit);

        next();

    });

};

【讨论】:

  • 这节省了我一整天的时间
【解决方案2】:

您是否尝试过删除以下行? 因为默认情况下,如果未设置foreignKey,它将设置为<relationName>Id。但是由于您将其设置为空白,因此环回不会查找要引用的任何列。因此它正在获取您相关模型的所有记录。

{
  "name": "Widget",
  ...
  "relations": {
    "foos": {
      "type": "hasMany",
      "model": "Foo",
      "foreignKey": "widgetId"
    }
  }
}

{
  "name": "Foo",
  ...
  "relations": {
    "bars": {
      "type": "hasMany",
      "model": "Bar",
      "foreignKey": "fooId"
    },
    "widget": {
      "type": "belongsTo",
      "model": "Widget",
      "foreignKey": "" // remove this
    }
  }
}

{
  "name": "Bar"
  ...
  "relations": {
    "foo": {
      "type": "belongsTo",
      "model": "Foo",
      "foreignKey": "" //remove this
    }
  }
}

更新:

这就是我所说的二级(或三级)关系:

/api/Widgets/1?filter={include: [{"relation":"foo", "scope":{"include:"bars"}}]}

【讨论】:

  • 这似乎没有什么不同。默认foreignKey计算为relationName + 'Id',无论该行为空还是省略。
  • 更新了我的答案。添加了我如何称呼 3 级关系。
  • 这很有帮助,马克。我相信我使用的导致此问题的 mixin 是假设所有多级关系都是这样定义的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-27
  • 1970-01-01
  • 2021-12-08
相关资源
最近更新 更多