From 55a795a623b3f47a6ed221b5d4b36f980604d5ee Mon Sep 17 00:00:00 2001 From: ljl <17743125563@163.com> Date: Tue, 3 Dec 2024 10:17:07 +0800 Subject: [PATCH] spi-search:fix split query ext. --- .../spi/spi-search/src/dto/search_item_dto.rs | 22 +- .../src/serv/es/search_es_item_serv.rs | 3 +- .../src/serv/pg/search_pg_item_serv.rs | 767 +++++++----------- 3 files changed, 302 insertions(+), 490 deletions(-) diff --git a/backend/spi/spi-search/src/dto/search_item_dto.rs b/backend/spi/spi-search/src/dto/search_item_dto.rs index 654f1fc3..3c7483c4 100644 --- a/backend/spi/spi-search/src/dto/search_item_dto.rs +++ b/backend/spi/spi-search/src/dto/search_item_dto.rs @@ -203,27 +203,7 @@ pub struct AdvSearchItemQueryReq { pub group_by_or: Option, pub ext_by_or: Option, // Extended filtering conditions - pub ext: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -#[cfg_attr(feature = "default", derive(poem_openapi::Object))] -pub struct AdvBasicQueryCondInfo { - pub in_ext: Option, - #[oai(validator(min_length = "1"))] - pub field: String, - pub op: BasicQueryOpKind, - pub value: Value, -} - -impl From for BasicQueryCondInfo { - fn from(value: AdvBasicQueryCondInfo) -> Self { - Self { - field: value.field, - op: value.op, - value: value.value, - } - } + pub ext: Option>, } #[derive(poem_openapi::Object, Serialize, Deserialize, Debug, Default, Clone)] diff --git a/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs b/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs index 7d782d9d..140c2254 100644 --- a/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs +++ b/backend/spi/spi-search/src/serv/es/search_es_item_serv.rs @@ -19,6 +19,7 @@ use crate::dto::search_item_dto::{ }; use super::search_es_initializer; +const INNER_FIELD: [&str; 7] = ["key", "title", "content", "owner", "own_paths", "create_time", "update_time"]; fn format_index(req_index: &str, ext: &HashMap) -> String { if let Some(key_prefix) = common::get_isolation_flag_from_ext(ext) { @@ -643,7 +644,7 @@ fn gen_query_dsl(search_req: &SearchItemSearchReq) -> TardisResult { for group_query in adv_query { let mut group_query_q: Vec = vec![]; for cond_info in group_query.ext.clone().unwrap_or_default() { - let field = if cond_info.in_ext.unwrap_or(true) { + let field = if !INNER_FIELD.contains(&cond_info.field.clone().as_str()) { format!("ext.{}", cond_info.field) } else { cond_info.field.clone() diff --git a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs index 66c4ae4d..2334f449 100644 --- a/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs +++ b/backend/spi/spi-search/src/serv/pg/search_pg_item_serv.rs @@ -19,9 +19,8 @@ use bios_basic::{dto::BasicQueryCondInfo, enumeration::BasicQueryOpKind, helper: use crate::{ dto::search_item_dto::{ - AdvBasicQueryCondInfo, AdvSearchItemQueryReq, GroupSearchItemSearchReq, GroupSearchItemSearchResp, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, - SearchItemSearchCtxReq, SearchItemSearchPageReq, SearchItemSearchQScopeKind, SearchItemSearchReq, SearchItemSearchResp, SearchItemSearchSortReq, SearchQueryMetricsReq, - SearchQueryMetricsResp, + AdvSearchItemQueryReq, GroupSearchItemSearchReq, GroupSearchItemSearchResp, SearchItemAddReq, SearchItemModifyReq, SearchItemQueryReq, SearchItemSearchCtxReq, + SearchItemSearchPageReq, SearchItemSearchQScopeKind, SearchItemSearchReq, SearchItemSearchResp, SearchItemSearchSortReq, SearchQueryMetricsReq, SearchQueryMetricsResp, }, search_config::SearchConfig, }; @@ -416,14 +415,6 @@ fn package_query( where_fragments: &mut Vec, funs: &TardisFunsInst, ) -> TardisResult<(String, String)> { - let err_not_found = |ext_item: &BasicQueryCondInfo| { - Err(funs.err().not_found( - "item", - "search", - &format!("The ext field=[{}] value=[{}] operation=[{}] is not legal.", &ext_item.field, ext_item.value, &ext_item.op,), - "404-spi-search-op-not-legal", - )) - }; let select_fragments; let mut from_fragments = "".to_string(); if let Some(q) = &query.q { @@ -561,126 +552,362 @@ fn package_query( sql_vals.push(Value::from(update_time_end)); where_fragments.push(format!("{}.update_time <= ${}", table_alias_name, sql_vals.len())); } + package_ext(table_alias_name, query.ext.clone(), sql_vals, where_fragments, funs)?; + Ok((select_fragments, from_fragments)) +} + +fn package_adv_query(table_alias_name: &str, adv_query: Option>, sql_vals: &mut Vec, funs: &TardisFunsInst) -> TardisResult> { + // advanced query + let mut sql_adv_query = vec![]; + if let Some(adv_query) = &adv_query { + for group_query in adv_query { + let mut sql_and_where = vec![]; + package_ext(table_alias_name, group_query.ext.clone(), sql_vals, &mut sql_and_where, funs)?; + if !sql_and_where.is_empty() { + sql_adv_query.push(format!( + " {} ( {} )", + if group_query.group_by_or.unwrap_or(false) { "OR" } else { "AND" }, + sql_and_where.join(if group_query.ext_by_or.unwrap_or(false) { " OR " } else { " AND " }) + )); + } + } + } + Ok(sql_adv_query) +} + +fn package_order(table_alias_name: &str, sort: Option>) -> TardisResult> { + let mut order_fragments: Vec = Vec::new(); + if let Some(sort) = &sort { + for sort_item in sort { + if sort_item.field.to_lowercase() == "key" + || sort_item.field.to_lowercase() == "title" + || sort_item.field.to_lowercase() == "content" + || sort_item.field.to_lowercase() == "owner" + || sort_item.field.to_lowercase() == "own_paths" + || sort_item.field.to_lowercase() == "create_time" + || sort_item.field.to_lowercase() == "update_time" + { + order_fragments.push(format!("{}.{} {}", table_alias_name, sort_item.field, sort_item.order.to_sql())); + } else { + order_fragments.push(format!("{}.ext -> '{}' {}", table_alias_name, sort_item.field, sort_item.order.to_sql())); + } + } + } + Ok(order_fragments) +} + +fn package_visit_filter(table_alias_name: &str, ctx: SearchItemSearchCtxReq, sql_vals: &mut Vec, where_fragments: &mut Vec) -> TardisResult<()> { + // Add visit_keys filter + let req_ctx = ctx.to_sql(); + if !req_ctx.is_empty() { + let mut where_visit_keys_fragments = Vec::new(); + for (scope_key, scope_values) in req_ctx { + if scope_values.is_empty() { + continue; + } + if scope_values.len() == 1 { + where_visit_keys_fragments.push(format!("{}.visit_keys -> '{scope_key}' ? ${}", table_alias_name, sql_vals.len() + 1)); + } else { + where_visit_keys_fragments.push(format!( + "{}.visit_keys -> '{scope_key}' ?| array[{}]", + table_alias_name, + (0..scope_values.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") + )); + } + for scope_value in scope_values { + sql_vals.push(Value::from(scope_value)); + } + } + where_fragments.push(format!( + "({}.visit_keys IS NULL OR ({}))", + table_alias_name, + where_visit_keys_fragments.join(if ctx.cond_by_or.unwrap_or(false) { " OR " } else { " AND " }) + )); + } + Ok(()) +} - if let Some(ext) = &query.ext { +fn package_page(page: SearchItemSearchPageReq, sql_vals: &mut Vec) -> TardisResult { + sql_vals.push(Value::from(page.size)); + sql_vals.push(Value::from((page.number - 1) * page.size as u32)); + Ok(format!("LIMIT ${} OFFSET ${}", sql_vals.len() - 1, sql_vals.len())) +} + +fn package_ext( + table_alias_name: &str, + ext: Option>, + sql_vals: &mut Vec, + sql_and_where: &mut Vec, + funs: &TardisFunsInst, +) -> TardisResult<()> { + let err_not_found = |ext_item: &BasicQueryCondInfo| { + Err(funs.err().not_found( + "item", + "search", + &format!("The ext field=[{}] value=[{}] operation=[{}] is not legal.", &ext_item.field, ext_item.value, &ext_item.op,), + "404-spi-search-op-not-legal", + )) + }; + if let Some(ext) = &ext { for ext_item in ext { let value = db_helper::json_to_sea_orm_value(&ext_item.value, &ext_item.op); - let Some(mut value) = value else { return err_not_found(ext_item) }; - if ext_item.op == BasicQueryOpKind::In { - let value = value.clone(); - if value.len() == 1 { - where_fragments.push(format!("{}.ext -> '{}' ? ${}", table_alias_name, ext_item.field, sql_vals.len() + 1)); + let Some(mut value) = value else { return err_not_found(&ext_item.clone().into()) }; + if !INNER_FIELD.contains(&ext_item.field.clone().as_str()) { + if ext_item.op == BasicQueryOpKind::In { + if value.len() == 1 { + sql_and_where.push(format!("{}.ext -> '{}' ? ${}", table_alias_name, ext_item.field, sql_vals.len() + 1)); + } else { + sql_and_where.push(format!( + "{}.ext -> '{}' ?| array[{}]", + table_alias_name, + ext_item.field, + (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") + )); + } + for val in value { + sql_vals.push(val); + } + } else if ext_item.op == BasicQueryOpKind::NotIn { + let value = value.clone(); + if value.len() == 1 { + sql_and_where.push(format!("not ({}.ext -> '{}' ? ${})", table_alias_name, ext_item.field, sql_vals.len() + 1)); + } else { + sql_and_where.push(format!( + "not ({}.ext -> '{}' ?| array[{}])", + table_alias_name, + ext_item.field, + (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") + )); + } + for val in value { + sql_vals.push(val); + } + } else if ext_item.op == BasicQueryOpKind::IsNull { + sql_and_where.push(format!("{}.ext ->> '{}' is null", table_alias_name, ext_item.field)); + } else if ext_item.op == BasicQueryOpKind::IsNotNull { + sql_and_where.push(format!( + "({}.ext ->> '{}' is not null and {}.ext ->> '{}' != '' and {}.ext ->> '{}' != '[]')", + table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field + )); + } else if ext_item.op == BasicQueryOpKind::IsNullOrEmpty { + sql_and_where.push(format!( + "({}.ext ->> '{}' is null or {}.ext ->> '{}' = '' or {}.ext ->> '{}' = '[]')", + table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field + )); } else { - where_fragments.push(format!( - "{}.ext -> '{}' ?| array[{}]", + if value.len() > 1 { + return err_not_found(&ext_item.clone().into()); + } + let Some(value) = value.pop() else { + return Err(funs.err().bad_request("item", "search", "Request item using 'IN' operator show hava a value", "400-spi-item-op-in-without-value")); + }; + if let Value::Bool(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::boolean {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::TinyInt(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::smallint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::SmallInt(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::smallint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::Int(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::integer {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::BigInt(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::bigint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::TinyUnsigned(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::smallint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::SmallUnsigned(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::integer {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::Unsigned(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::bigint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::BigUnsigned(_) = value { + // TODO + sql_and_where.push(format!( + "({}.ext ->> '{}')::bigint {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::Float(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::real {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if let Value::Double(_) = value { + sql_and_where.push(format!( + "({}.ext ->> '{}')::double precision {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else if value.is_chrono_date_time_utc() { + sql_and_where.push(format!( + "({}.ext ->> '{}')::timestamp with time zone {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } else { + sql_and_where.push(format!( + "{}.ext ->> '{}' {} ${}", + table_alias_name, + ext_item.field, + ext_item.op.to_sql(), + sql_vals.len() + 1 + )); + } + sql_vals.push(value); + } + } else if ext_item.op == BasicQueryOpKind::In { + if !value.is_empty() { + sql_and_where.push(format!( + "{}.{} IN ({})", table_alias_name, ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") + (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(",") )); - } - for val in value { - sql_vals.push(val); + for val in value { + sql_vals.push(val); + } } } else if ext_item.op == BasicQueryOpKind::NotIn { - let value = value.clone(); - if value.len() == 1 { - where_fragments.push(format!("not ({}.ext -> '{}' ? ${})", table_alias_name, ext_item.field, sql_vals.len() + 1)); - } else { - where_fragments.push(format!( - "not ({}.ext -> '{}' ?| array[{}])", + if !value.is_empty() { + sql_and_where.push(format!( + "{}.{} NOT IN ({})", table_alias_name, ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") + (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(",") )); - } - for val in value { - sql_vals.push(val); + for val in value { + sql_vals.push(val); + } } } else if ext_item.op == BasicQueryOpKind::IsNull { - where_fragments.push(format!("{}.ext ->> '{}' is null", table_alias_name, ext_item.field)); + sql_and_where.push(format!("{}.{} is null", table_alias_name, ext_item.field)); } else if ext_item.op == BasicQueryOpKind::IsNotNull { - where_fragments.push(format!( - "({}.ext ->> '{}' is not null and {}.ext ->> '{}' != '' and {}.ext ->> '{}' != '[]')", - table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field - )); + sql_and_where.push(format!("({}.{} is not null)", table_alias_name, ext_item.field)); } else if ext_item.op == BasicQueryOpKind::IsNullOrEmpty { - where_fragments.push(format!( - "({}.ext ->> '{}' is null or {}.ext ->> '{}' = '' or {}.ext ->> '{}' = '[]' )", - table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field + sql_and_where.push(format!( + "({}.{} is null or {}.{}::text = '' )", + table_alias_name, ext_item.field, table_alias_name, ext_item.field )); - } else if ext_item.op == BasicQueryOpKind::Len { - if let Some(first_value) = value.pop() { - where_fragments.push(format!("(length({}.ext->>'{}') = ${})", table_alias_name, ext_item.field, sql_vals.len() + 1)); - sql_vals.push(first_value); - } else { - return err_not_found(ext_item); - }; } else { if value.len() > 1 { - return err_not_found(ext_item); + return err_not_found(&ext_item.clone().into()); } let Some(value) = value.pop() else { - return Err(funs.err().bad_request("item", "search", "Request item using 'IN' operator show hava a value", "400-spi-item-op-in-without-value")); + return Err(funs.err().bad_request("item", "search", "Request item using 'IN' operator show have a value", "400-spi-item-op-in-without-value")); }; if let Value::Bool(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::boolean {} ${}", + sql_and_where.push(format!( + "({}.{}::boolean) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::TinyInt(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", + sql_and_where.push(format!( + "({}.{}::smallint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::SmallInt(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", + sql_and_where.push(format!( + "({}.{}::smallint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::Int(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::integer {} ${}", + sql_and_where.push(format!( + "({}.{}::integer) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::BigInt(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", + sql_and_where.push(format!( + "({}.{}::bigint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::TinyUnsigned(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", + sql_and_where.push(format!( + "({}.{}::smallint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::SmallUnsigned(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::integer {} ${}", + sql_and_where.push(format!( + "({}.{}::integer) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::Unsigned(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", + sql_and_where.push(format!( + "({}.{}::bigint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), @@ -688,428 +915,32 @@ fn package_query( )); } else if let Value::BigUnsigned(_) = value { // TODO - where_fragments.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", + sql_and_where.push(format!( + "({}.{}::bigint) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else if let Value::Float(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::real {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); + sql_and_where.push(format!("({}.{}::real) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1)); } else if let Value::Double(_) = value { - where_fragments.push(format!( - "({}.ext ->> '{}')::double precision {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if value.is_chrono_date_time_utc() { - where_fragments.push(format!( - "({}.ext ->> '{}')::timestamp with time zone {} ${}", + sql_and_where.push(format!( + "({}.{}::double precision) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1 )); } else { - where_fragments.push(format!( - "{}.ext ->> '{}' {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); + sql_and_where.push(format!("{}.{} {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1)); } sql_vals.push(value); } } } - Ok((select_fragments, from_fragments)) -} - -fn package_adv_query(table_alias_name: &str, adv_query: Option>, sql_vals: &mut Vec, funs: &TardisFunsInst) -> TardisResult> { - let err_not_found = |ext_item: &BasicQueryCondInfo| { - Err(funs.err().not_found( - "item", - "search", - &format!("The ext field=[{}] value=[{}] operation=[{}] is not legal.", &ext_item.field, ext_item.value, &ext_item.op,), - "404-spi-search-op-not-legal", - )) - }; - // advanced query - let mut sql_adv_query = vec![]; - if let Some(adv_query) = &adv_query { - for group_query in adv_query { - let mut sql_and_where = vec![]; - if let Some(ext) = &group_query.ext { - for ext_item in ext { - let value = db_helper::json_to_sea_orm_value(&ext_item.value, &ext_item.op); - let Some(mut value) = value else { return err_not_found(&ext_item.clone().into()) }; - if !INNER_FIELD.contains(&ext_item.field.clone().as_str()) { - if ext_item.op == BasicQueryOpKind::In { - if value.len() == 1 { - sql_and_where.push(format!("{}.ext -> '{}' ? ${}", table_alias_name, ext_item.field, sql_vals.len() + 1)); - } else { - sql_and_where.push(format!( - "{}.ext -> '{}' ?| array[{}]", - table_alias_name, - ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") - )); - } - for val in value { - sql_vals.push(val); - } - } else if ext_item.op == BasicQueryOpKind::NotIn { - let value = value.clone(); - if value.len() == 1 { - sql_and_where.push(format!("not ({}.ext -> '{}' ? ${})", table_alias_name, ext_item.field, sql_vals.len() + 1)); - } else { - sql_and_where.push(format!( - "not ({}.ext -> '{}' ?| array[{}])", - table_alias_name, - ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") - )); - } - for val in value { - sql_vals.push(val); - } - } else if ext_item.op == BasicQueryOpKind::IsNull { - sql_and_where.push(format!("{}.ext ->> '{}' is null", table_alias_name, ext_item.field)); - } else if ext_item.op == BasicQueryOpKind::IsNotNull { - sql_and_where.push(format!( - "({}.ext ->> '{}' is not null and {}.ext ->> '{}' != '' and {}.ext ->> '{}' != '[]')", - table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field - )); - } else if ext_item.op == BasicQueryOpKind::IsNullOrEmpty { - sql_and_where.push(format!( - "({}.ext ->> '{}' is null or {}.ext ->> '{}' = '' or {}.ext ->> '{}' = '[]')", - table_alias_name, ext_item.field, table_alias_name, ext_item.field, table_alias_name, ext_item.field - )); - } else { - if value.len() > 1 { - return err_not_found(&ext_item.clone().into()); - } - let Some(value) = value.pop() else { - return Err(funs.err().bad_request("item", "search", "Request item using 'IN' operator show hava a value", "400-spi-item-op-in-without-value")); - }; - if let Value::Bool(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::boolean {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::TinyInt(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::SmallInt(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Int(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::integer {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::BigInt(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::TinyUnsigned(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::smallint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::SmallUnsigned(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::integer {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Unsigned(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::BigUnsigned(_) = value { - // TODO - sql_and_where.push(format!( - "({}.ext ->> '{}')::bigint {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Float(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::real {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Double(_) = value { - sql_and_where.push(format!( - "({}.ext ->> '{}')::double precision {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if value.is_chrono_date_time_utc() { - sql_and_where.push(format!( - "({}.ext ->> '{}')::timestamp with time zone {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else { - sql_and_where.push(format!( - "{}.ext ->> '{}' {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } - sql_vals.push(value); - } - } else if ext_item.op == BasicQueryOpKind::In { - if !value.is_empty() { - sql_and_where.push(format!( - "{}.{} IN ({})", - table_alias_name, - ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(",") - )); - for val in value { - sql_vals.push(val); - } - } - } else if ext_item.op == BasicQueryOpKind::NotIn { - if !value.is_empty() { - sql_and_where.push(format!( - "{}.{} NOT IN ({})", - table_alias_name, - ext_item.field, - (0..value.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(",") - )); - for val in value { - sql_vals.push(val); - } - } - } else if ext_item.op == BasicQueryOpKind::IsNull { - sql_and_where.push(format!("{}.{} is null", table_alias_name, ext_item.field)); - } else if ext_item.op == BasicQueryOpKind::IsNotNull { - sql_and_where.push(format!("({}.{} is not null)", table_alias_name, ext_item.field)); - } else if ext_item.op == BasicQueryOpKind::IsNullOrEmpty { - sql_and_where.push(format!( - "({}.{} is null or {}.{}::text = '' )", - table_alias_name, ext_item.field, table_alias_name, ext_item.field - )); - } else { - if value.len() > 1 { - return err_not_found(&ext_item.clone().into()); - } - let Some(value) = value.pop() else { - return Err(funs.err().bad_request("item", "search", "Request item using 'IN' operator show have a value", "400-spi-item-op-in-without-value")); - }; - if let Value::Bool(_) = value { - sql_and_where.push(format!( - "({}.{}::boolean) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::TinyInt(_) = value { - sql_and_where.push(format!( - "({}.{}::smallint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::SmallInt(_) = value { - sql_and_where.push(format!( - "({}.{}::smallint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Int(_) = value { - sql_and_where.push(format!( - "({}.{}::integer) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::BigInt(_) = value { - sql_and_where.push(format!( - "({}.{}::bigint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::TinyUnsigned(_) = value { - sql_and_where.push(format!( - "({}.{}::smallint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::SmallUnsigned(_) = value { - sql_and_where.push(format!( - "({}.{}::integer) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Unsigned(_) = value { - sql_and_where.push(format!( - "({}.{}::bigint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::BigUnsigned(_) = value { - // TODO - sql_and_where.push(format!( - "({}.{}::bigint) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else if let Value::Float(_) = value { - sql_and_where.push(format!("({}.{}::real) {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1)); - } else if let Value::Double(_) = value { - sql_and_where.push(format!( - "({}.{}::double precision) {} ${}", - table_alias_name, - ext_item.field, - ext_item.op.to_sql(), - sql_vals.len() + 1 - )); - } else { - sql_and_where.push(format!("{}.{} {} ${}", table_alias_name, ext_item.field, ext_item.op.to_sql(), sql_vals.len() + 1)); - } - sql_vals.push(value); - } - } - } - if !sql_and_where.is_empty() { - sql_adv_query.push(format!( - " {} ( {} )", - if group_query.group_by_or.unwrap_or(false) { "OR" } else { "AND" }, - sql_and_where.join(if group_query.ext_by_or.unwrap_or(false) { " OR " } else { " AND " }) - )); - } - } - } - Ok(sql_adv_query) -} - -fn package_order(table_alias_name: &str, sort: Option>) -> TardisResult> { - let mut order_fragments: Vec = Vec::new(); - if let Some(sort) = &sort { - for sort_item in sort { - if sort_item.field.to_lowercase() == "key" - || sort_item.field.to_lowercase() == "title" - || sort_item.field.to_lowercase() == "content" - || sort_item.field.to_lowercase() == "owner" - || sort_item.field.to_lowercase() == "own_paths" - || sort_item.field.to_lowercase() == "create_time" - || sort_item.field.to_lowercase() == "update_time" - { - order_fragments.push(format!("{}.{} {}", table_alias_name, sort_item.field, sort_item.order.to_sql())); - } else { - order_fragments.push(format!("{}.ext -> '{}' {}", table_alias_name, sort_item.field, sort_item.order.to_sql())); - } - } - } - Ok(order_fragments) -} - -fn package_visit_filter(table_alias_name: &str, ctx: SearchItemSearchCtxReq, sql_vals: &mut Vec, where_fragments: &mut Vec) -> TardisResult<()> { - // Add visit_keys filter - let req_ctx = ctx.to_sql(); - if !req_ctx.is_empty() { - let mut where_visit_keys_fragments = Vec::new(); - for (scope_key, scope_values) in req_ctx { - if scope_values.is_empty() { - continue; - } - if scope_values.len() == 1 { - where_visit_keys_fragments.push(format!("{}.visit_keys -> '{scope_key}' ? ${}", table_alias_name, sql_vals.len() + 1)); - } else { - where_visit_keys_fragments.push(format!( - "{}.visit_keys -> '{scope_key}' ?| array[{}]", - table_alias_name, - (0..scope_values.len()).map(|idx| format!("${}", sql_vals.len() + idx + 1)).collect::>().join(", ") - )); - } - for scope_value in scope_values { - sql_vals.push(Value::from(scope_value)); - } - } - where_fragments.push(format!( - "({}.visit_keys IS NULL OR ({}))", - table_alias_name, - where_visit_keys_fragments.join(if ctx.cond_by_or.unwrap_or(false) { " OR " } else { " AND " }) - )); - } Ok(()) } - -fn package_page(page: SearchItemSearchPageReq, sql_vals: &mut Vec) -> TardisResult { - sql_vals.push(Value::from(page.size)); - sql_vals.push(Value::from((page.number - 1) * page.size as u32)); - Ok(format!("LIMIT ${} OFFSET ${}", sql_vals.len() - 1, sql_vals.len())) -} - fn merge(a: &mut serde_json::Value, b: serde_json::Value) { match (a, b) { (a @ &mut serde_json::Value::Object(_), serde_json::Value::Object(b)) => {