0

我有以下要求:

let artPerCall = 20
let artPerUser = 2
let start = req.params.page

let query = `
    SELECT * from 
    (
        SELECT a.*, row_to_json(u.*) as userinfo,
            row_number() over (partition by u.address order by a.date desc) as ucount
        FROM artworks a INNER JOIN users u ON a.address = u.address 
        WHERE a.flag != ($1) OR a.flag IS NULL
    ) t
    WHERE ucount <= ($2)
    ORDER BY date DESC 
    LIMIT ${artPerCall} OFFSET ${(start-1) * artPerCall}`

pool.query(query, ["ILLEGAL", artPerUser])
    .then(users => {
        if (users) {
            res.json(users.rows);
        }
    })
    .catch(err => {
        next(err);
    })

通过具有以下路径的 API 调用/artworks/paginate/1/20where ( /artworks/paginate/{page}/20)

预期结果是每次调用获得 20 个结果,每个用户最多 2 个条目。

当前结果:似乎它按预期只为每个用户返回 2 个条目,但是一旦它为页面上的用户返回 2 个,那么即使他们有条目,后续页面中的同一用户也不会再有结果。

知道我缺少什么吗?

4

1 回答 1

1

似乎它按预期只为每个用户返回 2 个条目,但是一旦它为页面上的用户返回 2 个,那么即使他们有条目,后续页面中的同一用户也不会再有结果。

正确,这就是查询的作用。它选择:

row_number() over (partition by u.address order by a.date desc) as ucount
...
WHERE ucount <= ($2)

如果在示例代码中将参数 $2 设置为 2,那么它将在排序和分页之前为每个用户选择 2 个条目,而不是更多。如果用户有更多条目,他们将被过滤掉。

如果您删除“WHERE ucount <= ($2)”,那么您将简单地获得按日期排序的所有结果,但这听起来不像您想要的。

但是,我认为您想要实现的目标听起来有点复杂。我不确定它是否对可用性有好处,因为结果对用户来说看起来很随机。因此,您需要使用示例数据准确描述您想要的内容。

例如,如果您想避免一个用户发布大量具有相同日期的项目,将所有其他用户推到搜索结果中,限制每个用户的结果数量是一个好主意,但也许一个按钮“更多来自此用户...”将是比将用户的项目下推到下一页更好的选择。

假设您只有两个用户,user1 发布了 20 个日期为“今天”的项目,而 user2 昨天发布了 10 个项目。你想要来自 user1 的 2 个项目,然后来自 user2 的 2 个项目,然后来自 user1 的剩余 18 个项目,然后来自用户的 8 个剩余项目?或者它们会在某种程度上相互交错,这会使结果中的日期顺序有点随机?

编辑

这是一个建议:

SELECT * from 
(
    SELECT *, 
        row_number() over (partition by user_id order by date desc) as ucount
    FROM artworks
) t
ORDER BY (ucount/3)::INTEGER ASC, date DESC 
LIMIT 20 OFFSET 0;

"(ucount/3)::INTEGER" 为每个用户的前两幅作品为 0,然后为接下来的 3 幅作品为 1,然后为接下来的 3 幅作品为 2,依此类推。所以每个用户最近的 2 幅作品首先结束,然后是每个用户最近的 3 幅作品,等等。

另一个:

ORDER BY ucount<3 ASC, date DESC 

这将把每个用户最近的 2 件艺术品放在第一位,其余的则简单地按日期排序。

于 2021-03-03T14:38:16.190 回答