Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion catalyst-gateway/bin/src/db/event/common/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,28 @@
//! Reusable common database objects

pub(crate) mod eq_or_ranged_uuid;
pub(crate) mod query_limits;
pub(crate) mod uuid_list;
pub(crate) mod uuid_selector;

/// SQL conditional statement trait
pub(crate) trait ConditionalStmt {
/// Return a sql conditional statement by the provided `table_field`
fn conditional_stmt(
&self,
f: &mut std::fmt::Formatter<'_>,
table_field: &str,
) -> std::fmt::Result;
}

impl<T: ConditionalStmt> ConditionalStmt for Option<T> {
fn conditional_stmt(
&self,
f: &mut std::fmt::Formatter<'_>,
table_field: &str,
) -> std::fmt::Result {
if let Some(v) = self {
v.conditional_stmt(f, table_field)?;
}
Ok(())
}
}
39 changes: 39 additions & 0 deletions catalyst-gateway/bin/src/db/event/common/uuid_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//! `UuidList` query conditional stmt object.

use std::ops::Deref;

use itertools::Itertools;

use crate::db::event::common::ConditionalStmt;

/// Search by a list of UUIDs.
#[derive(Clone, Debug, PartialEq, Default)]
pub(crate) struct UuidList(Vec<uuid::Uuid>);

