【问题标题】:Database search results with context带有上下文的数据库搜索结果
【发布时间】:2021-03-03 09:45:44
【问题描述】:

我已经构建了一个系统,用于跨不同平台(例如 MSN Messenger、Hangouts、WhatsApp)归档、汇总和索引我的 IM 历史记录。数据当前存储在 SQLite3 数据库中,架构的相关部分如下所示:

CREATE TABLE messages (
  message_id TEXT PRIMARY KEY,
  conversation_id TEXT,
  sender_id TEXT,
  sent_micros BIGINT,  -- microseconds since epoch
  received_micros BIGINT,
  read_micros BIGINT,
  message_plain TEXT,
  -- various other columns
)

所有列上还有NOT NULL 约束,但我跳过了这些约束以使架构更易于阅读。

我想做的是实现“使用上下文搜索”,其中显示结果周围(之前/之后)的记录。这是一个示例,添加了假语法和换行符,以便更容易区分结果组:

RETURNING CONTEXT 3 ROWS AROUND PARTITION BY conversation_id ORDER BY sent_micros ASC
SELECT message_id, conversation_id, sender_id, sent_micros, message_plain FROM messages
WHERE message_plain LIKE '%Waterdeep%'
ORDER BY sent_micros ASC, conversation_id ASC;
message_id | conversation_id | sender_id  | sent_s      | message_plain
-----------+-----------------+------------+-------------+-----------------------------------------
xkezwE9bMz | Mv3YdQsXZw      | vBPkl8G8e4 | 1614762003… | what did you want to play later?
PKhydJ3TCM | Mv3YdQsXZw      | i3QsU2T2ms | 1614762008… | I dunno
umfTYF2Qlv | Mv3YdQsXZw      | i3QsU2T2ms | 1614762009… | well actually
RJy6MVAQYv | Mv3YdQsXZw      | i3QsU2T2ms | 1614762011… | what about Lords of Waterdeep?
k2V49Ls1Jj | Mv3YdQsXZw      | vBPkl8G8e4 | 1614762314… | sounds good
PpUSRDVC9O | Mv3YdQsXZw      | vBPkl8G8e4 | 1614762315… | maybe I'll even let you win this time!
HNaiAd0zY6 | Mv3YdQsXZw      | i3QsU2T2ms | 1614762322… | hah, like that would ever happen

TUR5ZvEBMB | P2nclp3jaG      | vBPkl8G8e4 | 1614726660… | I'm heading to bed
au72U5GaQQ | P2nclp3jaG      | vBPkl8G8e4 | 1614726661… | goodnight
HFGXbn1X0S | P2nclp3jaG      | KcDHZ9zM4c | 1614727012… | 'night
E6u30kU7Fo | P2nclp3jaG      | vBPkl8G8e4 | 1614762840… | did Sam get the Waterdeep expansion?
ssTPVZO1ce | P2nclp3jaG      | KcDHZ9zM4c | 1614762921… | yeah
WEA69uNJ7a | P2nclp3jaG      | KcDHZ9zM4c | 1614762926… | we're hoping to play it soon
wktsSF3L3l | P2nclp3jaG      | KcDHZ9zM4c | 1614762934… | like maybe this weekend

请注意,时间戳仅在一组记录中排序; “我要睡觉了”在“你以后想玩什么?”之前发送。但“深水”行的顺序正确。


我能想到的唯一方法是使用 2n + 1 个查询:

  1. SELECT message_id, conversation_id, sent_micros FROM messages WHERE message_plain LIKE '%Waterdeep%' ORDER BY sent_micros ASC
  2. 对于该结果中的每一行:
    • SELECT * FROM messages WHERE conversation_id = :conversation_id AND sent_micros >= :sent_micros ORDER BY sent_micros ASC LIMIT 4
    • SELECT * FROM messages WHERE conversation_id = :conversation_id AND sent_micros <= :sent_micros ORDER BY sent_micros DESC LIMIT 4

但这似乎非常浪费和低效。

有没有更好的方法来处理这个问题?窗口函数有用吗?不同的数据库引擎会有帮助吗?


似乎在某个时候我会添加全文搜索功能,而不是简单的基于LIKE 的匹配。我想这基本上只会给出查询的message_id结果,所以感觉就像是某种“返回n行围绕主键值x y, z" 解决方案将是理想的。基于LIKE 的查询可以分解为该形式,并且只会是两个查询而不是2n + 1

【问题讨论】:

    标签: sql sqlite window-functions


    【解决方案1】:

    你可以用ROW_NUMBER()窗口函数来做到这一点:

    WITH cte AS (
      SELECT *, 
             message_plain LIKE '%Waterdeep%' flag,
             ROW_NUMBER() OVER (ORDER BY conversation_id, sent_micros) rn
      FROM messages 
    )
    SELECT c1.message_id, c1.conversation_id, c1.sent_micros
    FROM cte c1 INNER JOIN cte c2
    ON c2.conversation_id = c1.conversation_id AND c1.rn BETWEEN c2.rn - 4 AND c2.rn + 4
    WHERE c2.flag
    

    【讨论】:

    • 哦,太优雅了,谢谢!
    猜你喜欢
    • 1970-01-01
    • 2016-04-28
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 2020-07-24
    • 1970-01-01
    • 2017-09-06
    • 2015-03-09
    相关资源
    最近更新 更多