fix: search improvements (#5473)

* fix: search workspace sync indexing

* chore: update collab rev temporarily

* feat: revert comparison and implement index check

* chore: fixes after merg

* feat: clean code + support delete workspace

* fix: improve code

* fix: improvements after merge

* fix: cargo fmt

* fix: remove indices for workspace method

* fix: clippy errors

* fix: clippy too many arguments
This commit is contained in:
Mathias Mogensen 2024-06-05 13:44:32 +02:00 committed by GitHub
parent 6e7d044208
commit bd7977d8ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 240 additions and 124 deletions

View File

@ -12,12 +12,37 @@ import 'package:flowy_infra_ui/widget/flowy_tooltip.dart';
import 'package:flowy_infra_ui/widget/spacing.dart'; import 'package:flowy_infra_ui/widget/spacing.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
class SearchField extends StatelessWidget { class SearchField extends StatefulWidget {
const SearchField({super.key, this.query, this.isLoading = false}); const SearchField({super.key, this.query, this.isLoading = false});
final String? query; final String? query;
final bool isLoading; final bool isLoading;
@override
State<SearchField> createState() => _SearchFieldState();
}
class _SearchFieldState extends State<SearchField> {
final focusNode = FocusNode();
late final controller = TextEditingController(text: widget.query);
@override
void initState() {
super.initState();
focusNode.requestFocus();
controller.selection = TextSelection(
baseOffset: 0,
extentOffset: controller.text.length,
);
}
@override
void dispose() {
focusNode.dispose();
controller.dispose();
super.dispose();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
@ -29,7 +54,8 @@ class SearchField extends StatelessWidget {
), ),
Expanded( Expanded(
child: FlowyTextField( child: FlowyTextField(
controller: TextEditingController(text: query), focusNode: focusNode,
controller: controller,
textStyle: textStyle:
Theme.of(context).textTheme.bodySmall?.copyWith(fontSize: 14), Theme.of(context).textTheme.bodySmall?.copyWith(fontSize: 14),
decoration: InputDecoration( decoration: InputDecoration(
@ -84,7 +110,7 @@ class SearchField extends StatelessWidget {
.add(CommandPaletteEvent.searchChanged(search: value)), .add(CommandPaletteEvent.searchChanged(search: value)),
), ),
), ),
if (isLoading) ...[ if (widget.isLoading) ...[
const HSpace(12), const HSpace(12),
FlowyTooltip( FlowyTooltip(
message: LocaleKeys.commandPalette_loadingTooltip.tr(), message: LocaleKeys.commandPalette_loadingTooltip.tr(),

View File

@ -112,4 +112,4 @@ collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlow
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }

View File

@ -76,4 +76,4 @@ collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlow
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }

View File

@ -2173,6 +2173,7 @@ dependencies = [
"nanoid", "nanoid",
"parking_lot 0.12.1", "parking_lot 0.12.1",
"protobuf", "protobuf",
"serde",
"serde_json", "serde_json",
"strum_macros 0.21.1", "strum_macros 0.21.1",
"tokio", "tokio",

View File

@ -113,4 +113,4 @@ collab-folder = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlow
collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-document = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-database = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-plugins = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }
collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" } collab-user = { version = "0.2", git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "6febf0397e66ebf0a281980a2e7602d7af00c975" }

View File

@ -33,28 +33,21 @@ pub struct FolderDepsResolver();
impl FolderDepsResolver { impl FolderDepsResolver {
pub async fn resolve( pub async fn resolve(
authenticate_user: Weak<AuthenticateUser>, authenticate_user: Weak<AuthenticateUser>,
document_manager: &Arc<DocumentManager>,
database_manager: &Arc<DatabaseManager>,
collab_builder: Arc<AppFlowyCollabBuilder>, collab_builder: Arc<AppFlowyCollabBuilder>,
server_provider: Arc<ServerProvider>, server_provider: Arc<ServerProvider>,
folder_indexer: Arc<FolderIndexManagerImpl>, folder_indexer: Arc<FolderIndexManagerImpl>,
store_preferences: Arc<StorePreferences>, store_preferences: Arc<StorePreferences>,
chat_manager: &Arc<ChatManager>, operation_handlers: FolderOperationHandlers,
) -> Arc<FolderManager> { ) -> Arc<FolderManager> {
let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl { let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl {
authenticate_user: authenticate_user.clone(), authenticate_user: authenticate_user.clone(),
}); });
let handlers = folder_operation_handlers(
document_manager.clone(),
database_manager.clone(),
chat_manager.clone(),
);
Arc::new( Arc::new(
FolderManager::new( FolderManager::new(
user.clone(), user.clone(),
collab_builder, collab_builder,
handlers, operation_handlers,
server_provider.clone(), server_provider.clone(),
folder_indexer, folder_indexer,
store_preferences, store_preferences,
@ -64,7 +57,7 @@ impl FolderDepsResolver {
} }
} }
fn folder_operation_handlers( pub fn folder_operation_handlers(
document_manager: Arc<DocumentManager>, document_manager: Arc<DocumentManager>,
database_manager: Arc<DatabaseManager>, database_manager: Arc<DatabaseManager>,
chat_manager: Arc<ChatManager>, chat_manager: Arc<ChatManager>,

View File

@ -59,4 +59,12 @@ impl UserWorkspaceService for UserWorkspaceServiceImpl {
.await?; .await?;
Ok(()) Ok(())
} }
fn did_delete_workspace(&self, workspace_id: String) -> FlowyResult<()> {
self
.folder_manager
.remove_indices_for_workspace(workspace_id)?;
Ok(())
}
} }

View File

@ -173,15 +173,20 @@ impl AppFlowyCore {
let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade( let folder_indexer = Arc::new(FolderIndexManagerImpl::new(Some(Arc::downgrade(
&authenticate_user, &authenticate_user,
)))); ))));
let folder_operation_handlers = folder_operation_handlers(
document_manager.clone(),
database_manager.clone(),
chat_manager.clone(),
);
let folder_manager = FolderDepsResolver::resolve( let folder_manager = FolderDepsResolver::resolve(
Arc::downgrade(&authenticate_user), Arc::downgrade(&authenticate_user),
&document_manager,
&database_manager,
collab_builder.clone(), collab_builder.clone(),
server_provider.clone(), server_provider.clone(),
folder_indexer.clone(), folder_indexer.clone(),
store_preference.clone(), store_preference.clone(),
&chat_manager, folder_operation_handlers,
) )
.await; .await;

View File

@ -232,8 +232,3 @@ pub struct UserFolderPB {
#[pb(index = 2)] #[pb(index = 2)]
pub workspace_id: String, pub workspace_id: String,
} }
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Eq, PartialEq)]
pub struct IndexedWorkspaceIds {
pub workspace_ids: Vec<String>,
}

View File

@ -1201,6 +1201,14 @@ impl FolderManager {
.filter(|id| !my_private_view_ids.contains(id)) .filter(|id| !my_private_view_ids.contains(id))
.collect() .collect()
} }
pub fn remove_indices_for_workspace(&self, workspace_id: String) -> FlowyResult<()> {
self
.folder_indexer
.remove_indices_for_workspace(workspace_id)?;
Ok(())
}
} }
/// Return the views that belong to the workspace. The views are filtered by the trash and all the private views. /// Return the views that belong to the workspace. The views are filtered by the trash and all the private views.

View File

@ -1,9 +1,8 @@
use crate::entities::IndexedWorkspaceIds;
use crate::manager::{FolderInitDataSource, FolderManager}; use crate::manager::{FolderInitDataSource, FolderManager};
use crate::manager_observer::*; use crate::manager_observer::*;
use crate::user_default::DefaultFolderBuilder; use crate::user_default::DefaultFolderBuilder;
use collab::core::collab::DataSource; use collab::core::collab::DataSource;
use collab_entity::CollabType; use collab_entity::{CollabType, EncodedCollab};
use collab_folder::{Folder, FolderNotify, UserId}; use collab_folder::{Folder, FolderNotify, UserId};
use collab_integrate::CollabKVDB; use collab_integrate::CollabKVDB;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
@ -11,8 +10,6 @@ use std::sync::{Arc, Weak};
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
use tracing::{event, info, Level}; use tracing::{event, info, Level};
pub const INDEXED_WORKSPACE_KEY: &str = "indexed-workspace-ids";
impl FolderManager { impl FolderManager {
/// Called immediately after the application launched if the user already sign in/sign up. /// Called immediately after the application launched if the user already sign in/sign up.
#[tracing::instrument(level = "info", skip(self, initial_data), err)] #[tracing::instrument(level = "info", skip(self, initial_data), err)]
@ -187,28 +184,27 @@ impl FolderManager {
} }
fn handle_index_folder(&self, workspace_id: String, folder: &Folder) { fn handle_index_folder(&self, workspace_id: String, folder: &Folder) {
let index_all; let mut index_all = true;
if self.folder_indexer.is_indexed() {
// If indexes already exist, we check if the workspace was let encoded_collab = self
// previously indexed, if it wasn't we index all .store_preferences
let indexed_ids = self .get_object::<EncodedCollab>(&workspace_id);
.store_preferences
.get_object::<IndexedWorkspaceIds>(INDEXED_WORKSPACE_KEY); if let Some(encoded_collab) = encoded_collab {
if let Some(indexed_ids) = indexed_ids { if let Ok(changes) = folder.calculate_view_changes(encoded_collab) {
index_all = !indexed_ids.workspace_ids.contains(&workspace_id.clone()); let folder_indexer = self.folder_indexer.clone();
if !index_all {
let mut workspace_ids = indexed_ids.workspace_ids.clone(); let views = folder.views.get_all_views();
workspace_ids.push(workspace_id.clone()); let wid = workspace_id.clone();
let _ = self
.store_preferences if !changes.is_empty() && !views.is_empty() {
.set_object(INDEXED_WORKSPACE_KEY, IndexedWorkspaceIds { workspace_ids }); spawn_blocking(move || {
// We index the changes
folder_indexer.index_view_changes(views, changes, wid);
});
index_all = false;
} }
} else {
index_all = true;
} }
} else {
// If there exists no indexes, we index all views in workspace
index_all = true;
} }
if index_all { if index_all {
@ -218,8 +214,24 @@ impl FolderManager {
// We spawn a blocking task to index all views in the folder // We spawn a blocking task to index all views in the folder
spawn_blocking(move || { spawn_blocking(move || {
// We remove old indexes just in case
let _ = folder_indexer.remove_indices_for_workspace(wid.clone());
// We index all views from the workspace
folder_indexer.index_all_views(views, wid); folder_indexer.index_all_views(views, wid);
}); });
} }
self.save_collab_to_preferences(folder);
}
fn save_collab_to_preferences(&self, folder: &Folder) {
let encoded_collab = folder.encode_collab_v1();
if let Ok(encoded) = encoded_collab {
let _ = self
.store_preferences
.set_object(&folder.get_workspace_id(), encoded);
}
} }
} }

View File

@ -2,7 +2,7 @@ use std::any::Any;
use std::sync::Arc; use std::sync::Arc;
use collab::core::collab::IndexContentReceiver; use collab::core::collab::IndexContentReceiver;
use collab_folder::{View, ViewIcon, ViewLayout}; use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewLayout};
use flowy_error::FlowyError; use flowy_error::FlowyError;
pub struct IndexableData { pub struct IndexableData {
@ -30,6 +30,7 @@ pub trait IndexManager: Send + Sync {
fn add_index(&self, data: IndexableData) -> Result<(), FlowyError>; fn add_index(&self, data: IndexableData) -> Result<(), FlowyError>;
fn update_index(&self, data: IndexableData) -> Result<(), FlowyError>; fn update_index(&self, data: IndexableData) -> Result<(), FlowyError>;
fn remove_indices(&self, ids: Vec<String>) -> Result<(), FlowyError>; fn remove_indices(&self, ids: Vec<String>) -> Result<(), FlowyError>;
fn remove_indices_for_workspace(&self, workspace_id: String) -> Result<(), FlowyError>;
fn is_indexed(&self) -> bool; fn is_indexed(&self) -> bool;
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
@ -37,4 +38,10 @@ pub trait IndexManager: Send + Sync {
pub trait FolderIndexManager: IndexManager { pub trait FolderIndexManager: IndexManager {
fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: String); fn index_all_views(&self, views: Vec<Arc<View>>, workspace_id: String);
fn index_view_changes(
&self,
views: Vec<Arc<View>>,
changes: Vec<FolderViewChange>,
workspace_id: String,
);
} }

View File

@ -15,15 +15,15 @@ use crate::{
}, },
}; };
use collab::core::collab::{IndexContent, IndexContentReceiver}; use collab::core::collab::{IndexContent, IndexContentReceiver};
use collab_folder::{View, ViewIcon, ViewIndexContent, ViewLayout}; use collab_folder::{folder_diff::FolderViewChange, View, ViewIcon, ViewIndexContent, ViewLayout};
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData}; use flowy_search_pub::entities::{FolderIndexManager, IndexManager, IndexableData};
use flowy_user::services::authenticate_user::AuthenticateUser; use flowy_user::services::authenticate_user::AuthenticateUser;
use lib_dispatch::prelude::af_spawn; use lib_dispatch::prelude::af_spawn;
use strsim::levenshtein; use strsim::levenshtein;
use tantivy::{ use tantivy::{
collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, Index, IndexReader, collector::TopDocs, directory::MmapDirectory, doc, query::QueryParser, schema::Field, Index,
IndexWriter, Term, IndexReader, IndexWriter, Term,
}; };
use super::entities::FolderIndexData; use super::entities::FolderIndexData;
@ -40,7 +40,6 @@ const FOLDER_INDEX_DIR: &str = "folder_index";
impl FolderIndexManagerImpl { impl FolderIndexManagerImpl {
pub fn new(auth_user: Option<Weak<AuthenticateUser>>) -> Self { pub fn new(auth_user: Option<Weak<AuthenticateUser>>) -> Self {
// TODO(Mathias): Temporarily disable seaerch
let auth_user = match auth_user { let auth_user = match auth_user {
Some(auth_user) => auth_user, Some(auth_user) => auth_user,
None => { None => {
@ -74,52 +73,45 @@ impl FolderIndexManagerImpl {
} }
} }
// We open the existing or newly created folder_index directory
// This is required by the Tantivy Index, as it will use it to store
// and read index data
let dir = MmapDirectory::open(index_path);
if let Err(e) = dir {
tracing::error!("FolderIndexManager failed to open index directory: {:?}", e);
return FolderIndexManagerImpl::empty();
}
// The folder schema is used to define the fields of the index along // The folder schema is used to define the fields of the index along
// with how they are stored and if the field is indexed // with how they are stored and if the field is indexed
let folder_schema = FolderSchema::new(); let folder_schema = FolderSchema::new();
// We open or create an index that takes the directory r/w and the schema. // We open the existing or newly created folder_index directory
let index_res = Index::open_or_create(dir.unwrap(), folder_schema.schema.clone()); // This is required by the Tantivy Index, as it will use it to store
if let Err(e) = index_res { // and read index data
tracing::error!("FolderIndexManager failed to open index: {:?}", e); let index = match MmapDirectory::open(index_path) {
return FolderIndexManagerImpl::empty(); // We open or create an index that takes the directory r/w and the schema.
} Ok(dir) => match Index::open_or_create(dir, folder_schema.schema.clone()) {
Ok(index) => index,
Err(e) => {
tracing::error!("FolderIndexManager failed to open index: {:?}", e);
return FolderIndexManagerImpl::empty();
},
},
Err(e) => {
tracing::error!("FolderIndexManager failed to open index directory: {:?}", e);
return FolderIndexManagerImpl::empty();
},
};
let index = index_res.unwrap(); // We only need one IndexReader per index
// We read the index reader, we only need one IndexReader per index
let index_reader = index.reader(); let index_reader = index.reader();
if let Err(e) = index_reader {
tracing::error!(
"FolderIndexManager failed to instantiate index reader: {:?}",
e
);
return FolderIndexManagerImpl::empty();
}
let index_writer = index.writer(50_000_000); let index_writer = index.writer(50_000_000);
if let Err(e) = index_writer {
tracing::error!( let (index_reader, index_writer) = match (index_reader, index_writer) {
"FolderIndexManager failed to instantiate index writer: {:?}", (Ok(reader), Ok(writer)) => (reader, writer),
e _ => {
); tracing::error!("FolderIndexManager failed to instantiate index writer and/or reader");
return FolderIndexManagerImpl::empty(); return FolderIndexManagerImpl::empty();
} },
};
Self { Self {
folder_schema: Some(folder_schema), folder_schema: Some(folder_schema),
index: Some(index), index: Some(index),
index_reader: Some(index_reader.unwrap()), index_reader: Some(index_reader),
index_writer: Some(Arc::new(Mutex::new(index_writer.unwrap()))), index_writer: Some(Arc::new(Mutex::new(index_writer))),
} }
} }
@ -129,7 +121,6 @@ impl FolderIndexManagerImpl {
} }
let mut index_writer = self.get_index_writer()?; let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?; let folder_schema = self.get_folder_schema()?;
let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?; let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?;
@ -223,15 +214,11 @@ impl FolderIndexManagerImpl {
) -> Result<Vec<SearchResultPB>, FlowyError> { ) -> Result<Vec<SearchResultPB>, FlowyError> {
let folder_schema = self.get_folder_schema()?; let folder_schema = self.get_folder_schema()?;
let index = match &self.index { let (index, index_reader) = self
Some(index) => index, .index
None => return Err(FlowyError::folder_index_manager_unavailable()), .as_ref()
}; .zip(self.index_reader.as_ref())
.ok_or_else(FlowyError::folder_index_manager_unavailable)?;
let index_reader = match &self.index_reader {
Some(index_reader) => index_reader,
None => return Err(FlowyError::folder_index_manager_unavailable()),
};
let title_field = folder_schema.schema.get_field(FOLDER_TITLE_FIELD_NAME)?; let title_field = folder_schema.schema.get_field(FOLDER_TITLE_FIELD_NAME)?;
@ -272,6 +259,29 @@ impl FolderIndexManagerImpl {
let distance = levenshtein(query, term) as f64; let distance = levenshtein(query, term) as f64;
1.0 / (distance + 1.0) 1.0 / (distance + 1.0)
} }
fn get_schema_fields(&self) -> Result<(Field, Field, Field, Field, Field), FlowyError> {
let folder_schema = match self.folder_schema.clone() {
Some(schema) => schema,
_ => return Err(FlowyError::folder_index_manager_unavailable()),
};
let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?;
let title_field = folder_schema.schema.get_field(FOLDER_TITLE_FIELD_NAME)?;
let icon_field = folder_schema.schema.get_field(FOLDER_ICON_FIELD_NAME)?;
let icon_ty_field = folder_schema.schema.get_field(FOLDER_ICON_TY_FIELD_NAME)?;
let workspace_id_field = folder_schema
.schema
.get_field(FOLDER_WORKSPACE_ID_FIELD_NAME)?;
Ok((
id_field,
title_field,
icon_field,
icon_ty_field,
workspace_id_field,
))
}
} }
impl IndexManager for FolderIndexManagerImpl { impl IndexManager for FolderIndexManagerImpl {
@ -288,9 +298,12 @@ impl IndexManager for FolderIndexManagerImpl {
let wid = workspace_id.clone(); let wid = workspace_id.clone();
af_spawn(async move { af_spawn(async move {
while let Ok(msg) = rx.recv().await { while let Ok(msg) = rx.recv().await {
tracing::warn!("[Indexer] Message received: {:?}", msg);
match msg { match msg {
IndexContent::Create(value) => match serde_json::from_value::<ViewIndexContent>(value) { IndexContent::Create(value) => match serde_json::from_value::<ViewIndexContent>(value) {
Ok(view) => { Ok(view) => {
tracing::warn!("[Indexer] CREATE: {:?}", view);
let _ = indexer.add_index(IndexableData { let _ = indexer.add_index(IndexableData {
id: view.id, id: view.id,
data: view.name, data: view.name,
@ -303,6 +316,7 @@ impl IndexManager for FolderIndexManagerImpl {
}, },
IndexContent::Update(value) => match serde_json::from_value::<ViewIndexContent>(value) { IndexContent::Update(value) => match serde_json::from_value::<ViewIndexContent>(value) {
Ok(view) => { Ok(view) => {
tracing::warn!("[Indexer] UPDATE: {:?}", view);
let _ = indexer.update_index(IndexableData { let _ = indexer.update_index(IndexableData {
id: view.id, id: view.id,
data: view.name, data: view.name,
@ -314,6 +328,7 @@ impl IndexManager for FolderIndexManagerImpl {
Err(err) => tracing::error!("FolderIndexManager error deserialize: {:?}", err), Err(err) => tracing::error!("FolderIndexManager error deserialize: {:?}", err),
}, },
IndexContent::Delete(ids) => { IndexContent::Delete(ids) => {
tracing::warn!("[Indexer] DELETE: {:?}", ids);
if let Err(e) = indexer.remove_indices(ids) { if let Err(e) = indexer.remove_indices(ids) {
tracing::error!("FolderIndexManager error deserialize: {:?}", e); tracing::error!("FolderIndexManager error deserialize: {:?}", e);
} }
@ -326,15 +341,8 @@ impl IndexManager for FolderIndexManagerImpl {
fn update_index(&self, data: IndexableData) -> Result<(), FlowyError> { fn update_index(&self, data: IndexableData) -> Result<(), FlowyError> {
let mut index_writer = self.get_index_writer()?; let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?; let (id_field, title_field, icon_field, icon_ty_field, workspace_id_field) =
let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?; self.get_schema_fields()?;
let title_field = folder_schema.schema.get_field(FOLDER_TITLE_FIELD_NAME)?;
let icon_field = folder_schema.schema.get_field(FOLDER_ICON_FIELD_NAME)?;
let icon_ty_field: tantivy::schema::Field =
folder_schema.schema.get_field(FOLDER_ICON_TY_FIELD_NAME)?;
let workspace_id_field = folder_schema
.schema
.get_field(FOLDER_WORKSPACE_ID_FIELD_NAME)?;
let delete_term = Term::from_field_text(id_field, &data.id.clone()); let delete_term = Term::from_field_text(id_field, &data.id.clone());
@ -361,7 +369,6 @@ impl IndexManager for FolderIndexManagerImpl {
let mut index_writer = self.get_index_writer()?; let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?; let folder_schema = self.get_folder_schema()?;
let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?; let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?;
for id in ids { for id in ids {
let delete_term = Term::from_field_text(id_field, &id); let delete_term = Term::from_field_text(id_field, &id);
@ -376,15 +383,8 @@ impl IndexManager for FolderIndexManagerImpl {
fn add_index(&self, data: IndexableData) -> Result<(), FlowyError> { fn add_index(&self, data: IndexableData) -> Result<(), FlowyError> {
let mut index_writer = self.get_index_writer()?; let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?; let (id_field, title_field, icon_field, icon_ty_field, workspace_id_field) =
self.get_schema_fields()?;
let id_field = folder_schema.schema.get_field(FOLDER_ID_FIELD_NAME)?;
let title_field = folder_schema.schema.get_field(FOLDER_TITLE_FIELD_NAME)?;
let icon_field = folder_schema.schema.get_field(FOLDER_ICON_FIELD_NAME)?;
let icon_ty_field = folder_schema.schema.get_field(FOLDER_ICON_TY_FIELD_NAME)?;
let workspace_id_field = folder_schema
.schema
.get_field(FOLDER_WORKSPACE_ID_FIELD_NAME)?;
let (icon, icon_ty) = self.extract_icon(data.icon, data.layout); let (icon, icon_ty) = self.extract_icon(data.icon, data.layout);
@ -402,6 +402,24 @@ impl IndexManager for FolderIndexManagerImpl {
Ok(()) Ok(())
} }
/// Removes all indexes that are related by workspace id. This is useful
/// for cleaning indexes when eg. removing/leaving a workspace.
///
fn remove_indices_for_workspace(&self, workspace_id: String) -> Result<(), FlowyError> {
let mut index_writer = self.get_index_writer()?;
let folder_schema = self.get_folder_schema()?;
let id_field = folder_schema
.schema
.get_field(FOLDER_WORKSPACE_ID_FIELD_NAME)?;
let delete_term = Term::from_field_text(id_field, &workspace_id);
index_writer.delete_term(delete_term);
index_writer.commit()?;
Ok(())
}
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
} }
@ -416,4 +434,35 @@ impl FolderIndexManager for FolderIndexManagerImpl {
let _ = self.index_all(indexable_data); let _ = self.index_all(indexable_data);
} }
fn index_view_changes(
&self,
views: Vec<Arc<View>>,
changes: Vec<FolderViewChange>,
workspace_id: String,
) {
let mut views_iter = views.into_iter();
for change in changes {
match change {
FolderViewChange::Inserted { view_id } => {
let view = views_iter.find(|view| view.id == view_id);
if let Some(view) = view {
let indexable_data = IndexableData::from_view(view, workspace_id.clone());
let _ = self.add_index(indexable_data);
}
},
FolderViewChange::Updated { view_id } => {
let view = views_iter.find(|view| view.id == view_id);
if let Some(view) = view {
let indexable_data = IndexableData::from_view(view, workspace_id.clone());
let _ = self.update_index(indexable_data);
}
},
FolderViewChange::Deleted { view_ids } => {
tracing::warn!("[Indexer] ViewChange Reached Deleted: {:?}", view_ids);
let _ = self.remove_indices(view_ids);
},
};
}
}
} }

View File

@ -68,10 +68,10 @@ diesel::table! {
} }
diesel::allow_tables_to_appear_in_same_query!( diesel::allow_tables_to_appear_in_same_query!(
chat_message_table, chat_message_table,
chat_table, chat_table,
collab_snapshot, collab_snapshot,
user_data_migration_records, user_data_migration_records,
user_table, user_table,
user_workspace_table, user_workspace_table,
); );

View File

@ -10,4 +10,7 @@ pub trait UserWorkspaceService: Send + Sync {
&self, &self,
ids_by_database_id: HashMap<String, Vec<String>>, ids_by_database_id: HashMap<String, Vec<String>>,
) -> FlowyResult<()>; ) -> FlowyResult<()>;
/// Removes local indexes when a workspace is left/deleted
fn did_delete_workspace(&self, workspace_id: String) -> FlowyResult<()>;
} }

View File

@ -261,7 +261,11 @@ impl UserManager {
// delete workspace from local sqlite db // delete workspace from local sqlite db
let uid = self.user_id()?; let uid = self.user_id()?;
let conn = self.db_connection(uid)?; let conn = self.db_connection(uid)?;
delete_user_workspaces(conn, workspace_id) delete_user_workspaces(conn, workspace_id)?;
self
.user_workspace_service
.did_delete_workspace(workspace_id.to_string())
} }
#[instrument(level = "info", skip(self), err)] #[instrument(level = "info", skip(self), err)]
@ -275,6 +279,11 @@ impl UserManager {
let uid = self.user_id()?; let uid = self.user_id()?;
let conn = self.db_connection(uid)?; let conn = self.db_connection(uid)?;
delete_user_workspaces(conn, workspace_id)?; delete_user_workspaces(conn, workspace_id)?;
self
.user_workspace_service
.did_delete_workspace(workspace_id.to_string())?;
Ok(()) Ok(())
} }