【问题标题】:Filter a many-to-many relation using a list of values使用值列表过滤多对多关系
【发布时间】:2021-07-21 05:33:34
【问题描述】:

我将 objection.js 和 knex.js 用于我的 RESTful API。

在我的数据库中,我有三个表:productscharacteristicsproduct_characteristics(使用名为“值”的额外列连接表)。

所以我要做的是获取所有具有相应特征值的产品。

在 GET 请求中,我接受 characteristics 查询参数并对其进行解析以获取我需要用来过滤我的产品的特征数组。

解析特征数组:

[{
   characteristicId: number,
   value: string
}, ...]

“产品”表字段:

[id, title, price, discount]

'特征的表格字段:

[id, name]

'product_characteristics' 连接表字段:

[productId, characteristicId, value]

目前我使用反对的queryBuilderwithGraphFetch 方法以及pagelimit 方法获取所有产品:

const query: any = ProductModel.query()
  .page(page - 1, limit)
  .orderBy(sortBy, order)
  .withGraphFetched({
    category: true,
    images: true,
    characteristics: true,
  });

const result = await query;
return {
  products: result.results,
  total: result.total,
};

Objection.js 提供了withGraphJoined 方法,该方法可以访问queryBuilder 中的相关实体,以便根据关系过滤parentModel,但不支持@987654337 @ 和 limit 方法。

因此,一种可能的解决方案是使用 knex.raw() 方法执行原始 SQL 查询。但是我花了一天时间尝试编写原始 SQL 查询来获取所有需要的数据。

理想的结果是一个特征过滤的产品数组,其中所有产品相关的特征作为 JSON 响应参数。

const products = [
  {
    id: 1,
    title: 'Pipe 1',
    price: 3000,
    discount: 500,
    characteristics: [
      {
        id: 1,
        name: 'diameter',
        value: '120 mm',
      },
      {
        id: 2,
        name: 'color',
        value: 'black',
      },
    ],
  },
  {
    id: 2,
    title: 'Pipe 2',
    price: 5000,
    discount: 0,
    characteristics: [
      {
        id: 1,
        name: 'diameter',
        value: '120 mm',
      },
      {
        id: 2,
        name: 'color',
        value: 'blue',
      },
    ],
  },
];

【问题讨论】:

    标签: knex.js objection.js


    【解决方案1】:

    我认为您尝试编写的 SQL 查询是

    SELECT
      p.id,
      p.title,
      p.price,
      p.discount,
      c.name characteristicName,
      pc.value characteristicValue
    FROM
      products p
      INNER JOIN product_characteristics pc ON pc.productId = p.id
      INNER JOIN characteristics c ON c.id = pc.characteristicId
    WHERE
      (c.id = 1 AND pc.value = 'value1')
      OR (c.id = 2 AND pc.value = 'value2')
      OR (c.id = 3 AND pc.value = 'value3')
    

    可以像这样在knex中实现

    knex({p: 'products'})
      .join({pc: 'product_characteristics'}, 'pc.productId', '=', 'p.id')
      .join({c: 'characteristics'}, 'c.id', '=', 'pc.characteristicId')
      .where(builder =>
        characteristics.forEach(item =>
          builder.orWhere(builder =>
            builder
              .andWhere('c.id', '=', item.characteristicId)
              .andWhere('pc.value', '=', item.value)
          )
        )
      )
      .select('p.id', 'p.title', 'p.price' /* etc */)
    

    您可以轻松地将.offset().limit() 添加到此以支持分页。它将转换为以下 SQL:

    select
      "p"."id",
      "p"."title",
      "p"."price"
    from
      "products" as "p"
      inner join "product_characteristics" as "pc" on "pc"."productId" = "p"."id"
      inner join "characteristics" as "c" on "c"."id" = "pc"."characteristicId"
    where
      (
        (
          "c"."id" = 1
          and "pc"."value" = 'value1'
        )
        or (
          "c"."id" = 2
          and "pc"."value" = 'value2'
        )
        or (
          "c"."id" = 3
          and "pc"."value" = 'value3'
        )
      )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-06-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-12-18
      相关资源
      最近更新 更多