使用值列表过滤多对多关系
Filter a many-to-many relation using a list of values
我正在为我的 RESTful API 使用 objection.js 和 knex.js。
在我的数据库中,我有三个 table:products
、characteristics
和 product_characteristics
(加入 table 和名为 [=54 的额外列=]).
所以我要做的是获取所有具有相应特征值的产品。
在 GET 请求中,我接受了一个 characteristics
查询参数并解析它以获取我需要用来过滤我的产品的特征数组。
解析特征数组:
[{
characteristicId: number,
value: string
}, ...]
'products' table 字段:
[id, title, price, discount]
'特征的table字段:
[id, name]
'product_characteristics' 加入 table 个字段:
[productId, characteristicId, value]
目前我使用 objection 的 queryBuilder
和 withGraphFetch
方法以及 page
和 limit
方法获取所有产品:
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
过滤关系,但它不支持 page
和 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',
},
],
},
];
我认为您要编写的 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()
and .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'
)
)
我正在为我的 RESTful API 使用 objection.js 和 knex.js。
在我的数据库中,我有三个 table:products
、characteristics
和 product_characteristics
(加入 table 和名为 [=54 的额外列=]).
所以我要做的是获取所有具有相应特征值的产品。
在 GET 请求中,我接受了一个 characteristics
查询参数并解析它以获取我需要用来过滤我的产品的特征数组。
解析特征数组:
[{
characteristicId: number,
value: string
}, ...]
'products' table 字段:
[id, title, price, discount]
'特征的table字段:
[id, name]
'product_characteristics' 加入 table 个字段:
[productId, characteristicId, value]
目前我使用 objection 的 queryBuilder
和 withGraphFetch
方法以及 page
和 limit
方法获取所有产品:
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
过滤关系,但它不支持 page
和 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',
},
],
},
];
我认为您要编写的 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()
and .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'
)
)