fix: calculation bug (#6489)

* chore: upgrade collab

* fix: calculate value

* chore: optimize calculate

* chore: cal when open database

* chore: update calculation when filter change

* chore: use same runtime
This commit is contained in:
Nathan.fooo 2024-10-07 08:57:42 +08:00 committed by GitHub
parent fd9b01ca27
commit 75da496128
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 364 additions and 306 deletions

View File

@ -144,15 +144,15 @@ pub extern "C" fn init_sdk(_port: i64, data: *mut c_char) -> i64 {
.take() .take()
.map(|isolate| Arc::new(LogStreamSenderImpl { isolate }) as Arc<dyn StreamLogSender>); .map(|isolate| Arc::new(LogStreamSenderImpl { isolate }) as Arc<dyn StreamLogSender>);
let (sender, task_rx) = mpsc::unbounded_channel::<Task>(); let (sender, task_rx) = mpsc::unbounded_channel::<Task>();
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone();
let handle = std::thread::spawn(move || { let handle = std::thread::spawn(move || {
let runtime = Builder::new_multi_thread().enable_all().build().unwrap();
let local_set = LocalSet::new(); let local_set = LocalSet::new();
runtime.block_on(local_set.run_until(Runner { rx: task_rx })); cloned_runtime.block_on(local_set.run_until(Runner { rx: task_rx }));
}); });
*DART_APPFLOWY_CORE.sender.write().unwrap() = Some(sender); *DART_APPFLOWY_CORE.sender.write().unwrap() = Some(sender);
*DART_APPFLOWY_CORE.handle.write().unwrap() = Some(handle); *DART_APPFLOWY_CORE.handle.write().unwrap() = Some(handle);
let runtime = Arc::new(AFPluginRuntime::new().unwrap());
let cloned_runtime = runtime.clone(); let cloned_runtime = runtime.clone();
*DART_APPFLOWY_CORE.core.write().unwrap() = runtime *DART_APPFLOWY_CORE.core.write().unwrap() = runtime
.block_on(async move { Some(AppFlowyCore::new(config, cloned_runtime, log_stream).await) }); .block_on(async move { Some(AppFlowyCore::new(config, cloned_runtime, log_stream).await) });

View File

@ -23,6 +23,19 @@ pub struct CalculationPB {
pub value: String, pub value: String,
} }
impl std::convert::From<&CalculationPB> for Calculation {
fn from(calculation: &CalculationPB) -> Self {
let calculation_type = calculation.calculation_type.into();
Self {
id: calculation.id.clone(),
field_id: calculation.field_id.clone(),
calculation_type,
value: calculation.value.clone(),
}
}
}
impl std::convert::From<&Calculation> for CalculationPB { impl std::convert::From<&Calculation> for CalculationPB {
fn from(calculation: &Calculation) -> Self { fn from(calculation: &Calculation) -> Self {
let calculation_type = calculation.calculation_type.into(); let calculation_type = calculation.calculation_type.into();

View File

@ -3,12 +3,13 @@ use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{Row, RowCell}; use collab_database::rows::{Cell, Row};
use dashmap::DashMap;
use flowy_error::FlowyResult; use flowy_error::FlowyResult;
use lib_infra::priority_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::RwLock as TokioRwLock; use tokio::sync::RwLock as TokioRwLock;
use tracing::instrument;
use lib_infra::priority_task::{QualityOfService, Task, TaskContent, TaskDispatcher};
use crate::entities::{ use crate::entities::{
CalculationChangesetNotificationPB, CalculationPB, CalculationType, FieldType, CalculationChangesetNotificationPB, CalculationPB, CalculationType, FieldType,
@ -21,10 +22,10 @@ use super::{Calculation, CalculationChangeset, CalculationsService};
#[async_trait] #[async_trait]
pub trait CalculationsDelegate: Send + Sync + 'static { pub trait CalculationsDelegate: Send + Sync + 'static {
async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<RowCell>>; async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<Cell>>;
async fn get_field(&self, field_id: &str) -> Option<Field>; async fn get_field(&self, field_id: &str) -> Option<Field>;
async fn get_calculation(&self, view_id: &str, field_id: &str) -> Option<Arc<Calculation>>; async fn get_calculation(&self, view_id: &str, field_id: &str) -> Option<Arc<Calculation>>;
async fn get_all_calculations(&self, view_id: &str) -> Arc<Vec<Arc<Calculation>>>; async fn get_all_calculations(&self, view_id: &str) -> Vec<Arc<Calculation>>;
async fn update_calculation(&self, view_id: &str, calculation: Calculation); async fn update_calculation(&self, view_id: &str, calculation: Calculation);
async fn remove_calculation(&self, view_id: &str, calculation_id: &str); async fn remove_calculation(&self, view_id: &str, calculation_id: &str);
} }
@ -71,15 +72,16 @@ impl CalculationsController {
} }
pub async fn close(&self) { pub async fn close(&self) {
if let Ok(mut task_scheduler) = self.task_scheduler.try_write() { self
task_scheduler.unregister_handler(&self.handler_id).await; .task_scheduler
} else { .write()
tracing::error!("Attempt to get the lock of task_scheduler failed"); .await
} .unregister_handler(&self.handler_id)
.await;
} }
#[tracing::instrument(name = "schedule_calculation_task", level = "trace", skip(self))] #[tracing::instrument(name = "schedule_calculation_task", level = "trace", skip(self))]
async fn gen_task(&self, task_type: CalculationEvent, qos: QualityOfService) { pub(crate) async fn gen_task(&self, task_type: CalculationEvent, qos: QualityOfService) {
let task_id = self.task_scheduler.read().await.next_task_id(); let task_id = self.task_scheduler.read().await.next_task_id();
let task = Task::new( let task = Task::new(
&self.handler_id, &self.handler_id,
@ -100,7 +102,7 @@ impl CalculationsController {
pub async fn process(&self, predicate: &str) -> FlowyResult<()> { pub async fn process(&self, predicate: &str) -> FlowyResult<()> {
let event_type = CalculationEvent::from_str(predicate).unwrap(); let event_type = CalculationEvent::from_str(predicate).unwrap();
match event_type { match event_type {
CalculationEvent::RowChanged(row) => self.handle_row_changed(row).await, CalculationEvent::RowChanged(row) => self.handle_row_changed(&row).await,
CalculationEvent::CellUpdated(field_id) => self.handle_cell_changed(field_id).await, CalculationEvent::CellUpdated(field_id) => self.handle_cell_changed(field_id).await,
CalculationEvent::FieldDeleted(field_id) => self.handle_field_deleted(field_id).await, CalculationEvent::FieldDeleted(field_id) => self.handle_field_deleted(field_id).await,
CalculationEvent::FieldTypeChanged(field_id, new_field_type) => { CalculationEvent::FieldTypeChanged(field_id, new_field_type) => {
@ -108,6 +110,11 @@ impl CalculationsController {
.handle_field_type_changed(field_id, new_field_type) .handle_field_type_changed(field_id, new_field_type)
.await .await
}, },
CalculationEvent::InitialRows(rows) => {
for row in rows {
self.handle_row_changed(row.as_ref()).await;
}
},
} }
Ok(()) Ok(())
@ -117,7 +124,7 @@ impl CalculationsController {
self self
.gen_task( .gen_task(
CalculationEvent::FieldDeleted(field_id), CalculationEvent::FieldDeleted(field_id),
QualityOfService::UserInteractive, QualityOfService::Background,
) )
.await .await
} }
@ -151,7 +158,7 @@ impl CalculationsController {
self self
.gen_task( .gen_task(
CalculationEvent::FieldTypeChanged(field_id, new_field_type), CalculationEvent::FieldTypeChanged(field_id, new_field_type),
QualityOfService::UserInteractive, QualityOfService::Background,
) )
.await .await
} }
@ -188,7 +195,7 @@ impl CalculationsController {
self self
.gen_task( .gen_task(
CalculationEvent::CellUpdated(field_id), CalculationEvent::CellUpdated(field_id),
QualityOfService::UserInteractive, QualityOfService::Background,
) )
.await .await
} }
@ -200,13 +207,20 @@ impl CalculationsController {
.await; .await;
if let Some(calculation) = calculation { if let Some(calculation) = calculation {
let update = self.get_updated_calculation(calculation).await; if let Some(field) = self.delegate.get_field(&field_id).await {
if let Some(update) = update { let cells = self
.delegate
.get_cells_for_field(&self.view_id, &calculation.field_id)
.await;
// Update the calculation
if let Some(update) = self.update_calculation(calculation, &field, cells).await {
self self
.delegate .delegate
.update_calculation(&self.view_id, update.clone()) .update_calculation(&self.view_id, update.clone())
.await; .await;
// Send notification
let notification = CalculationChangesetNotificationPB::from_update( let notification = CalculationChangesetNotificationPB::from_update(
&self.view_id, &self.view_id,
vec![CalculationPB::from(&update)], vec![CalculationPB::from(&update)],
@ -220,56 +234,49 @@ impl CalculationsController {
} }
} }
} }
}
pub async fn did_receive_row_changed(&self, row: Row) { pub async fn did_receive_row_changed(&self, row: Row) {
self self
.gen_task( .gen_task(
CalculationEvent::RowChanged(row), CalculationEvent::RowChanged(row),
QualityOfService::UserInteractive, QualityOfService::Background,
) )
.await .await
} }
async fn handle_row_changed(&self, row: Row) { async fn handle_row_changed(&self, row: &Row) {
let cells = row.cells.iter(); let cells = &row.cells;
let mut updates = vec![]; let mut updates = vec![];
let mut cells_by_field = DashMap::<String, Vec<Arc<Cell>>>::new();
// In case there are calculations where empty cells are counted // In case there are calculations where empty cells are counted
// as a contribution to the value. // as a contribution to the value.
if cells.len() == 0 { if cells.len() == 0 {
let calculations = self.delegate.get_all_calculations(&self.view_id).await; let calculations = self.delegate.get_all_calculations(&self.view_id).await;
for calculation in calculations.iter() { for calculation in calculations.into_iter() {
let update = self.get_updated_calculation(calculation.clone()).await; let cells = self
if let Some(update) = update { .get_or_fetch_cells(&calculation.field_id, &mut cells_by_field)
updates.push(CalculationPB::from(&update));
self
.delegate
.update_calculation(&self.view_id, update)
.await; .await;
} updates.extend(self.handle_cells_changed(calculation, cells).await);
} }
} }
// Iterate each cell in the row // Iterate each cell in the row
for cell in cells { for cell in cells {
let field_id = cell.0; let field_id = &cell.0;
let calculation = self.delegate.get_calculation(&self.view_id, field_id).await; let calculation = self.delegate.get_calculation(&self.view_id, field_id).await;
if let Some(calculation) = calculation { if let Some(calculation) = calculation {
let update = self.get_updated_calculation(calculation.clone()).await; let cells = self
.get_or_fetch_cells(&calculation.field_id, &mut cells_by_field)
if let Some(update) = update {
updates.push(CalculationPB::from(&update));
self
.delegate
.update_calculation(&self.view_id, update)
.await; .await;
} let changes = self.handle_cells_changed(calculation, cells).await;
updates.extend(changes);
} }
} }
if !updates.is_empty() { if !updates.is_empty() {
let notification = CalculationChangesetNotificationPB::from_update(&self.view_id, updates); let notification = CalculationChangesetNotificationPB::from_update(&self.view_id, updates);
let _ = self let _ = self
.notifier .notifier
.send(DatabaseViewChanged::CalculationValueNotification( .send(DatabaseViewChanged::CalculationValueNotification(
@ -278,17 +285,58 @@ impl CalculationsController {
} }
} }
async fn get_updated_calculation(&self, calculation: Arc<Calculation>) -> Option<Calculation> { async fn get_or_fetch_cells<'a>(
let field_cells = self &'a self,
field_id: &'a str,
cells_by_field: &'a mut DashMap<String, Vec<Arc<Cell>>>,
) -> Vec<Arc<Cell>> {
let cells = cells_by_field.get(field_id).map(|entry| entry.to_vec());
match cells {
None => {
let fetch_cells = self
.delegate .delegate
.get_cells_for_field(&self.view_id, &calculation.field_id) .get_cells_for_field(&self.view_id, field_id)
.await; .await;
let field = self.delegate.get_field(&calculation.field_id).await?; cells_by_field.insert(field_id.to_string(), fetch_cells.clone());
fetch_cells
},
Some(cells) => cells,
}
}
let value = /// field_cells will be the cells that belong to the field with field_id
async fn handle_cells_changed(
&self,
calculation: Arc<Calculation>,
field_cells: Vec<Arc<Cell>>,
) -> Vec<CalculationPB> {
let mut updates = vec![];
if let Some(field) = self.delegate.get_field(&calculation.field_id).await {
let update = self
.update_calculation(calculation, &field, field_cells)
.await;
if let Some(update) = update {
updates.push(CalculationPB::from(&update));
self self
.delegate
.update_calculation(&self.view_id, update)
.await;
}
}
updates
}
#[instrument(level = "trace", skip_all)]
async fn update_calculation(
&self,
calculation: Arc<Calculation>,
field: &Field,
cells: Vec<Arc<Cell>>,
) -> Option<Calculation> {
let value = self
.calculations_service .calculations_service
.calculate(&field, calculation.calculation_type, field_cells); .calculate(&field, calculation.calculation_type, cells);
if value != calculation.value { if value != calculation.value {
return Some(calculation.with_value(value)); return Some(calculation.with_value(value));
@ -304,7 +352,7 @@ impl CalculationsController {
let mut notification: Option<CalculationChangesetNotificationPB> = None; let mut notification: Option<CalculationChangesetNotificationPB> = None;
if let Some(insert) = &changeset.insert_calculation { if let Some(insert) = &changeset.insert_calculation {
let row_cells: Vec<Arc<RowCell>> = self let cells = self
.delegate .delegate
.get_cells_for_field(&self.view_id, &insert.field_id) .get_cells_for_field(&self.view_id, &insert.field_id)
.await; .await;
@ -313,7 +361,7 @@ impl CalculationsController {
let value = self let value = self
.calculations_service .calculations_service
.calculate(&field, insert.calculation_type, row_cells); .calculate(&field, insert.calculation_type, cells);
notification = Some(CalculationChangesetNotificationPB::from_insert( notification = Some(CalculationChangesetNotificationPB::from_insert(
&self.view_id, &self.view_id,
@ -352,7 +400,8 @@ impl CalculationsController {
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
enum CalculationEvent { pub(crate) enum CalculationEvent {
InitialRows(Vec<Arc<Row>>),
RowChanged(Row), RowChanged(Row),
CellUpdated(String), CellUpdated(String),
FieldTypeChanged(String, FieldType), FieldTypeChanged(String, FieldType),

View File

@ -3,8 +3,6 @@ use collab::preclude::Any;
use collab_database::views::{CalculationMap, CalculationMapBuilder}; use collab_database::views::{CalculationMap, CalculationMapBuilder};
use serde::Deserialize; use serde::Deserialize;
use crate::entities::CalculationPB;
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct Calculation { pub struct Calculation {
pub id: String, pub id: String,
@ -31,19 +29,6 @@ impl From<Calculation> for CalculationMap {
} }
} }
impl std::convert::From<&CalculationPB> for Calculation {
fn from(calculation: &CalculationPB) -> Self {
let calculation_type = calculation.calculation_type.into();
Self {
id: calculation.id.clone(),
field_id: calculation.field_id.clone(),
calculation_type,
value: calculation.value.clone(),
}
}
}
impl TryFrom<CalculationMap> for Calculation { impl TryFrom<CalculationMap> for Calculation {
type Error = anyhow::Error; type Error = anyhow::Error;

View File

@ -1,64 +1,57 @@
use std::sync::Arc; use std::sync::Arc;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::RowCell; use collab_database::rows::{Cell, RowCell};
use crate::entities::CalculationType; use crate::entities::CalculationType;
use crate::services::field::TypeOptionCellExt; use crate::services::field::TypeOptionCellExt;
use rayon::prelude::*;
pub struct CalculationsService {} pub struct CalculationsService;
impl CalculationsService { impl CalculationsService {
pub fn new() -> Self { pub fn new() -> Self {
Self {} Self
} }
pub fn calculate( pub fn calculate(&self, field: &Field, calculation_type: i64, cells: Vec<Arc<Cell>>) -> String {
&self,
field: &Field,
calculation_type: i64,
row_cells: Vec<Arc<RowCell>>,
) -> String {
let ty: CalculationType = calculation_type.into(); let ty: CalculationType = calculation_type.into();
match ty { match ty {
CalculationType::Average => self.calculate_average(field, row_cells), CalculationType::Average => self.calculate_average(field, cells),
CalculationType::Max => self.calculate_max(field, row_cells), CalculationType::Max => self.calculate_max(field, cells),
CalculationType::Median => self.calculate_median(field, row_cells), CalculationType::Median => self.calculate_median(field, cells),
CalculationType::Min => self.calculate_min(field, row_cells), CalculationType::Min => self.calculate_min(field, cells),
CalculationType::Sum => self.calculate_sum(field, row_cells), CalculationType::Sum => self.calculate_sum(field, cells),
CalculationType::Count => self.calculate_count(row_cells), CalculationType::Count => self.calculate_count(cells),
CalculationType::CountEmpty => self.calculate_count_empty(field, row_cells), CalculationType::CountEmpty => self.calculate_count_empty(field, cells),
CalculationType::CountNonEmpty => self.calculate_count_non_empty(field, row_cells), CalculationType::CountNonEmpty => self.calculate_count_non_empty(field, cells),
} }
} }
fn calculate_average(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String { fn calculate_average(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
let mut sum = 0.0;
let mut len = 0.0;
if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() { if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
for row_cell in row_cells { let (sum, len): (f64, usize) = cells
if let Some(cell) = &row_cell.cell { .par_iter()
if let Some(value) = handler.handle_numeric_cell(cell) { .filter_map(|cell| handler.handle_numeric_cell(cell))
sum += value; .map(|value| (value, 1))
len += 1.0; .reduce(
} || (0.0, 0),
} |(sum1, len1), (sum2, len2)| (sum1 + sum2, len1 + len2),
} );
}
if len > 0.0 { if len > 0 {
format!("{:.5}", sum / len) format!("{:.5}", sum / len as f64)
} else {
String::new()
}
} else { } else {
String::new() String::new()
} }
} }
fn calculate_median(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String { fn calculate_median(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
let values = self.reduce_values_f64(field, row_cells, |values| { let mut values = self.reduce_values_f64(field, cells);
values.sort_by(|a, b| a.partial_cmp(b).unwrap()); values.par_sort_by(|a, b| a.partial_cmp(b).unwrap());
values.clone()
});
if !values.is_empty() { if !values.is_empty() {
format!("{:.5}", Self::median(&values)) format!("{:.5}", Self::median(&values))
@ -67,8 +60,74 @@ impl CalculationsService {
} }
} }
fn calculate_min(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
let values = self.reduce_values_f64(field, cells);
if let Some(min) = values.par_iter().min_by(|a, b| a.total_cmp(b)) {
format!("{:.5}", min)
} else {
String::new()
}
}
fn calculate_max(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
let values = self.reduce_values_f64(field, cells);
if let Some(max) = values.par_iter().max_by(|a, b| a.total_cmp(b)) {
format!("{:.5}", max)
} else {
String::new()
}
}
fn calculate_sum(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
let values = self.reduce_values_f64(field, cells);
if !values.is_empty() {
format!("{:.5}", values.par_iter().sum::<f64>())
} else {
String::new()
}
}
fn calculate_count(&self, cells: Vec<Arc<Cell>>) -> String {
format!("{}", cells.len())
}
fn calculate_count_empty(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
let empty_count = cells
.par_iter()
.filter(|cell| handler.handle_is_cell_empty(cell, field))
.count();
empty_count.to_string()
} else {
"".to_string()
}
}
fn calculate_count_non_empty(&self, field: &Field, cells: Vec<Arc<Cell>>) -> String {
if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
let non_empty_count = cells
.par_iter()
.filter(|cell| !handler.handle_is_cell_empty(cell, field))
.count();
non_empty_count.to_string()
} else {
"".to_string()
}
}
fn reduce_values_f64(&self, field: &Field, row_cells: Vec<Arc<Cell>>) -> Vec<f64> {
if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
row_cells
.par_iter()
.filter_map(|cell| handler.handle_numeric_cell(cell))
.collect::<Vec<_>>()
} else {
vec![]
}
}
fn median(array: &[f64]) -> f64 { fn median(array: &[f64]) -> f64 {
if (array.len() % 2) == 0 { if array.len() % 2 == 0 {
let left = array.len() / 2 - 1; let left = array.len() / 2 - 1;
let right = array.len() / 2; let right = array.len() / 2;
(array[left] + array[right]) / 2.0 (array[left] + array[right]) / 2.0
@ -76,109 +135,4 @@ impl CalculationsService {
array[array.len() / 2] array[array.len() / 2]
} }
} }
fn calculate_min(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
let values = self.reduce_values_f64(field, row_cells, |values| {
values.sort_by(|a, b| a.partial_cmp(b).unwrap());
values.clone()
});
if !values.is_empty() {
let min = values.iter().min_by(|a, b| a.total_cmp(b));
if let Some(min) = min {
return format!("{:.5}", min);
}
}
String::new()
}
fn calculate_max(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
let values = self.reduce_values_f64(field, row_cells, |values| {
values.sort_by(|a, b| a.partial_cmp(b).unwrap());
values.clone()
});
if !values.is_empty() {
let max = values.iter().max_by(|a, b| a.total_cmp(b));
if let Some(max) = max {
return format!("{:.5}", max);
}
}
String::new()
}
fn calculate_sum(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
let values = self.reduce_values_f64(field, row_cells, |values| values.clone());
if !values.is_empty() {
format!("{:.5}", values.iter().sum::<f64>())
} else {
String::new()
}
}
fn calculate_count(&self, row_cells: Vec<Arc<RowCell>>) -> String {
if !row_cells.is_empty() {
format!("{}", row_cells.len())
} else {
String::new()
}
}
fn calculate_count_empty(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
match TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
Some(handler) if !row_cells.is_empty() => row_cells
.iter()
.filter(|row_cell| {
if let Some(cell) = &row_cell.cell {
handler.handle_is_cell_empty(cell, field)
} else {
true
}
})
.collect::<Vec<_>>()
.len()
.to_string(),
_ => "".to_string(),
}
}
fn calculate_count_non_empty(&self, field: &Field, row_cells: Vec<Arc<RowCell>>) -> String {
match TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
Some(handler) if !row_cells.is_empty() => row_cells
.iter()
.filter(|row_cell| {
if let Some(cell) = &row_cell.cell {
!handler.handle_is_cell_empty(cell, field)
} else {
false
}
})
.collect::<Vec<_>>()
.len()
.to_string(),
_ => "".to_string(),
}
}
fn reduce_values_f64<F, T>(&self, field: &Field, row_cells: Vec<Arc<RowCell>>, f: F) -> T
where
F: FnOnce(&mut Vec<f64>) -> T,
{
let mut values = vec![];
if let Some(handler) = TypeOptionCellExt::new(field, None).get_type_option_cell_data_handler() {
for row_cell in row_cells {
if let Some(cell) = &row_cell.cell {
if let Some(value) = handler.handle_numeric_cell(cell) {
values.push(value);
}
}
}
}
f(&mut values)
}
} }

View File

@ -1,9 +1,9 @@
use lib_infra::future::BoxResultFuture; use crate::services::calculations::CalculationsController;
use async_trait::async_trait;
use lib_infra::priority_task::{TaskContent, TaskHandler}; use lib_infra::priority_task::{TaskContent, TaskHandler};
use std::sync::Arc; use std::sync::Arc;
use crate::services::calculations::CalculationsController;
pub struct CalculationsTaskHandler { pub struct CalculationsTaskHandler {
handler_id: String, handler_id: String,
calculations_controller: Arc<CalculationsController>, calculations_controller: Arc<CalculationsController>,
@ -18,6 +18,7 @@ impl CalculationsTaskHandler {
} }
} }
#[async_trait]
impl TaskHandler for CalculationsTaskHandler { impl TaskHandler for CalculationsTaskHandler {
fn handler_id(&self) -> &str { fn handler_id(&self) -> &str {
&self.handler_id &self.handler_id
@ -27,9 +28,8 @@ impl TaskHandler for CalculationsTaskHandler {
"CalculationsTaskHandler" "CalculationsTaskHandler"
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> { async fn run(&self, content: TaskContent) -> Result<(), anyhow::Error> {
let calculations_controller = self.calculations_controller.clone(); let calculations_controller = self.calculations_controller.clone();
Box::pin(async move {
if let TaskContent::Text(predicate) = content { if let TaskContent::Text(predicate) = content {
calculations_controller calculations_controller
.process(&predicate) .process(&predicate)
@ -37,6 +37,5 @@ impl TaskHandler for CalculationsTaskHandler {
.map_err(anyhow::Error::from)?; .map_err(anyhow::Error::from)?;
} }
Ok(()) Ok(())
})
} }
} }

View File

@ -1670,10 +1670,17 @@ impl DatabaseEditor {
loaded_rows.len(), loaded_rows.len(),
blocking_read blocking_read
); );
let loaded_rows = apply_filter_and_sort(loaded_rows, view_editor).await; let loaded_rows = apply_filter_and_sort(loaded_rows, view_editor.clone()).await;
// Update calculation values
let calculate_rows = loaded_rows.clone();
if let Some(notify_finish) = notify_finish { if let Some(notify_finish) = notify_finish {
let _ = notify_finish.send(loaded_rows); let _ = notify_finish.send(loaded_rows);
} }
tokio::spawn(async move {
let _ = view_editor.v_calculate_rows(calculate_rows).await;
});
}); });
} }
@ -1976,14 +1983,12 @@ impl DatabaseViewOperation for DatabaseViewOperationImpl {
self.database.write().await.remove_row(row_id).await self.database.write().await.remove_row(row_id).await
} }
async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<RowCell>> { async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<RowCell> {
let cells = self let editor = self.editor_by_view_id.read().await.get(view_id).cloned();
.database match editor {
.read() None => vec![],
.await Some(editor) => editor.v_get_cells_for_field(field_id).await,
.get_cells_for_field(view_id, field_id) }
.await;
cells.into_iter().map(Arc::new).collect()
} }
async fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Arc<RowCell> { async fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Arc<RowCell> {

View File

@ -2,7 +2,7 @@ use async_trait::async_trait;
use collab_database::fields::Field; use collab_database::fields::Field;
use std::sync::Arc; use std::sync::Arc;
use collab_database::rows::RowCell; use collab_database::rows::Cell;
use crate::services::calculations::{ use crate::services::calculations::{
Calculation, CalculationsController, CalculationsDelegate, CalculationsTaskHandler, Calculation, CalculationsController, CalculationsDelegate, CalculationsTaskHandler,
@ -46,8 +46,14 @@ struct DatabaseViewCalculationsDelegateImpl(Arc<dyn DatabaseViewOperation>);
#[async_trait] #[async_trait]
impl CalculationsDelegate for DatabaseViewCalculationsDelegateImpl { impl CalculationsDelegate for DatabaseViewCalculationsDelegateImpl {
async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<RowCell>> { async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<Cell>> {
self.0.get_cells_for_field(view_id, field_id).await self
.0
.get_cells_for_field(view_id, field_id)
.await
.into_iter()
.filter_map(|row_cell| row_cell.cell.map(Arc::new))
.collect()
} }
async fn get_field(&self, field_id: &str) -> Option<Field> { async fn get_field(&self, field_id: &str) -> Option<Field> {
@ -70,7 +76,7 @@ impl CalculationsDelegate for DatabaseViewCalculationsDelegateImpl {
self.0.remove_calculation(view_id, calculation_id).await self.0.remove_calculation(view_id, calculation_id).await
} }
async fn get_all_calculations(&self, view_id: &str) -> Arc<Vec<Arc<Calculation>>> { async fn get_all_calculations(&self, view_id: &str) -> Vec<Arc<Calculation>> {
self.0.get_all_calculations(view_id).await.into() self.0.get_all_calculations(view_id).await
} }
} }

View File

@ -11,7 +11,9 @@ use crate::entities::{
SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB, SortChangesetNotificationPB, SortPB, UpdateCalculationChangesetPB, UpdateSortPayloadPB,
}; };
use crate::notification::{send_notification, DatabaseNotification}; use crate::notification::{send_notification, DatabaseNotification};
use crate::services::calculations::{Calculation, CalculationChangeset, CalculationsController}; use crate::services::calculations::{
Calculation, CalculationChangeset, CalculationEvent, CalculationsController,
};
use crate::services::cell::{CellBuilder, CellCache}; use crate::services::cell::{CellBuilder, CellCache};
use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow}; use crate::services::database::{database_view_setting_pb_from_view, DatabaseRowEvent, UpdatedRow};
use crate::services::database_view::view_calculations::make_calculations_controller; use crate::services::database_view::view_calculations::make_calculations_controller;
@ -36,10 +38,11 @@ use crate::services::sort::{Sort, SortChangeset, SortController};
use collab_database::database::{gen_database_calculation_id, gen_database_sort_id, gen_row_id}; use collab_database::database::{gen_database_calculation_id, gen_database_sort_id, gen_row_id};
use collab_database::entity::DatabaseView; use collab_database::entity::DatabaseView;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{Cells, Row, RowDetail, RowId}; use collab_database::rows::{Cells, Row, RowCell, RowDetail, RowId};
use collab_database::views::{DatabaseLayout, RowOrder}; use collab_database::views::{DatabaseLayout, RowOrder};
use dashmap::DashMap; use dashmap::DashMap;
use flowy_error::{FlowyError, FlowyResult}; use flowy_error::{FlowyError, FlowyResult};
use lib_infra::priority_task::QualityOfService;
use lib_infra::util::timestamp; use lib_infra::util::timestamp;
use tokio::sync::{broadcast, RwLock}; use tokio::sync::{broadcast, RwLock};
use tracing::{instrument, trace, warn}; use tracing::{instrument, trace, warn};
@ -387,6 +390,27 @@ impl DatabaseViewEditor {
rows rows
} }
pub async fn v_get_cells_for_field(&self, field_id: &str) -> Vec<RowCell> {
let row_orders = self.delegate.get_all_row_orders(&self.view_id).await;
let rows = self.delegate.get_all_rows(&self.view_id, row_orders).await;
let rows = self.v_filter_rows(rows).await;
let rows = rows
.into_iter()
.filter_map(|row| {
row
.cells
.get(field_id)
.map(|cell| RowCell::new(row.id.clone(), Some(cell.clone())))
})
.collect::<Vec<_>>();
trace!(
"[Database]: get cells for field: {}, total rows:{}",
field_id,
rows.len()
);
rows
}
pub async fn v_get_row(&self, row_id: &RowId) -> Option<(usize, Arc<RowDetail>)> { pub async fn v_get_row(&self, row_id: &RowId) -> Option<(usize, Arc<RowDetail>)> {
self.delegate.get_row_detail(&self.view_id, row_id).await self.delegate.get_row_detail(&self.view_id, row_id).await
} }
@ -650,6 +674,18 @@ impl DatabaseViewEditor {
Ok(()) Ok(())
} }
pub async fn v_calculate_rows(&self, rows: Vec<Arc<Row>>) -> FlowyResult<()> {
self
.calculations_controller
.gen_task(
CalculationEvent::InitialRows(rows),
QualityOfService::UserInteractive,
)
.await;
Ok(())
}
pub async fn v_delete_all_sorts(&self) -> FlowyResult<()> { pub async fn v_delete_all_sorts(&self) -> FlowyResult<()> {
let all_sorts = self.v_get_all_sorts().await; let all_sorts = self.v_get_all_sorts().await;
self.sort_controller.write().await.delete_all_sorts().await; self.sort_controller.write().await.delete_all_sorts().await;
@ -669,11 +705,9 @@ impl DatabaseViewEditor {
&self, &self,
params: UpdateCalculationChangesetPB, params: UpdateCalculationChangesetPB,
) -> FlowyResult<()> { ) -> FlowyResult<()> {
let calculation_id = match params.calculation_id { let calculation_id = params
None => gen_database_calculation_id(), .calculation_id
Some(calculation_id) => calculation_id, .unwrap_or_else(|| gen_database_calculation_id());
};
let calculation = Calculation::none( let calculation = Calculation::none(
calculation_id, calculation_id,
params.field_id, params.field_id,
@ -748,6 +782,9 @@ impl DatabaseViewEditor {
self.v_group_by_field(&field_id).await?; self.v_group_by_field(&field_id).await?;
} }
let row_orders = self.delegate.get_all_row_orders(&self.view_id).await;
let rows = self.delegate.get_all_rows(&self.view_id, row_orders).await;
self.v_calculate_rows(rows).await?;
Ok(()) Ok(())
} }

View File

@ -62,7 +62,7 @@ pub trait DatabaseViewOperation: Send + Sync + 'static {
async fn remove_row(&self, row_id: &RowId) -> Option<Row>; async fn remove_row(&self, row_id: &RowId) -> Option<Row>;
async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<Arc<RowCell>>; async fn get_cells_for_field(&self, view_id: &str, field_id: &str) -> Vec<RowCell>;
async fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Arc<RowCell>; async fn get_cell_in_row(&self, field_id: &str, row_id: &RowId) -> Arc<RowCell>;

View File

@ -116,11 +116,12 @@ impl FilterController {
} }
pub async fn close(&self) { pub async fn close(&self) {
if let Ok(mut task_scheduler) = self.task_scheduler.try_write() { self
task_scheduler.unregister_handler(&self.handler_id).await; .task_scheduler
} else { .write()
tracing::error!("Try to get the lock of task_scheduler failed"); .await
} .unregister_handler(&self.handler_id)
.await;
} }
#[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))] #[tracing::instrument(name = "schedule_filter_task", level = "trace", skip(self))]

View File

@ -1,5 +1,6 @@
use crate::services::filter::FilterController; use crate::services::filter::FilterController;
use lib_infra::future::BoxResultFuture; use async_trait::async_trait;
use lib_infra::priority_task::{TaskContent, TaskHandler}; use lib_infra::priority_task::{TaskContent, TaskHandler};
use std::sync::Arc; use std::sync::Arc;
@ -17,6 +18,7 @@ impl FilterTaskHandler {
} }
} }
#[async_trait]
impl TaskHandler for FilterTaskHandler { impl TaskHandler for FilterTaskHandler {
fn handler_id(&self) -> &str { fn handler_id(&self) -> &str {
&self.handler_id &self.handler_id
@ -26,9 +28,8 @@ impl TaskHandler for FilterTaskHandler {
"FilterTaskHandler" "FilterTaskHandler"
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> { async fn run(&self, content: TaskContent) -> Result<(), anyhow::Error> {
let filter_controller = self.filter_controller.clone(); let filter_controller = self.filter_controller.clone();
Box::pin(async move {
if let TaskContent::Text(predicate) = content { if let TaskContent::Text(predicate) = content {
filter_controller filter_controller
.process(&predicate) .process(&predicate)
@ -36,6 +37,5 @@ impl TaskHandler for FilterTaskHandler {
.map_err(anyhow::Error::from)?; .map_err(anyhow::Error::from)?;
} }
Ok(()) Ok(())
})
} }
} }

View File

@ -6,6 +6,7 @@ use std::sync::Arc;
use collab_database::fields::Field; use collab_database::fields::Field;
use collab_database::rows::{Cell, Row, RowId}; use collab_database::rows::{Cell, Row, RowId};
use rayon::prelude::ParallelSliceMut; use rayon::prelude::ParallelSliceMut;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use tokio::sync::RwLock as TokioRwLock; use tokio::sync::RwLock as TokioRwLock;

View File

@ -1,5 +1,6 @@
use crate::services::sort::SortController; use crate::services::sort::SortController;
use lib_infra::future::BoxResultFuture; use async_trait::async_trait;
use lib_infra::priority_task::{TaskContent, TaskHandler}; use lib_infra::priority_task::{TaskContent, TaskHandler};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::RwLock; use tokio::sync::RwLock;
@ -19,6 +20,7 @@ impl SortTaskHandler {
} }
} }
#[async_trait]
impl TaskHandler for SortTaskHandler { impl TaskHandler for SortTaskHandler {
fn handler_id(&self) -> &str { fn handler_id(&self) -> &str {
&self.handler_id &self.handler_id
@ -28,9 +30,8 @@ impl TaskHandler for SortTaskHandler {
"SortTaskHandler" "SortTaskHandler"
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), anyhow::Error> { async fn run(&self, content: TaskContent) -> Result<(), anyhow::Error> {
let sort_controller = self.sort_controller.clone(); let sort_controller = self.sort_controller.clone();
Box::pin(async move {
if let TaskContent::Text(predicate) = content { if let TaskContent::Text(predicate) = content {
sort_controller sort_controller
.write() .write()
@ -40,6 +41,5 @@ impl TaskHandler for SortTaskHandler {
.map_err(anyhow::Error::from)?; .map_err(anyhow::Error::from)?;
} }
Ok(()) Ok(())
})
} }
} }

View File

@ -2,13 +2,14 @@ use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use crate::future::BoxResultFuture;
use crate::priority_task::queue::TaskQueue; use crate::priority_task::queue::TaskQueue;
use crate::priority_task::store::TaskStore; use crate::priority_task::store::TaskStore;
use crate::priority_task::{Task, TaskContent, TaskId, TaskState}; use crate::priority_task::{Task, TaskContent, TaskId, TaskState};
use anyhow::Error; use anyhow::Error;
use async_trait::async_trait;
use tokio::sync::{watch, RwLock}; use tokio::sync::{watch, RwLock};
use tokio::time::interval; use tokio::time::interval;
use tracing::trace;
pub struct TaskDispatcher { pub struct TaskDispatcher {
queue: TaskQueue, queue: TaskQueue,
@ -105,6 +106,11 @@ impl TaskDispatcher {
return; return;
} }
trace!(
"Add task: handler:{}, task:{:?}",
task.handler_id,
task.content
);
self.queue.push(&task); self.queue.push(&task);
self.store.insert_task(task); self.store.insert_task(task);
self.notify(); self.notify();
@ -160,6 +166,7 @@ impl TaskRunner {
} }
} }
#[async_trait]
pub trait TaskHandler: Send + Sync + 'static { pub trait TaskHandler: Send + Sync + 'static {
fn handler_id(&self) -> &str; fn handler_id(&self) -> &str;
@ -167,9 +174,10 @@ pub trait TaskHandler: Send + Sync + 'static {
"" ""
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error>; async fn run(&self, content: TaskContent) -> Result<(), Error>;
} }
#[async_trait]
impl<T> TaskHandler for Box<T> impl<T> TaskHandler for Box<T>
where where
T: TaskHandler, T: TaskHandler,
@ -182,11 +190,12 @@ where
(**self).handler_name() (**self).handler_name()
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> { async fn run(&self, content: TaskContent) -> Result<(), Error> {
(**self).run(content) (**self).run(content).await
} }
} }
#[async_trait]
impl<T> TaskHandler for Arc<T> impl<T> TaskHandler for Arc<T>
where where
T: TaskHandler, T: TaskHandler,
@ -199,7 +208,7 @@ where
(**self).handler_name() (**self).handler_name()
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> { async fn run(&self, content: TaskContent) -> Result<(), Error> {
(**self).run(content) (**self).run(content).await
} }
} }

View File

@ -170,13 +170,13 @@ impl RefCountValue for MockBlobTaskHandler {
async fn did_remove(&self) {} async fn did_remove(&self) {}
} }
#[async_trait]
impl TaskHandler for MockBlobTaskHandler { impl TaskHandler for MockBlobTaskHandler {
fn handler_id(&self) -> &str { fn handler_id(&self) -> &str {
"2" "2"
} }
fn run(&self, content: TaskContent) -> BoxResultFuture<(), Error> { async fn run(&self, content: TaskContent) -> Result<(), Error> {
Box::pin(async move {
match content { match content {
TaskContent::Text(_) => panic!("Only support blob"), TaskContent::Text(_) => panic!("Only support blob"),
TaskContent::Blob(bytes) => { TaskContent::Blob(bytes) => {
@ -185,7 +185,6 @@ impl TaskHandler for MockBlobTaskHandler {
}, },
} }
Ok(()) Ok(())
})
} }
} }