mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: cache filter result
This commit is contained in:
@ -9,7 +9,7 @@ use flowy_grid_data_model::entities::{
|
||||
CellChangeset, GridRowsChangeset, IndexRowOrder, Row, RowOrder, UpdatedRowOrder,
|
||||
};
|
||||
use flowy_grid_data_model::revision::{
|
||||
CellRevision, GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
|
||||
GridBlockMetaRevision, GridBlockMetaRevisionChangeset, RowMetaChangeset, RowRevision,
|
||||
};
|
||||
use flowy_revision::disk::SQLiteGridBlockMetaRevisionPersistence;
|
||||
use flowy_revision::{RevisionManager, RevisionPersistence};
|
||||
@ -23,7 +23,7 @@ pub(crate) struct GridBlockManager {
|
||||
grid_id: String,
|
||||
user: Arc<dyn GridUser>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
block_editor_map: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
|
||||
block_editors: DashMap<BlockId, Arc<GridBlockRevisionEditor>>,
|
||||
}
|
||||
|
||||
impl GridBlockManager {
|
||||
@ -33,13 +33,13 @@ impl GridBlockManager {
|
||||
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
||||
persistence: Arc<BlockIndexCache>,
|
||||
) -> FlowyResult<Self> {
|
||||
let editor_map = make_block_meta_editor_map(user, block_meta_revs).await?;
|
||||
let block_editors = make_block_editors(user, block_meta_revs).await?;
|
||||
let user = user.clone();
|
||||
let grid_id = grid_id.to_owned();
|
||||
let manager = Self {
|
||||
grid_id,
|
||||
user,
|
||||
block_editor_map: editor_map,
|
||||
block_editors,
|
||||
persistence,
|
||||
};
|
||||
Ok(manager)
|
||||
@ -48,11 +48,11 @@ impl GridBlockManager {
|
||||
// #[tracing::instrument(level = "trace", skip(self))]
|
||||
pub(crate) async fn get_editor(&self, block_id: &str) -> FlowyResult<Arc<GridBlockRevisionEditor>> {
|
||||
debug_assert!(!block_id.is_empty());
|
||||
match self.block_editor_map.get(block_id) {
|
||||
match self.block_editors.get(block_id) {
|
||||
None => {
|
||||
tracing::error!("This is a fatal error, block with id:{} is not exist", block_id);
|
||||
let editor = Arc::new(make_block_meta_editor(&self.user, block_id).await?);
|
||||
self.block_editor_map.insert(block_id.to_owned(), editor.clone());
|
||||
let editor = Arc::new(make_block_editor(&self.user, block_id).await?);
|
||||
self.block_editors.insert(block_id.to_owned(), editor.clone());
|
||||
Ok(editor)
|
||||
}
|
||||
Some(editor) => Ok(editor.clone()),
|
||||
@ -222,33 +222,31 @@ impl GridBlockManager {
|
||||
editor.get_row_orders::<&str>(None).await
|
||||
}
|
||||
|
||||
pub(crate) async fn make_block_snapshots(&self, block_ids: Vec<String>) -> FlowyResult<Vec<GridBlockSnapshot>> {
|
||||
pub(crate) async fn get_block_snapshots(
|
||||
&self,
|
||||
block_ids: Option<Vec<String>>,
|
||||
) -> FlowyResult<Vec<GridBlockSnapshot>> {
|
||||
let mut snapshots = vec![];
|
||||
for block_id in block_ids {
|
||||
let editor = self.get_editor(&block_id).await?;
|
||||
let row_revs = editor.get_row_revs::<&str>(None).await?;
|
||||
snapshots.push(GridBlockSnapshot { block_id, row_revs });
|
||||
match block_ids {
|
||||
None => {
|
||||
for iter in self.block_editors.iter() {
|
||||
let editor = iter.value();
|
||||
let block_id = editor.block_id.clone();
|
||||
let row_revs = editor.get_row_revs::<&str>(None).await?;
|
||||
snapshots.push(GridBlockSnapshot { block_id, row_revs });
|
||||
}
|
||||
}
|
||||
Some(block_ids) => {
|
||||
for block_id in block_ids {
|
||||
let editor = self.get_editor(&block_id).await?;
|
||||
let row_revs = editor.get_row_revs::<&str>(None).await?;
|
||||
snapshots.push(GridBlockSnapshot { block_id, row_revs });
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(snapshots)
|
||||
}
|
||||
|
||||
// Optimization: Using the shared memory(Arc, Cow,etc.) to reduce memory usage.
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_cell_revs(
|
||||
&self,
|
||||
block_ids: Vec<String>,
|
||||
field_id: &str,
|
||||
row_ids: Option<Vec<Cow<'_, String>>>,
|
||||
) -> FlowyResult<Vec<CellRevision>> {
|
||||
let mut block_cell_revs = vec![];
|
||||
for block_id in block_ids {
|
||||
let editor = self.get_editor(&block_id).await?;
|
||||
let cell_revs = editor.get_cell_revs(field_id, row_ids.clone()).await?;
|
||||
block_cell_revs.extend(cell_revs);
|
||||
}
|
||||
Ok(block_cell_revs)
|
||||
}
|
||||
|
||||
async fn notify_did_update_block(&self, block_id: &str, changeset: GridRowsChangeset) -> FlowyResult<()> {
|
||||
send_dart_notification(block_id, GridNotification::DidUpdateGridBlock)
|
||||
.payload(changeset)
|
||||
@ -263,20 +261,20 @@ impl GridBlockManager {
|
||||
}
|
||||
}
|
||||
|
||||
async fn make_block_meta_editor_map(
|
||||
async fn make_block_editors(
|
||||
user: &Arc<dyn GridUser>,
|
||||
block_meta_revs: Vec<Arc<GridBlockMetaRevision>>,
|
||||
) -> FlowyResult<DashMap<String, Arc<GridBlockRevisionEditor>>> {
|
||||
let editor_map = DashMap::new();
|
||||
for block_meta_rev in block_meta_revs {
|
||||
let editor = make_block_meta_editor(user, &block_meta_rev.block_id).await?;
|
||||
let editor = make_block_editor(user, &block_meta_rev.block_id).await?;
|
||||
editor_map.insert(block_meta_rev.block_id.clone(), Arc::new(editor));
|
||||
}
|
||||
|
||||
Ok(editor_map)
|
||||
}
|
||||
|
||||
async fn make_block_meta_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> {
|
||||
async fn make_block_editor(user: &Arc<dyn GridUser>, block_id: &str) -> FlowyResult<GridBlockRevisionEditor> {
|
||||
tracing::trace!("Open block:{} meta editor", block_id);
|
||||
let token = user.token()?;
|
||||
let user_id = user.user_id()?;
|
||||
|
@ -4,7 +4,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use flowy_grid_data_model::entities::{FieldType, GridCheckboxFilter};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -40,7 +40,7 @@ impl_type_option!(CheckboxTypeOption, FieldType::Checkbox);
|
||||
const YES: &str = "Yes";
|
||||
const NO: &str = "No";
|
||||
|
||||
impl CellDataOperation<String> for CheckboxTypeOption {
|
||||
impl CellDataOperation<String, GridCheckboxFilter> for CheckboxTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -62,6 +62,10 @@ impl CellDataOperation<String> for CheckboxTypeOption {
|
||||
Ok(DecodedCellData::default())
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridCheckboxFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
|
@ -7,7 +7,7 @@ use chrono::format::strftime::StrftimeItems;
|
||||
use chrono::{NaiveDateTime, Timelike};
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{CellChangeset, FieldType};
|
||||
use flowy_grid_data_model::entities::{CellChangeset, FieldType, GridDateFilter};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::EnumIter;
|
||||
@ -115,7 +115,7 @@ impl DateTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation<String> for DateTypeOption {
|
||||
impl CellDataOperation<String, GridDateFilter> for DateTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -138,6 +138,10 @@ impl CellDataOperation<String> for DateTypeOption {
|
||||
DecodedCellData::try_from_bytes(date)
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridDateFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
|
@ -6,7 +6,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use flowy_grid_data_model::entities::{FieldType, GridNumberFilter};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use rust_decimal::Decimal;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -139,7 +139,7 @@ impl NumberTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation<String> for NumberTypeOption {
|
||||
impl CellDataOperation<String, GridNumberFilter> for NumberTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -179,6 +179,10 @@ impl CellDataOperation<String> for NumberTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridNumberFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
|
@ -6,7 +6,7 @@ use crate::services::row::{CellContentChangeset, CellDataOperation, DecodedCellD
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::{CellChangeset, FieldType};
|
||||
use flowy_grid_data_model::entities::{CellChangeset, FieldType, GridSelectOptionFilter};
|
||||
use flowy_grid_data_model::parser::NotEmptyStr;
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use nanoid::nanoid;
|
||||
@ -94,7 +94,7 @@ impl SelectOptionOperation for SingleSelectTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation<String> for SingleSelectTypeOption {
|
||||
impl CellDataOperation<String, GridSelectOptionFilter> for SingleSelectTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -122,6 +122,10 @@ impl CellDataOperation<String> for SingleSelectTypeOption {
|
||||
DecodedCellData::try_from_bytes(cell_data)
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
@ -192,7 +196,7 @@ impl SelectOptionOperation for MultiSelectTypeOption {
|
||||
}
|
||||
}
|
||||
|
||||
impl CellDataOperation<String> for MultiSelectTypeOption {
|
||||
impl CellDataOperation<String, GridSelectOptionFilter> for MultiSelectTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -220,6 +224,10 @@ impl CellDataOperation<String> for MultiSelectTypeOption {
|
||||
DecodedCellData::try_from_bytes(cell_data)
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridSelectOptionFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<T>(&self, changeset: T, cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
T: Into<CellContentChangeset>,
|
||||
|
@ -4,7 +4,7 @@ use crate::services::row::{try_decode_cell_data, CellContentChangeset, CellDataO
|
||||
use bytes::Bytes;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use flowy_grid_data_model::entities::{FieldType, GridTextFilter};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -30,7 +30,7 @@ pub struct RichTextTypeOption {
|
||||
}
|
||||
impl_type_option!(RichTextTypeOption, FieldType::RichText);
|
||||
|
||||
impl CellDataOperation<String> for RichTextTypeOption {
|
||||
impl CellDataOperation<String, GridTextFilter> for RichTextTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -45,13 +45,17 @@ impl CellDataOperation<String> for RichTextTypeOption {
|
||||
|| decoded_field_type.is_multi_select()
|
||||
|| decoded_field_type.is_number()
|
||||
{
|
||||
try_decode_cell_data(encoded_data, field_rev, decoded_field_type, decoded_field_type)
|
||||
try_decode_cell_data(encoded_data.into(), field_rev, decoded_field_type, decoded_field_type)
|
||||
} else {
|
||||
let cell_data = encoded_data.into();
|
||||
Ok(DecodedCellData::new(cell_data))
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
|
@ -5,7 +5,7 @@ use bytes::Bytes;
|
||||
use fancy_regex::Regex;
|
||||
use flowy_derive::ProtoBuf;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use flowy_grid_data_model::entities::{FieldType, GridTextFilter};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, TypeOptionDataDeserializer, TypeOptionDataEntry};
|
||||
use lazy_static::lazy_static;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -33,7 +33,7 @@ pub struct URLTypeOption {
|
||||
}
|
||||
impl_type_option!(URLTypeOption, FieldType::URL);
|
||||
|
||||
impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
|
||||
impl CellDataOperation<EncodedCellData<URLCellData>, GridTextFilter> for URLTypeOption {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -50,6 +50,10 @@ impl CellDataOperation<EncodedCellData<URLCellData>> for URLTypeOption {
|
||||
DecodedCellData::try_from_bytes(cell_data)
|
||||
}
|
||||
|
||||
fn apply_filter(&self, _filter: GridTextFilter) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn apply_changeset<C>(&self, changeset: C, _cell_rev: Option<CellRevision>) -> Result<String, FlowyError>
|
||||
where
|
||||
C: Into<CellContentChangeset>,
|
||||
|
@ -1,10 +1,15 @@
|
||||
use crate::manager::GridTaskSchedulerRwLock;
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::tasks::Task;
|
||||
use flowy_error::FlowyResult;
|
||||
|
||||
use crate::services::grid_editor_task::GridServiceTaskScheduler;
|
||||
use crate::services::row::GridBlockSnapshot;
|
||||
use crate::services::tasks::{FilterTaskContext, Task, TaskContent};
|
||||
use flowy_error::FlowyResult;
|
||||
use flowy_grid_data_model::entities::{
|
||||
FieldType, GridCheckboxFilter, GridDateFilter, GridNumberFilter, GridSelectOptionFilter,
|
||||
GridSettingChangesetParams, GridTextFilter,
|
||||
};
|
||||
use flowy_grid_data_model::revision::{FieldRevision, RowRevision};
|
||||
use flowy_sync::client_grid::GridRevisionPad;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@ -12,47 +17,231 @@ pub(crate) struct GridFilterService {
|
||||
scheduler: Arc<dyn GridServiceTaskScheduler>,
|
||||
grid_pad: Arc<RwLock<GridRevisionPad>>,
|
||||
block_manager: Arc<GridBlockManager>,
|
||||
filter_cache: Arc<RwLock<FilterCache>>,
|
||||
filter_result: Arc<RwLock<GridFilterResult>>,
|
||||
}
|
||||
impl GridFilterService {
|
||||
pub fn new<S: GridServiceTaskScheduler>(
|
||||
pub async fn new<S: GridServiceTaskScheduler>(
|
||||
grid_pad: Arc<RwLock<GridRevisionPad>>,
|
||||
block_manager: Arc<GridBlockManager>,
|
||||
scheduler: S,
|
||||
) -> Self {
|
||||
let filter_cache = Arc::new(RwLock::new(FilterCache::from_grid_pad(&grid_pad).await));
|
||||
let filter_result = Arc::new(RwLock::new(GridFilterResult::default()));
|
||||
Self {
|
||||
grid_pad,
|
||||
block_manager,
|
||||
scheduler: Arc::new(scheduler),
|
||||
filter_cache,
|
||||
filter_result,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn process_task(&self, _task: Task) -> FlowyResult<()> {
|
||||
pub async fn process(&self, task_context: FilterTaskContext) -> FlowyResult<()> {
|
||||
let mut filter_result = self.filter_result.write().await;
|
||||
for block in task_context.blocks {
|
||||
for row_rev in block.row_revs {
|
||||
let row_filter_result = RowFilterResult::new(&row_rev);
|
||||
|
||||
filter_result.insert(&row_rev.id, row_filter_result);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn notify_changed(&self) {
|
||||
let task_id = self.scheduler.gen_task_id().await;
|
||||
pub async fn apply_changeset(&self, changeset: GridFilterChangeset) {
|
||||
if !changeset.is_changed() {
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// let grid_pad = self.grid_pad.read().await;
|
||||
// match grid_pad.get_filters(None) {
|
||||
// None => {}
|
||||
// Some(filter_revs) => {
|
||||
// filter_revs
|
||||
// .iter()
|
||||
// .for_each(|filter_rev| match grid_pad.get_field_rev(&filter_rev.field_id) {
|
||||
// None => {}
|
||||
// Some((_, _field_rev)) => match field_rev.field_type {
|
||||
// FieldType::RichText => {}
|
||||
// FieldType::Number => {}
|
||||
// FieldType::DateTime => {}
|
||||
// FieldType::SingleSelect => {}
|
||||
// FieldType::MultiSelect => {}
|
||||
// FieldType::Checkbox => {}
|
||||
// FieldType::URL => {}
|
||||
// },
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
if let Some(filter_id) = &changeset.insert_filter {
|
||||
let mut cache = self.filter_cache.write().await;
|
||||
let field_ids = Some(vec![filter_id.field_id.clone()]);
|
||||
reload_filter_cache(&mut cache, field_ids, &self.grid_pad).await;
|
||||
}
|
||||
|
||||
if let Some(filter_id) = &changeset.delete_filter {
|
||||
self.filter_cache.write().await.remove(filter_id);
|
||||
}
|
||||
|
||||
match self.block_manager.get_block_snapshots(None).await {
|
||||
Ok(blocks) => {
|
||||
let task = self.gen_task(blocks).await;
|
||||
let _ = self.scheduler.register_task(task).await;
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
async fn gen_task(&self, blocks: Vec<GridBlockSnapshot>) -> Task {
|
||||
let task_id = self.scheduler.gen_task_id().await;
|
||||
let handler_id = self.grid_pad.read().await.grid_id();
|
||||
|
||||
let context = FilterTaskContext { blocks };
|
||||
let task = Task {
|
||||
handler_id,
|
||||
id: task_id,
|
||||
content: TaskContent::Filter(context),
|
||||
};
|
||||
|
||||
task
|
||||
}
|
||||
}
|
||||
pub struct GridFilterChangeset {
|
||||
insert_filter: Option<FilterId>,
|
||||
delete_filter: Option<FilterId>,
|
||||
}
|
||||
|
||||
impl GridFilterChangeset {
|
||||
fn is_changed(&self) -> bool {
|
||||
self.insert_filter.is_some() || self.delete_filter.is_some()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<&GridSettingChangesetParams> for GridFilterChangeset {
|
||||
fn from(params: &GridSettingChangesetParams) -> Self {
|
||||
let insert_filter = params.insert_filter.as_ref().map(|insert_filter_params| FilterId {
|
||||
field_id: insert_filter_params.field_id.clone(),
|
||||
field_type: insert_filter_params.field_type.clone(),
|
||||
});
|
||||
|
||||
let delete_filter = params.delete_filter.as_ref().map(|delete_filter_params| FilterId {
|
||||
field_id: delete_filter_params.filter_id.clone(),
|
||||
field_type: delete_filter_params.field_type.clone(),
|
||||
});
|
||||
GridFilterChangeset {
|
||||
insert_filter,
|
||||
delete_filter,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct GridFilterResult {
|
||||
rows: HashMap<String, RowFilterResult>,
|
||||
}
|
||||
|
||||
impl GridFilterResult {
|
||||
fn insert(&mut self, row_id: &str, result: RowFilterResult) {
|
||||
self.rows.insert(row_id.to_owned(), result);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct RowFilterResult {
|
||||
cell_by_field_id: HashMap<String, bool>,
|
||||
}
|
||||
|
||||
impl RowFilterResult {
|
||||
fn new(row_rev: &RowRevision) -> Self {
|
||||
Self {
|
||||
cell_by_field_id: row_rev.cells.iter().map(|(k, _)| (k.clone(), true)).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn update_cell(&mut self, cell_id: &str, exist: bool) {
|
||||
self.cell_by_field_id.insert(cell_id.to_owned(), exist);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct FilterCache {
|
||||
text_filter: HashMap<FilterId, GridTextFilter>,
|
||||
url_filter: HashMap<FilterId, GridTextFilter>,
|
||||
number_filter: HashMap<FilterId, GridNumberFilter>,
|
||||
date_filter: HashMap<FilterId, GridDateFilter>,
|
||||
select_option_filter: HashMap<FilterId, GridSelectOptionFilter>,
|
||||
checkbox_filter: HashMap<FilterId, GridCheckboxFilter>,
|
||||
}
|
||||
|
||||
impl FilterCache {
|
||||
async fn from_grid_pad(grid_pad: &Arc<RwLock<GridRevisionPad>>) -> Self {
|
||||
let mut this = Self::default();
|
||||
reload_filter_cache(&mut this, None, grid_pad);
|
||||
this
|
||||
}
|
||||
|
||||
fn remove(&mut self, filter_id: &FilterId) {
|
||||
let _ = match filter_id.field_type {
|
||||
FieldType::RichText => {
|
||||
let _ = self.text_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::Number => {
|
||||
let _ = self.number_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::DateTime => {
|
||||
let _ = self.date_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::SingleSelect => {
|
||||
let _ = self.select_option_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::MultiSelect => {
|
||||
let _ = self.select_option_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::Checkbox => {
|
||||
let _ = self.checkbox_filter.remove(filter_id);
|
||||
}
|
||||
FieldType::URL => {
|
||||
let _ = self.url_filter.remove(filter_id);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async fn reload_filter_cache(
|
||||
cache: &mut FilterCache,
|
||||
field_ids: Option<Vec<String>>,
|
||||
grid_pad: &Arc<RwLock<GridRevisionPad>>,
|
||||
) {
|
||||
let grid_pad = grid_pad.read().await;
|
||||
let filters_revs = grid_pad.get_filters(None, field_ids).unwrap_or_default();
|
||||
|
||||
for filter_rev in filters_revs {
|
||||
match grid_pad.get_field_rev(&filter_rev.field_id) {
|
||||
None => {}
|
||||
Some((_, field_rev)) => {
|
||||
let filter_id = FilterId::from(field_rev);
|
||||
match &field_rev.field_type {
|
||||
FieldType::RichText => {
|
||||
let _ = cache.text_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
||||
}
|
||||
FieldType::Number => {
|
||||
let _ = cache
|
||||
.number_filter
|
||||
.insert(filter_id, GridNumberFilter::from(filter_rev));
|
||||
}
|
||||
FieldType::DateTime => {
|
||||
let _ = cache.date_filter.insert(filter_id, GridDateFilter::from(filter_rev));
|
||||
}
|
||||
FieldType::SingleSelect | FieldType::MultiSelect => {
|
||||
let _ = cache
|
||||
.select_option_filter
|
||||
.insert(filter_id, GridSelectOptionFilter::from(filter_rev));
|
||||
}
|
||||
FieldType::Checkbox => {
|
||||
let _ = cache
|
||||
.checkbox_filter
|
||||
.insert(filter_id, GridCheckboxFilter::from(filter_rev));
|
||||
}
|
||||
FieldType::URL => {
|
||||
let _ = cache.url_filter.insert(filter_id, GridTextFilter::from(filter_rev));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct FilterId {
|
||||
field_id: String,
|
||||
field_type: FieldType,
|
||||
}
|
||||
|
||||
impl std::convert::From<&FieldRevision> for FilterId {
|
||||
fn from(rev: &FieldRevision) -> Self {
|
||||
Self {
|
||||
field_id: rev.id.clone(),
|
||||
field_type: rev.field_type.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ use crate::entities::CellIdentifier;
|
||||
use crate::manager::{GridTaskSchedulerRwLock, GridUser};
|
||||
use crate::services::block_manager::GridBlockManager;
|
||||
use crate::services::field::{default_type_option_builder_from_type, type_option_builder_from_bytes, FieldBuilder};
|
||||
use crate::services::filter::GridFilterService;
|
||||
use crate::services::filter::{GridFilterChangeset, GridFilterService};
|
||||
use crate::services::persistence::block_index::BlockIndexCache;
|
||||
use crate::services::row::*;
|
||||
|
||||
@ -53,11 +53,8 @@ impl GridRevisionEditor {
|
||||
let grid_pad = Arc::new(RwLock::new(grid_pad));
|
||||
let block_meta_revs = grid_pad.read().await.get_block_meta_revs();
|
||||
let block_manager = Arc::new(GridBlockManager::new(grid_id, &user, block_meta_revs, persistence).await?);
|
||||
let filter_service = Arc::new(GridFilterService::new(
|
||||
grid_pad.clone(),
|
||||
block_manager.clone(),
|
||||
task_scheduler.clone(),
|
||||
));
|
||||
let filter_service =
|
||||
Arc::new(GridFilterService::new(grid_pad.clone(), block_manager.clone(), task_scheduler.clone()).await);
|
||||
let editor = Arc::new(Self {
|
||||
grid_id: grid_id.to_owned(),
|
||||
user,
|
||||
@ -454,32 +451,31 @@ impl GridRevisionEditor {
|
||||
}
|
||||
|
||||
pub async fn get_grid_setting(&self) -> FlowyResult<GridSetting> {
|
||||
let read_guard = self.grid_pad.read().await;
|
||||
let grid_setting_rev = read_guard.get_grid_setting_rev();
|
||||
Ok(grid_setting_rev.into())
|
||||
// let read_guard = self.grid_pad.read().await;
|
||||
// let grid_setting_rev = read_guard.get_grid_setting_rev();
|
||||
// Ok(grid_setting_rev.into())
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn get_grid_filter(&self, layout_type: &GridLayoutType) -> FlowyResult<Vec<GridFilter>> {
|
||||
let read_guard = self.grid_pad.read().await;
|
||||
let layout_rev = layout_type.clone().into();
|
||||
match read_guard.get_filters(Some(&layout_rev)) {
|
||||
match read_guard.get_filters(Some(&layout_rev), None) {
|
||||
Some(filter_revs) => Ok(filter_revs.iter().map(GridFilter::from).collect::<Vec<GridFilter>>()),
|
||||
None => Ok(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn update_grid_setting(&self, params: GridSettingChangesetParams) -> FlowyResult<()> {
|
||||
let is_filter_changed = params.is_filter_changed();
|
||||
let filter_changeset = GridFilterChangeset::from(¶ms);
|
||||
let _ = self
|
||||
.modify(|grid_pad| Ok(grid_pad.update_grid_setting_rev(params)?))
|
||||
.await?;
|
||||
|
||||
if is_filter_changed {
|
||||
let filter_service = self.filter_service.clone();
|
||||
tokio::spawn(async move {
|
||||
filter_service.notify_changed().await;
|
||||
});
|
||||
}
|
||||
let filter_service = self.filter_service.clone();
|
||||
tokio::spawn(async move {
|
||||
filter_service.apply_changeset(filter_changeset).await;
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -495,7 +491,7 @@ impl GridRevisionEditor {
|
||||
.collect::<Vec<String>>(),
|
||||
Some(block_ids) => block_ids,
|
||||
};
|
||||
let snapshots = self.block_manager.make_block_snapshots(block_ids).await?;
|
||||
let snapshots = self.block_manager.get_block_snapshots(Some(block_ids)).await?;
|
||||
Ok(snapshots)
|
||||
}
|
||||
|
||||
|
@ -5,7 +5,7 @@ use flowy_error::FlowyError;
|
||||
use futures::future::BoxFuture;
|
||||
use lib_infra::future::BoxResultFuture;
|
||||
|
||||
pub trait GridServiceTaskScheduler: Send + Sync + 'static {
|
||||
pub(crate) trait GridServiceTaskScheduler: Send + Sync + 'static {
|
||||
fn gen_task_id(&self) -> BoxFuture<TaskId>;
|
||||
fn register_task(&self, task: Task) -> BoxFuture<()>;
|
||||
}
|
||||
@ -17,9 +17,9 @@ impl GridTaskHandler for GridRevisionEditor {
|
||||
|
||||
fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError> {
|
||||
Box::pin(async move {
|
||||
match &task.content {
|
||||
TaskContent::Snapshot { .. } => {}
|
||||
TaskContent::Filter { .. } => self.filter_service.process_task(task).await?,
|
||||
match task.content {
|
||||
TaskContent::Snapshot => {}
|
||||
TaskContent::Filter(context) => self.filter_service.process(context).await?,
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
@ -2,12 +2,13 @@ use crate::services::field::*;
|
||||
use bytes::Bytes;
|
||||
use flowy_error::{internal_error, ErrorCode, FlowyError, FlowyResult};
|
||||
use flowy_grid_data_model::entities::FieldType;
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision};
|
||||
use flowy_grid_data_model::revision::{CellRevision, FieldRevision, GridFilterRevision};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Formatter;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub trait CellDataOperation<ED> {
|
||||
pub trait CellDataOperation<D, F> {
|
||||
fn decode_cell_data<T>(
|
||||
&self,
|
||||
encoded_data: T,
|
||||
@ -15,9 +16,10 @@ pub trait CellDataOperation<ED> {
|
||||
field_rev: &FieldRevision,
|
||||
) -> FlowyResult<DecodedCellData>
|
||||
where
|
||||
T: Into<ED>;
|
||||
T: Into<D>;
|
||||
|
||||
fn apply_filter(&self, filter: F) -> bool;
|
||||
|
||||
//
|
||||
fn apply_changeset<C: Into<CellContentChangeset>>(
|
||||
&self,
|
||||
changeset: C,
|
||||
@ -113,6 +115,14 @@ impl TypeOptionCellData {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apply_cell_filter(
|
||||
_filter_rev: Arc<GridFilterRevision>,
|
||||
_field_rev: &FieldRevision,
|
||||
_cell_rev: Option<CellRevision>,
|
||||
) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// The changeset will be deserialized into specific data base on the FieldType.
|
||||
/// For example, it's String on FieldType::RichText, and SelectOptionChangeset on FieldType::SingleSelect
|
||||
pub fn apply_cell_data_changeset<T: Into<CellContentChangeset>>(
|
||||
@ -150,13 +160,12 @@ pub fn decode_cell_data<T: TryInto<TypeOptionCellData>>(data: T, field_rev: &Fie
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_decode_cell_data<T: Into<String>>(
|
||||
encoded_data: T,
|
||||
pub fn try_decode_cell_data(
|
||||
encoded_data: String,
|
||||
field_rev: &FieldRevision,
|
||||
s_field_type: &FieldType,
|
||||
t_field_type: &FieldType,
|
||||
) -> FlowyResult<DecodedCellData> {
|
||||
let encoded_data = encoded_data.into();
|
||||
let get_cell_data = || {
|
||||
let data = match t_field_type {
|
||||
FieldType::RichText => field_rev
|
||||
|
@ -1,4 +1,6 @@
|
||||
use flowy_grid_data_model::entities::{CreateGridFilterParams, GridLayoutType, GridSettingChangesetParams};
|
||||
use flowy_grid_data_model::entities::{
|
||||
CreateGridFilterParams, DeleteFilterParams, GridLayoutType, GridSettingChangesetParams,
|
||||
};
|
||||
|
||||
pub struct GridSettingChangesetBuilder {
|
||||
params: GridSettingChangesetParams,
|
||||
@ -24,8 +26,8 @@ impl GridSettingChangesetBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn delete_filter(mut self, filter_id: &str) -> Self {
|
||||
self.params.delete_filter = Some(filter_id.to_string());
|
||||
pub fn delete_filter(mut self, params: DeleteFilterParams) -> Self {
|
||||
self.params.delete_filter = Some(params);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ impl GridTaskQueue {
|
||||
|
||||
pub(crate) fn push(&mut self, task: &Task) {
|
||||
let task_type = match task.content {
|
||||
TaskContent::Snapshot { .. } => TaskType::Snapshot,
|
||||
TaskContent::Snapshot => TaskType::Snapshot,
|
||||
TaskContent::Filter { .. } => TaskType::Filter,
|
||||
};
|
||||
let pending_task = PendingTask {
|
||||
|
@ -11,7 +11,7 @@ use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use tokio::sync::{watch, RwLock};
|
||||
|
||||
pub trait GridTaskHandler: Send + Sync + 'static {
|
||||
pub(crate) trait GridTaskHandler: Send + Sync + 'static {
|
||||
fn handler_id(&self) -> &TaskHandlerId;
|
||||
|
||||
fn process_task(&self, task: Task) -> BoxResultFuture<(), FlowyError>;
|
||||
@ -25,7 +25,7 @@ pub struct GridTaskScheduler {
|
||||
}
|
||||
|
||||
impl GridTaskScheduler {
|
||||
pub fn new() -> Arc<RwLock<Self>> {
|
||||
pub(crate) fn new() -> Arc<RwLock<Self>> {
|
||||
let (notifier, rx) = watch::channel(());
|
||||
|
||||
let scheduler = Self {
|
||||
@ -45,7 +45,7 @@ impl GridTaskScheduler {
|
||||
scheduler
|
||||
}
|
||||
|
||||
pub fn register_handler<T>(&mut self, handler: Arc<T>)
|
||||
pub(crate) fn register_handler<T>(&mut self, handler: Arc<T>)
|
||||
where
|
||||
T: GridTaskHandler,
|
||||
{
|
||||
@ -53,11 +53,11 @@ impl GridTaskScheduler {
|
||||
self.handlers.insert(handler_id, handler);
|
||||
}
|
||||
|
||||
pub fn unregister_handler<T: AsRef<str>>(&mut self, handler_id: T) {
|
||||
pub(crate) fn unregister_handler<T: AsRef<str>>(&mut self, handler_id: T) {
|
||||
let _ = self.handlers.remove(handler_id.as_ref());
|
||||
}
|
||||
|
||||
pub async fn process_next_task(&mut self) -> FlowyResult<()> {
|
||||
pub(crate) async fn process_next_task(&mut self) -> FlowyResult<()> {
|
||||
let mut get_next_task = || {
|
||||
let pending_task = self.queue.mut_head(|list| list.pop())?;
|
||||
let task = self.store.remove_task(&pending_task.id)?;
|
||||
@ -65,7 +65,7 @@ impl GridTaskScheduler {
|
||||
};
|
||||
|
||||
if let Some(task) = get_next_task() {
|
||||
match self.handlers.get(&task.hid) {
|
||||
match self.handlers.get(&task.handler_id) {
|
||||
None => {}
|
||||
Some(handler) => {
|
||||
let _ = handler.process_task(task).await;
|
||||
@ -75,18 +75,18 @@ impl GridTaskScheduler {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn register_task(&mut self, task: Task) {
|
||||
pub(crate) fn register_task(&mut self, task: Task) {
|
||||
assert!(!task.is_finished());
|
||||
self.queue.push(&task);
|
||||
self.store.insert_task(task);
|
||||
self.notify();
|
||||
}
|
||||
|
||||
pub fn next_task_id(&self) -> TaskId {
|
||||
pub(crate) fn next_task_id(&self) -> TaskId {
|
||||
self.store.next_task_id()
|
||||
}
|
||||
|
||||
pub fn notify(&self) {
|
||||
pub(crate) fn notify(&self) {
|
||||
let _ = self.notifier.send(());
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use std::collections::HashMap;
|
||||
use std::sync::atomic::AtomicU32;
|
||||
use std::sync::atomic::Ordering::SeqCst;
|
||||
|
||||
pub struct GridTaskStore {
|
||||
pub(crate) struct GridTaskStore {
|
||||
tasks: HashMap<TaskId, Task>,
|
||||
task_id_counter: AtomicU32,
|
||||
}
|
||||
@ -17,15 +17,15 @@ impl GridTaskStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert_task(&mut self, task: Task) {
|
||||
pub(crate) fn insert_task(&mut self, task: Task) {
|
||||
self.tasks.insert(task.id, task);
|
||||
}
|
||||
|
||||
pub fn remove_task(&mut self, task_id: &TaskId) -> Option<Task> {
|
||||
pub(crate) fn remove_task(&mut self, task_id: &TaskId) -> Option<Task> {
|
||||
self.tasks.remove(task_id)
|
||||
}
|
||||
|
||||
pub fn next_task_id(&self) -> TaskId {
|
||||
pub(crate) fn next_task_id(&self) -> TaskId {
|
||||
let _ = self.task_id_counter.fetch_add(1, SeqCst);
|
||||
self.task_id_counter.load(SeqCst)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::services::row::GridBlockSnapshot;
|
||||
use crate::services::tasks::queue::TaskHandlerId;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -49,17 +50,17 @@ impl Ord for PendingTask {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SnapshotTaskContext {}
|
||||
|
||||
pub struct FilterTaskContext {}
|
||||
|
||||
pub enum TaskContent {
|
||||
Snapshot { context: SnapshotTaskContext },
|
||||
Filter { context: FilterTaskContext },
|
||||
pub(crate) struct FilterTaskContext {
|
||||
pub blocks: Vec<GridBlockSnapshot>,
|
||||
}
|
||||
|
||||
pub struct Task {
|
||||
pub hid: TaskHandlerId,
|
||||
pub(crate) enum TaskContent {
|
||||
Snapshot,
|
||||
Filter(FilterTaskContext),
|
||||
}
|
||||
|
||||
pub(crate) struct Task {
|
||||
pub handler_id: TaskHandlerId,
|
||||
pub id: TaskId,
|
||||
pub content: TaskContent,
|
||||
}
|
||||
|
@ -26,14 +26,17 @@ async fn grid_filter_invalid_condition_panic_test() {
|
||||
#[tokio::test]
|
||||
async fn grid_filter_delete_test() {
|
||||
let mut test = GridEditorTest::new().await;
|
||||
let field_rev = test.text_field();
|
||||
let payload = CreateGridFilterPayload::new(field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
|
||||
let field_rev = test.text_field().clone();
|
||||
let payload = CreateGridFilterPayload::new(&field_rev, TextFilterCondition::TextIsEmpty, Some("abc".to_owned()));
|
||||
let scripts = vec![InsertGridTableFilter { payload }, AssertTableFilterCount { count: 1 }];
|
||||
test.run_scripts(scripts).await;
|
||||
|
||||
let filter = test.grid_filters().await.pop().unwrap();
|
||||
test.run_scripts(vec![
|
||||
DeleteGridTableFilter { filter_id: filter.id },
|
||||
DeleteGridTableFilter {
|
||||
filter_id: filter.id,
|
||||
field_type: field_rev.field_type.clone(),
|
||||
},
|
||||
AssertTableFilterCount { count: 0 },
|
||||
])
|
||||
.await;
|
||||
|
@ -77,6 +77,7 @@ pub enum EditorScript {
|
||||
},
|
||||
DeleteGridTableFilter {
|
||||
filter_id: String,
|
||||
field_type: FieldType,
|
||||
},
|
||||
#[allow(dead_code)]
|
||||
AssertGridSetting {
|
||||
@ -265,10 +266,10 @@ impl GridEditorTest {
|
||||
let filters = self.editor.get_grid_filter(&layout_type).await.unwrap();
|
||||
assert_eq!(count as usize, filters.len());
|
||||
}
|
||||
EditorScript::DeleteGridTableFilter { filter_id } => {
|
||||
EditorScript::DeleteGridTableFilter { filter_id ,field_type} => {
|
||||
let layout_type = GridLayoutType::Table;
|
||||
let params = GridSettingChangesetBuilder::new(&self.grid_id, &layout_type)
|
||||
.delete_filter(&filter_id)
|
||||
.delete_filter(DeleteFilterParams { filter_id, field_type })
|
||||
.build();
|
||||
let _ = self.editor.update_grid_setting(params).await.unwrap();
|
||||
}
|
||||
|
Reference in New Issue
Block a user