You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django:按两列分组且不区分顺序去重,获取对话最新消息

解决方法

因为PostgreSQL支持DISTINCT ON语法,我们可以通过统一对话的用户标识来将(A,B)和(B,A)视为同一对话,具体实现如下:

1. 封装数据库函数

通过Django的Func类封装PostgreSQL的LEAST和GREATEST函数,用来将对话的两个用户ID按大小排序,生成固定的对话唯一标识:

from django.db.models import Func, F, Q, models

class Least(Func):
    function = 'LEAST'
    arity = 2

class Greatest(Func):
    function = 'GREATEST'
    arity = 2

2. 编写查询语句

通过annotate为每条消息生成统一的对话标识,再按对话标识+最新修改时间排序,最后用distinct保留每个对话的最新消息:

conversations = (
    models.TextMessage.objects.filter(Q(sender=user) | Q(receiver=user))
    # 生成对话唯一标识:user1为两个用户ID的较小值,user2为较大值
    .annotate(
        user1=Least(F('sender_id'), F('receiver_id')),
        user2=Greatest(F('sender_id'), F('receiver_id'))
    )
    # 先按对话标识分组排序,再按修改时间倒序(最新消息在前)
    .order_by('user1', 'user2', '-modified_at')
    # 按对话标识去重,保留每个对话的第一条(最新)消息
    .distinct('user1', 'user2')
).all()

原理说明

  • LEAST和GREATEST函数会把(A,B)和(B,A)的组合统一成(min_id, max_id),确保同一对话的标识唯一,不会被拆分为两个对话。
  • DISTINCT ON(user1, user2)配合order_by的顺序,会保留每个对话分组中排序后的第一条记录,也就是修改时间最新的消息。

内容的提问来源于stack exchange,提问作者thisisjab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 15:13:36