mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
6e7d044208
commit
bd7977d8ba
@ -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(),
|
||||||
|
@ -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" }
|
||||||
|
@ -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" }
|
||||||
|
1
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
1
frontend/appflowy_web_app/src-tauri/Cargo.lock
generated
@ -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",
|
||||||
|
@ -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" }
|
||||||
|
@ -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>,
|
||||||
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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>,
|
|
||||||
}
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
);
|
);
|
||||||
|
@ -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<()>;
|
||||||
}
|
}
|
||||||
|
@ -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(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user