impl Deref for UuidList {
type Target = Vec<uuid::Uuid>;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl From<Vec<uuid::Uuid>> for UuidList {
fn from(value: Vec<uuid::Uuid>) -> Self {
Self(value)
}
}

impl ConditionalStmt for UuidList {
fn conditional_stmt(
&self,
f: &mut std::fmt::Formatter<'_>,
table_field: &str,
) -> std::fmt::Result {
write!(
f,
"{table_field} IN ({})",
self.0.iter().map(|uuid| format!("'{uuid}'")).join(",")
)
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! `EqOrRangedUuid` query conditional stmt object.

use crate::db::event::common::{ConditionalStmt, uuid_list::UuidList};

/// Search either by a singe UUID, a range of UUIDs or a list of UUIDs.
#[derive(Clone, Debug, PartialEq)]
pub(crate) enum UuidSelector {
Expand All @@ -13,27 +15,21 @@ pub(crate) enum UuidSelector {
max: uuid::Uuid,
},
/// Search UUIDs in the given list.
In(Vec<uuid::Uuid>),
In(UuidList),
}

impl UuidSelector {
/// Return a sql conditional statement by the provided `table_field`
pub(crate) fn conditional_stmt(
impl ConditionalStmt for UuidSelector {
fn conditional_stmt(
&self,
f: &mut std::fmt::Formatter<'_>,
table_field: &str,
) -> String {
) -> std::fmt::Result {
match self {
Self::Eq(id) => format!("{table_field} = '{id}'"),
Self::Eq(id) => write!(f, "{table_field} = '{id}'"),
Self::Range { min, max } => {
format!("{table_field} >= '{min}' AND {table_field} <= '{max}'")
},
Self::In(ids) => {
itertools::intersperse(
ids.iter().map(|id| format!("{table_field} = '{id}'")),
" OR ".to_string(),
)
.collect()
write!(f, "{table_field} >= '{min}' AND {table_field} <= '{max}'")
},
Self::In(ids) => ids.conditional_stmt(f, table_field),
}
}
}
36 changes: 15 additions & 21 deletions catalyst-gateway/bin/src/db/event/signed_docs/doc_ref.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
//! Document Reference filtering object.

use std::fmt::Write;

use crate::db::event::common::eq_or_ranged_uuid::UuidSelector;
use crate::db::event::common::{ConditionalStmt, uuid_selector::UuidSelector};

/// Document Reference filtering struct.
#[derive(Clone, Debug)]
Expand All @@ -13,30 +11,26 @@ pub(crate) struct DocumentRef {
pub(crate) ver: Option<UuidSelector>,
}

impl DocumentRef {
/// Return a sql conditional statement by the provided `table_field`
pub(crate) fn conditional_stmt(
impl ConditionalStmt for DocumentRef {
fn conditional_stmt(
&self,
f: &mut std::fmt::Formatter<'_>,
table_field: &str,
) -> String {
let mut stmt = "TRUE".to_string();
) -> std::fmt::Result {
write!(
f,
"EXISTS (SELECT 1 FROM JSONB_ARRAY_ELEMENTS({table_field}) AS doc_ref WHERE TRUE"
)?;

if let Some(id) = &self.id {
let _ = write!(
stmt,
" AND {}",
id.conditional_stmt("(doc_ref->>'id')::uuid")
);
write!(f, " AND ")?;
id.conditional_stmt(f, "(doc_ref->>'id')::uuid")?;
}
if let Some(ver) = &self.ver {
let _ = write!(
stmt,
" AND {}",
ver.conditional_stmt("(doc_ref->>'ver')::uuid")
);
write!(f, " AND ")?;
ver.conditional_stmt(f, "(doc_ref->>'id')::uuid")?;
}

format!(
"EXISTS (SELECT 1 FROM JSONB_ARRAY_ELEMENTS({table_field}) AS doc_ref WHERE {stmt})"
)
write!(f, ")")
}
}
137 changes: 70 additions & 67 deletions catalyst-gateway/bin/src/db/event/signed_docs/query_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,78 +3,78 @@
use std::fmt::Display;

use super::DocumentRef;
use crate::db::event::common::eq_or_ranged_uuid::UuidSelector;
use crate::db::event::common::{ConditionalStmt, uuid_list::UuidList, uuid_selector::UuidSelector};

/// A `select_signed_docs` query filtering argument.
/// If all fields would be `None` the query will search for all entries from the db.
#[derive(Clone, Debug, Default)]
pub(crate) struct DocsQueryFilter {
/// `type` field. Empty list if unspecified.
doc_type: Vec<uuid::Uuid>,
doc_type: UuidList,
/// `id` field. `None` if unspecified.
id: Option<UuidSelector>,
/// `ver` field. `None` if unspecified.
ver: Option<UuidSelector>,
/// `metadata->'ref'` field. Empty list if unspecified.
doc_ref: Vec<DocumentRef>,
/// `metadata->'template'` field. Empty list if unspecified.
template: Vec<DocumentRef>,
/// `metadata->'reply'` field. Empty list if unspecified.
reply: Vec<DocumentRef>,
/// `metadata->'parameters'` field. Empty list if unspecified.
parameters: Vec<DocumentRef>,
/// `metadata->'ref'` field.
doc_ref: Option<DocumentRef>,
/// `metadata->'template'` field.
template: Option<DocumentRef>,
/// `metadata->'reply'` field.
reply: Option<DocumentRef>,
/// `metadata->'parameters'` field.
parameters: Option<DocumentRef>,
}

impl Display for DocsQueryFilter {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
use std::fmt::Write;
let mut query = "TRUE".to_string();

if !self.doc_type.is_empty() {
write!(
&mut query,
" AND signed_docs.type IN ({})",
self.doc_type
.iter()
.map(|uuid| format!("'{uuid}'"))
.collect::<Vec<_>>()
.join(",")
)?;
}

if let Some(id) = &self.id {
write!(&mut query, " AND {}", id.conditional_stmt("signed_docs.id"))?;
}
if let Some(ver) = &self.ver {
write!(
&mut query,
" AND {}",
ver.conditional_stmt("signed_docs.ver")
)?;
}
let doc_ref_queries = [
("ref", &self.doc_ref),
("template", &self.template),
("reply", &self.reply),
("parameters", &self.parameters),
write!(f, "TRUE")?;

let stmts: [(_, Option<&dyn ConditionalStmt>); _] = [
(
"signed_docs.type",
(!self.doc_type.is_empty()).then_some(&self.doc_type),
),
(
"signed_docs.id",
self.id.as_ref().map(|v| -> &dyn ConditionalStmt { v }),
),
(
"signed_docs.ver",
self.ver.as_ref().map(|v| -> &dyn ConditionalStmt { v }),
),
(
"metadata->'ref'",
self.doc_ref.as_ref().map(|v| -> &dyn ConditionalStmt { v }),
),
(
"metadata->'template'",
self.template
.as_ref()
.map(|v| -> &dyn ConditionalStmt { v }),
),
(
"metadata->'reply'",
self.reply.as_ref().map(|v| -> &dyn ConditionalStmt { v }),
),
(
"metadata->'parameters'",
self.parameters
.as_ref()
.map(|v| -> &dyn ConditionalStmt { v }),
),
];

for (field_name, doc_refs) in doc_ref_queries {
if !doc_refs.is_empty() {
let stmt = doc_refs
.iter()
.map(|doc_ref| doc_ref.conditional_stmt(&format!("metadata->'{field_name}'")))
.collect::<Vec<_>>()
.join(" OR ");

write!(&mut query, " AND ({stmt})")?;
for (field_name, stmt) in stmts {
if let Some(stmt) = stmt {
write!(f, " AND ",)?;
stmt.conditional_stmt(f, field_name)?;
}
}

write!(f, "{query}")
Ok(())
}
}

Expand All @@ -89,7 +89,10 @@ impl DocsQueryFilter {
self,
doc_type: Vec<uuid::Uuid>,
) -> Self {
DocsQueryFilter { doc_type, ..self }
DocsQueryFilter {
doc_type: doc_type.into(),
..self
}
}

/// Set the `id` field filter condition
Expand Down Expand Up @@ -119,42 +122,42 @@ impl DocsQueryFilter {
self,
arg: DocumentRef,
) -> Self {
let mut doc_ref = self.doc_ref;
doc_ref.push(arg);

DocsQueryFilter { doc_ref, ..self }
DocsQueryFilter {
doc_ref: Some(arg),
..self
}
}

/// Set the `metadata->'template'` field filter condition
pub fn with_template(
self,
arg: DocumentRef,
) -> Self {
let mut template = self.template;
template.push(arg);

DocsQueryFilter { template, ..self }
DocsQueryFilter {
template: Some(arg),
..self
}
}

/// Set the `metadata->'reply'` field filter condition
pub fn with_reply(
self,
arg: DocumentRef,
) -> Self {
let mut reply = self.reply;
reply.push(arg);

DocsQueryFilter { reply, ..self }
DocsQueryFilter {
reply: Some(arg),
..self
}
}

/// Set the `metadata->'parameters'` field filter condition
pub fn with_parameters(
self,
arg: DocumentRef,
) -> Self {
let mut parameters = self.parameters;
parameters.push(arg);

DocsQueryFilter { parameters, ..self }
DocsQueryFilter {
parameters: Some(arg),
..self
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use futures::TryStreamExt;

use super::*;
use crate::db::event::{
common::{eq_or_ranged_uuid::UuidSelector, query_limits::QueryLimits},
common::{query_limits::QueryLimits, uuid_selector::UuidSelector},
establish_connection_pool,
};

Expand Down
Loading
Loading