summaryrefslogtreecommitdiff
path: root/src/database/key_value/rooms/search.rs
blob: ad573f065caf51d03eec4f8578a65ae5bebf1263 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
use ruma::RoomId;

use crate::{database::KeyValueDatabase, service, services, utils, Result};

impl service::rooms::search::Data for KeyValueDatabase {
    fn index_pdu<'a>(&self, shortroomid: u64, pdu_id: &[u8], message_body: &str) -> Result<()> {
        let mut batch = message_body
            .split_terminator(|c: char| !c.is_alphanumeric())
            .filter(|s| !s.is_empty())
            .filter(|word| word.len() <= 50)
            .map(str::to_lowercase)
            .map(|word| {
                let mut key = shortroomid.to_be_bytes().to_vec();
                key.extend_from_slice(word.as_bytes());
                key.push(0xff);
                key.extend_from_slice(pdu_id); // TODO: currently we save the room id a second time here
                (key, Vec::new())
            });

        self.tokenids.insert_batch(&mut batch)
    }

    fn search_pdus<'a>(
        &'a self,
        room_id: &RoomId,
        search_string: &str,
    ) -> Result<Option<(Box<dyn Iterator<Item = Vec<u8>> + 'a>, Vec<String>)>> {
        let prefix = services()
            .rooms
            .short
            .get_shortroomid(room_id)?
            .expect("room exists")
            .to_be_bytes()
            .to_vec();

        let words: Vec<_> = search_string
            .split_terminator(|c: char| !c.is_alphanumeric())
            .filter(|s| !s.is_empty())
            .map(str::to_lowercase)
            .collect();

        let iterators = words.clone().into_iter().map(move |word| {
            let mut prefix2 = prefix.clone();
            prefix2.extend_from_slice(word.as_bytes());
            prefix2.push(0xff);
            let prefix3 = prefix2.clone();

            let mut last_possible_id = prefix2.clone();
            last_possible_id.extend_from_slice(&u64::MAX.to_be_bytes());

            self.tokenids
                .iter_from(&last_possible_id, true) // Newest pdus first
                .take_while(move |(k, _)| k.starts_with(&prefix2))
                .map(move |(key, _)| key[prefix3.len()..].to_vec())
        });

        let common_elements = match utils::common_elements(iterators, |a, b| {
            // We compare b with a because we reversed the iterator earlier
            b.cmp(a)
        }) {
            Some(it) => it,
            None => return Ok(None),
        };

        Ok(Some((Box::new(common_elements), words)))
    }
}