Database schema optimization - part 2

This commit is contained in:
mdecimus
2025-10-31 18:25:01 +01:00
parent 3e1d2d54e4
commit d5c5f12adf
43 changed files with 2266 additions and 1853 deletions

View File

@@ -8,10 +8,10 @@ pub mod index;
pub mod local;
pub mod query;
use ahash::AHashMap;
use nlp::language::Language;
use roaring::RoaringBitmap;
use std::borrow::Cow;
use types::collection::Collection;
use std::{borrow::Cow, collections::hash_map::Entry};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SearchOperator {
@@ -24,7 +24,7 @@ pub enum SearchOperator {
Exists,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SearchField {
Email(EmailSearchField),
Calendar(CalendarSearchField),
@@ -32,7 +32,7 @@ pub enum SearchField {
File(FileSearchField),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum EmailSearchField {
From,
To,
@@ -48,17 +48,34 @@ pub enum EmailSearchField {
Header(Cow<'static, str>),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CalendarSearchField {
Summary,
Title,
Description,
Location,
Owner,
Attendee,
Start,
Uid,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ContactSearchField {
Created,
Member,
Kind,
Name,
Nickname,
Organization,
Email,
Phone,
OnlineService,
Address,
Note,
Uid,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FileSearchField {
Name,
Content,
@@ -95,18 +112,11 @@ pub enum SearchComparator {
#[derive(Debug)]
pub struct IndexDocument {
pub(crate) account_id: u32,
pub(crate) collection: Collection,
pub(crate) document_id: u32,
pub(crate) fields: Vec<IndexField>,
pub(crate) fields: AHashMap<SearchField, SearchValue>,
pub(crate) default_language: Language,
}
#[derive(Debug)]
pub struct IndexField {
pub(crate) field: SearchField,
pub(crate) value: SearchValue,
}
impl SearchFilter {
pub fn cond(
field: impl Into<SearchField>,
@@ -214,10 +224,16 @@ impl SearchFilter {
}
}
#[inline(always)]
pub fn has_english_text(field: impl Into<SearchField>, text: impl Into<String>) -> Self {
Self::has_text(field, text, Language::English)
}
#[inline(always)]
pub fn has_unknown_text(field: impl Into<SearchField>, text: impl Into<String>) -> Self {
Self::has_text(field, text, Language::Unknown)
}
pub fn is_in_set(set: RoaringBitmap) -> Self {
SearchFilter::DocumentSet(set)
}
@@ -257,11 +273,10 @@ impl SearchComparator {
impl IndexDocument {
pub fn with_default_language(default_language: Language) -> Self {
Self {
fields: vec![],
fields: Default::default(),
default_language,
account_id: 0,
document_id: 0,
collection: Collection::None,
}
}
@@ -275,16 +290,39 @@ impl IndexDocument {
self
}
pub fn with_collection(mut self, collection: Collection) -> Self {
self.collection = collection;
self
pub fn index_text(&mut self, field: impl Into<SearchField>, value: &str, language: Language) {
match self.fields.entry(field.into()) {
Entry::Occupied(mut entry) => {
if let SearchValue::Text {
value: existing_value,
..
} = entry.get_mut()
{
existing_value.push(' ');
existing_value.push_str(value);
}
}
Entry::Vacant(entry) => {
entry.insert(SearchValue::Text {
value: value.to_string(),
language,
});
}
}
}
pub fn index(&mut self, field: impl Into<SearchField>, value: impl Into<SearchValue>) {
self.fields.push(IndexField {
field: field.into(),
value: value.into(),
});
pub fn index_bool(&mut self, field: impl Into<SearchField>, value: bool) {
self.fields
.insert(field.into(), SearchValue::Boolean(value));
}
pub fn index_number<N: Into<i64>>(&mut self, field: impl Into<SearchField>, value: N) {
self.fields
.insert(field.into(), SearchValue::Number(value.into()));
}
pub fn has_field(&self, field: &SearchField) -> bool {
self.fields.contains_key(field)
}
}

View File

@@ -15,7 +15,8 @@ use crate::{
SUBSPACE_QUEUE_EVENT, SUBSPACE_QUEUE_MESSAGE, SUBSPACE_QUOTA, SUBSPACE_REPORT_IN,
SUBSPACE_REPORT_OUT, SUBSPACE_SETTINGS, SUBSPACE_TASK_QUEUE, SUBSPACE_TELEMETRY_INDEX,
SUBSPACE_TELEMETRY_METRIC, SUBSPACE_TELEMETRY_SPAN, U16_LEN, U32_LEN, U64_LEN, ValueKey,
WITH_SUBSPACE, write::IndexPropertyClass,
WITH_SUBSPACE,
write::{IndexPropertyClass, SearchIndex},
};
use std::convert::TryInto;
use types::{blob_hash::BLOB_HASH_LEN, collection::SyncCollection};
@@ -279,15 +280,15 @@ impl ValueClass {
.write(document_id),
ValueClass::TaskQueue(task) => match task {
TaskQueueClass::UpdateIndex {
collection,
index,
is_insert,
due,
} => serializer
.write(*due)
.write(account_id)
.write(if *is_insert { 7u8 } else { 8u8 })
.write(u8::from(*collection))
.write(document_id),
.write(document_id)
.write(index.to_u8()),
TaskQueueClass::BayesTrain { due, learn_spam } => serializer
.write(*due)
.write(account_id)
@@ -657,3 +658,26 @@ impl Deserialize for ReportEvent {
})
}
}
impl SearchIndex {
pub fn to_u8(&self) -> u8 {
match self {
SearchIndex::Email => 0,
SearchIndex::Calendar => 1,
SearchIndex::Contacts => 2,
SearchIndex::File => 3,
SearchIndex::DeliveryHistory => 4,
}
}
pub fn try_from_u8(value: u8) -> Option<Self> {
match value {
0 => Some(SearchIndex::Email),
1 => Some(SearchIndex::Calendar),
2 => Some(SearchIndex::Contacts),
3 => Some(SearchIndex::File),
4 => Some(SearchIndex::DeliveryHistory),
_ => None,
}
}
}

View File

@@ -192,7 +192,7 @@ pub enum IndexPropertyClass {
pub enum TaskQueueClass {
UpdateIndex {
due: u64,
collection: Collection,
index: SearchIndex,
is_insert: bool,
},
BayesTrain {
@@ -211,6 +211,15 @@ pub enum TaskQueueClass {
},
}
#[derive(Debug, PartialEq, Clone, Copy, Eq, Hash)]
pub enum SearchIndex {
Email,
Calendar,
Contacts,
File,
DeliveryHistory,
}
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
pub struct AnyClass {
pub subspace: u8,