use anyhow::Result;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use surrealdb::types::{RecordId, SurrealValue, Table, Value as SurrealDbValue};
use crate::connection::get_db;
use crate::model::meta::{ModelMeta, ResolveRecordId};
use crate::model::relation::{RelationMeta, ensure_relation_name};
use crate::query::builder::QueryKind;
use crate::{ForeignModel, StoredModel};
#[derive(Debug, Serialize, Deserialize, SurrealValue)]
pub struct RelationEdge {
#[serde(rename = "in")]
pub _in: RecordId,
pub out: RecordId,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, SurrealValue)]
pub struct OrderedRelationEdge {
#[serde(rename = "in")]
pub _in: Option<RecordId>,
pub out: RecordId,
pub position: i64,
}
#[derive(Debug, Deserialize, SurrealValue)]
struct OrderedRelationEdgeRow {
source: RecordId,
out: RecordId,
position: i64,
}
impl From<OrderedRelationEdgeRow> for OrderedRelationEdge {
fn from(value: OrderedRelationEdgeRow) -> Self {
Self {
_in: Some(value.source),
out: value.out,
position: value.position,
}
}
}
pub struct GraphRepo;
impl GraphRepo {
pub async fn relate_at(in_id: RecordId, out_id: RecordId, rel: &str) -> Result<()> {
let db = get_db()?;
let sql = QueryKind::relate(&in_id, &out_id, rel);
db.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.bind(("out", out_id))
.await?
.check()?;
Ok(())
}
pub async fn back_relate_at(self_id: RecordId, target_id: RecordId, rel: &str) -> Result<()> {
Self::relate_at(target_id, self_id, rel).await
}
pub async fn unrelate_at(self_id: RecordId, target_id: RecordId, rel: &str) -> Result<()> {
let db = get_db()?;
db.query(QueryKind::unrelate(&self_id, &target_id, rel))
.bind(("rel", Table::from(rel)))
.bind(("in", self_id))
.bind(("out", target_id))
.await?
.check()?;
Ok(())
}
pub async fn unrelate_all(self_id: RecordId, rel: &str) -> Result<()> {
let db = get_db()?;
db.query(QueryKind::unrelate_all(&self_id, rel))
.bind(("rel", Table::from(rel)))
.bind(("in", self_id))
.await?
.check()?;
Ok(())
}
pub async fn out_ids(in_id: RecordId, rel: &str, out_table: &str) -> Result<Vec<RecordId>> {
let sql = QueryKind::select_out_ids(&in_id, rel, out_table);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.bind(("out_table", out_table.to_owned()))
.await?
.check()?;
let rows: Vec<RecordId> = result.take(0)?;
Ok(rows)
}
pub async fn outgoing_ids(in_id: RecordId, rel: &str) -> Result<Vec<RecordId>> {
let sql = QueryKind::select_all_out_ids(&in_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.await?
.check()?;
let rows: Vec<RecordId> = result.take(0)?;
Ok(rows)
}
pub async fn outgoing<T>(in_id: RecordId, rel: &str) -> Result<Vec<T>>
where
T: ModelMeta + StoredModel + ForeignModel,
T::Stored: serde::de::DeserializeOwned,
{
let sql = QueryKind::select_outgoing_rows(&in_id, rel, T::storage_table());
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.bind(("out_table", T::storage_table().to_owned()))
.await?
.check()?;
let rows: Vec<SurrealDbValue> = result.take(1)?;
crate::repository::raw_rows_to_public_hydrated::<T>(rows).await
}
pub async fn outgoing_count(in_id: RecordId, rel: &str) -> Result<i64> {
let sql = QueryKind::count_all_outgoing(&in_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.await?
.check()?;
let count: Option<i64> = result.take(0)?;
Ok(count.unwrap_or(0))
}
pub async fn outgoing_count_as<T>(in_id: RecordId, rel: &str) -> Result<i64>
where
T: ModelMeta + StoredModel + ForeignModel,
{
let sql = QueryKind::count_outgoing_in_table(&in_id, rel, T::storage_table());
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.bind(("out_table", T::storage_table().to_owned()))
.await?
.check()?;
let count: Option<i64> = result.take(0)?;
Ok(count.unwrap_or(0))
}
pub async fn out_edges(in_id: RecordId, rel: &str) -> Result<Vec<OrderedRelationEdge>> {
let sql = QueryKind::select_out_edges(&in_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("in", in_id))
.await?
.check()?;
let rows: Vec<OrderedRelationEdgeRow> = result.take(0)?;
Ok(rows.into_iter().map(OrderedRelationEdge::from).collect())
}
pub async fn in_ids(out_id: RecordId, rel: &str, in_table: &str) -> Result<Vec<RecordId>> {
let sql = QueryKind::select_in_ids(&out_id, rel, in_table);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.bind(("in_table", in_table.to_owned()))
.await?
.check()?;
let rows: Vec<RecordId> = result.take(0)?;
Ok(rows)
}
pub async fn in_edges(out_id: RecordId, rel: &str) -> Result<Vec<OrderedRelationEdge>> {
let sql = QueryKind::select_in_edges(&out_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.await?
.check()?;
let rows: Vec<OrderedRelationEdgeRow> = result.take(0)?;
Ok(rows.into_iter().map(OrderedRelationEdge::from).collect())
}
pub async fn incoming_ids(out_id: RecordId, rel: &str) -> Result<Vec<RecordId>> {
let sql = QueryKind::select_all_in_ids(&out_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.await?
.check()?;
let rows: Vec<RecordId> = result.take(0)?;
Ok(rows)
}
pub async fn incoming<T>(out_id: RecordId, rel: &str) -> Result<Vec<T>>
where
T: ModelMeta + StoredModel + ForeignModel,
T::Stored: serde::de::DeserializeOwned,
{
let sql = QueryKind::select_incoming_rows(&out_id, rel, T::storage_table());
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.bind(("in_table", T::storage_table().to_owned()))
.await?
.check()?;
let rows: Vec<SurrealDbValue> = result.take(1)?;
crate::repository::raw_rows_to_public_hydrated::<T>(rows).await
}
pub async fn incoming_count(out_id: RecordId, rel: &str) -> Result<i64> {
let sql = QueryKind::count_all_incoming(&out_id, rel);
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.await?
.check()?;
let count: Option<i64> = result.take(0)?;
Ok(count.unwrap_or(0))
}
pub async fn incoming_count_as<T>(out_id: RecordId, rel: &str) -> Result<i64>
where
T: ModelMeta + StoredModel + ForeignModel,
{
let sql = QueryKind::count_incoming_in_table(&out_id, rel, T::storage_table());
let db = get_db()?;
let mut result = db
.query(sql)
.bind(("rel", Table::from(rel)))
.bind(("out", out_id))
.bind(("in_table", T::storage_table().to_owned()))
.await?
.check()?;
let count: Option<i64> = result.take(0)?;
Ok(count.unwrap_or(0))
}
pub async fn insert_relation(rel: &str, data: Vec<RelationEdge>) -> Result<Vec<RelationEdge>> {
let db = get_db()?;
let relate: Vec<RelationEdge> = db.insert(rel).relation(data).await?;
Ok(relate)
}
}
#[async_trait]
pub trait GraphCrud: ResolveRecordId + Send + Sync {
async fn relate<R, T>(&self, target: &T) -> Result<()>
where
R: RelationMeta + Send + Sync,
T: ResolveRecordId + Send + Sync,
{
GraphRepo::relate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
R::relation_name(),
)
.await
}
async fn relate_by_name<T>(&self, target: &T, relation: &str) -> Result<()>
where
T: ResolveRecordId + Send + Sync,
{
ensure_relation_name(relation)?;
GraphRepo::relate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
relation,
)
.await
}
async fn back_relate<R, T>(&self, target: &T) -> Result<()>
where
R: RelationMeta + Send + Sync,
T: ResolveRecordId + Send + Sync,
{
GraphRepo::back_relate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
R::relation_name(),
)
.await
}
async fn back_relate_by_name<T>(&self, target: &T, relation: &str) -> Result<()>
where
T: ResolveRecordId + Send + Sync,
{
ensure_relation_name(relation)?;
GraphRepo::back_relate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
relation,
)
.await
}
async fn unrelate<R, T>(&self, target: &T) -> Result<()>
where
R: RelationMeta + Send + Sync,
T: ResolveRecordId + Send + Sync,
{
GraphRepo::unrelate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
R::relation_name(),
)
.await
}
async fn unrelate_by_name<T>(&self, target: &T, relation: &str) -> Result<()>
where
T: ResolveRecordId + Send + Sync,
{
ensure_relation_name(relation)?;
GraphRepo::unrelate_at(
self.resolve_record_id().await?,
target.resolve_record_id().await?,
relation,
)
.await
}
}
impl<T> GraphCrud for T where T: ResolveRecordId + Send + Sync {}
pub async fn relate_at(in_id: RecordId, out_id: RecordId, rel: &str) -> Result<()> {
GraphRepo::relate_at(in_id, out_id, rel).await
}
pub async fn back_relate_at(self_id: RecordId, target_id: RecordId, rel: &str) -> Result<()> {
GraphRepo::back_relate_at(self_id, target_id, rel).await
}
pub async fn unrelate_at(self_id: RecordId, target_id: RecordId, rel: &str) -> Result<()> {
GraphRepo::unrelate_at(self_id, target_id, rel).await
}
pub async fn out_ids(in_id: RecordId, rel: &str, out_table: &str) -> Result<Vec<RecordId>> {
GraphRepo::out_ids(in_id, rel, out_table).await
}
pub async fn outgoing_ids(in_id: RecordId, rel: &str) -> Result<Vec<RecordId>> {
GraphRepo::outgoing_ids(in_id, rel).await
}
pub async fn outgoing<T>(in_id: RecordId, rel: &str) -> Result<Vec<T>>
where
T: ModelMeta + StoredModel + ForeignModel,
T::Stored: serde::de::DeserializeOwned,
{
GraphRepo::outgoing::<T>(in_id, rel).await
}
pub async fn outgoing_count(in_id: RecordId, rel: &str) -> Result<i64> {
GraphRepo::outgoing_count(in_id, rel).await
}
pub async fn outgoing_count_as<T>(in_id: RecordId, rel: &str) -> Result<i64>
where
T: ModelMeta + StoredModel + ForeignModel,
{
GraphRepo::outgoing_count_as::<T>(in_id, rel).await
}
pub async fn out_edges(in_id: RecordId, rel: &str) -> Result<Vec<OrderedRelationEdge>> {
GraphRepo::out_edges(in_id, rel).await
}
pub async fn in_ids(out_id: RecordId, rel: &str, in_table: &str) -> Result<Vec<RecordId>> {
GraphRepo::in_ids(out_id, rel, in_table).await
}
pub async fn in_edges(out_id: RecordId, rel: &str) -> Result<Vec<OrderedRelationEdge>> {
GraphRepo::in_edges(out_id, rel).await
}
pub async fn incoming_ids(out_id: RecordId, rel: &str) -> Result<Vec<RecordId>> {
GraphRepo::incoming_ids(out_id, rel).await
}
pub async fn incoming<T>(out_id: RecordId, rel: &str) -> Result<Vec<T>>
where
T: ModelMeta + StoredModel + ForeignModel,
T::Stored: serde::de::DeserializeOwned,
{
GraphRepo::incoming::<T>(out_id, rel).await
}
pub async fn incoming_count(out_id: RecordId, rel: &str) -> Result<i64> {
GraphRepo::incoming_count(out_id, rel).await
}
pub async fn incoming_count_as<T>(out_id: RecordId, rel: &str) -> Result<i64>
where
T: ModelMeta + StoredModel + ForeignModel,
{
GraphRepo::incoming_count_as::<T>(out_id, rel).await
}