Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[db] Add select edge_count #1084 #1087

Merged
merged 8 commits into from
Apr 22, 2024
Merged
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
15 changes: 15 additions & 0 deletions agdb/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,21 @@ impl<Store: StorageData> DbImpl<Store> {
}
}

pub(crate) fn edge_count(&self, db_id: DbId, from: bool, to: bool) -> Result<u64, QueryError> {
let index = self.graph_index(db_id.0)?;

if let Some(node) = self.graph.node(&self.storage, index) {
return Ok(match (from, to) {
(true, true) => node.edge_count(),
(true, false) => node.edge_count_from(),
(false, true) => node.edge_count_to(),
(false, false) => 0,
});
}

Ok(0)
}

#[allow(clippy::wrong_self_convention)]
pub(crate) fn from_id(&self, id: DbId) -> Option<DbId> {
if id.0 < 0 {
Expand Down
1 change: 1 addition & 0 deletions agdb/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub use query::search_query::SearchQuery;
pub use query::search_query::SearchQueryAlgorithm;
pub use query::select_aliases_query::SelectAliasesQuery;
pub use query::select_all_aliases_query::SelectAllAliasesQuery;
pub use query::select_edge_count_query::SelectEdgeCountQuery;
pub use query::select_indexes_query::SelectIndexesQuery;
pub use query::select_key_count_query::SelectKeyCountQuery;
pub use query::select_keys_query::SelectKeysQuery;
Expand Down
14 changes: 12 additions & 2 deletions agdb/src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub mod remove_values_query;
pub mod search_query;
pub mod select_aliases_query;
pub mod select_all_aliases_query;
pub mod select_edge_count_query;
pub mod select_indexes_query;
pub mod select_key_count_query;
pub mod select_keys_query;
Expand Down Expand Up @@ -47,8 +48,8 @@ pub trait QueryMut {
use crate::{
InsertAliasesQuery, InsertEdgesQuery, InsertIndexQuery, InsertNodesQuery, InsertValuesQuery,
RemoveAliasesQuery, RemoveIndexQuery, RemoveQuery, RemoveValuesQuery, SearchQuery,
SelectAliasesQuery, SelectAllAliasesQuery, SelectIndexesQuery, SelectKeyCountQuery,
SelectKeysQuery, SelectQuery, SelectValuesQuery,
SelectAliasesQuery, SelectAllAliasesQuery, SelectEdgeCountQuery, SelectIndexesQuery,
SelectKeyCountQuery, SelectKeysQuery, SelectQuery, SelectValuesQuery,
};

/// Convenience enum for serializing/deserializing queries.
Expand All @@ -70,6 +71,7 @@ pub enum QueryType {
Select(SelectQuery),
SelectAliases(SelectAliasesQuery),
SelectAllAliases(SelectAllAliasesQuery),
SelectEdgeCount(SelectEdgeCountQuery),
SelectIndexes(SelectIndexesQuery),
SelectKeys(SelectKeysQuery),
SelectKeyCount(SelectKeyCountQuery),
Expand Down Expand Up @@ -167,6 +169,13 @@ impl From<SelectAllAliasesQuery> for QueryType {
}
}

#[cfg(any(feature = "serde", feature = "opeanapi"))]
impl From<SelectEdgeCountQuery> for QueryType {
fn from(value: SelectEdgeCountQuery) -> Self {
QueryType::SelectEdgeCount(value)
}
}

#[cfg(any(feature = "serde", feature = "opeanapi"))]
impl From<SelectIndexesQuery> for QueryType {
fn from(value: SelectIndexesQuery) -> Self {
Expand Down Expand Up @@ -229,6 +238,7 @@ mod tests {
QueryBuilder::select().indexes().query().into(),
QueryBuilder::select().keys().ids(1).query().into(),
QueryBuilder::select().key_count().ids(1).query().into(),
QueryBuilder::select().edge_count().ids(1).query().into(),
QueryBuilder::select()
.values(vec!["key".into()])
.ids(1)
Expand Down
68 changes: 68 additions & 0 deletions agdb/src/query/select_edge_count_query.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use crate::DbElement;
use crate::Query;
use crate::QueryIds;
use crate::QueryResult;

/// Query to select number of edges of given node ids.
/// All of the ids must exist in the database. If any
/// of the ids is not a node the result will be 0 (not
/// an error).
///
/// The result will be number of elements returned and the list
/// of elements with a single property `String("edge_count")` with
/// a value `u64`.
///
/// NOTE: Self-referential edges are counted twice as if they
/// were coming from another edge. Therefore the edge count
/// might be greater than number of unique db elements.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, PartialEq)]
pub struct SelectEdgeCountQuery {
/// Ids of the nodes to select edge count for.
pub ids: QueryIds,

/// If set to `true` the query will count outgoing edges
/// from the nodes.
pub from: bool,

/// If set to `true` the query will count incoming edges
/// to the nodes.
pub to: bool,
}

impl Query for SelectEdgeCountQuery {
fn process<Store: crate::StorageData>(
&self,
db: &crate::DbImpl<Store>,
) -> Result<crate::QueryResult, crate::QueryError> {
let mut result = QueryResult::default();

let db_ids = match &self.ids {
QueryIds::Ids(ids) => {
let mut db_ids = Vec::with_capacity(ids.len());

for query_id in ids {
db_ids.push(db.db_id(query_id)?);
}

db_ids
}
QueryIds::Search(search_query) => search_query.search(db)?,
};

result.elements.reserve(db_ids.len());
result.result = db_ids.len() as i64;

for id in db_ids {
result.elements.push(DbElement {
id,
from: db.from_id(id),
to: db.to_id(id),
values: vec![("edge_count", db.edge_count(id, self.from, self.to)?).into()],
});
}

Ok(result)
}
}
1 change: 1 addition & 0 deletions agdb/src/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ mod remove_values;
mod search;
mod select;
mod select_aliases;
mod select_edge_count;
mod select_ids;
mod select_indexes;
mod select_key_count;
Expand Down
37 changes: 36 additions & 1 deletion agdb/src/query_builder/select.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use crate::query::query_values::QueryKeys;
use crate::query_builder::select_aliases::SelectAliases;
use crate::query_builder::select_edge_count::SelectEdgeCount;
use crate::query_builder::select_ids::SelectIds;
use crate::query_builder::select_indexes::SelectIndexes;
use crate::query_builder::select_key_count::SelectKeyCount;
use crate::query_builder::select_keys::SelectKeys;
use crate::query_builder::select_values::SelectValues;
use crate::QueryIds;
use crate::SelectAliasesQuery;
use crate::SelectEdgeCountQuery;
use crate::SelectKeyCountQuery;
use crate::SelectKeysQuery;
use crate::SelectQuery;
Expand All @@ -27,6 +29,39 @@ impl Select {
SelectAliases(SelectAliasesQuery(QueryIds::Ids(vec![])))
}

/// Select number of outgoing and incoming edges. Each
/// element of the result withll have a proeprty `String("edge_count")`
/// with u64 as the value.
pub fn edge_count(self) -> SelectEdgeCount {
SelectEdgeCount(SelectEdgeCountQuery {
ids: QueryIds::Ids(vec![0.into()]),
from: true,
to: true,
})
}

/// Select number of outgoing edges. Each
/// element of the result withll have a proeprty `String("edge_count")`
/// with u64 as the value.
pub fn edge_count_from(self) -> SelectEdgeCount {
SelectEdgeCount(SelectEdgeCountQuery {
ids: QueryIds::Ids(vec![0.into()]),
from: true,
to: false,
})
}

/// Select number of incoming edges. Each
/// element of the result withll have a proeprty `String("edge_count")`
/// with u64 as the value.
pub fn edge_count_to(self) -> SelectEdgeCount {
SelectEdgeCount(SelectEdgeCountQuery {
ids: QueryIds::Ids(vec![0.into()]),
from: false,
to: true,
})
}

/// Select elements with `ids` with all properties (key-values).
/// All ids specified must exist in the database.
pub fn ids<T: Into<QueryIds>>(self, ids: T) -> SelectIds {
Expand All @@ -46,7 +81,7 @@ impl Select {
}

/// Select number of keys. Each element of the result will have
/// a property `String("key_count")` with `i64` as the value.
/// a property `String("key_count")` with `u64` as the value.
pub fn key_count(self) -> SelectKeyCount {
SelectKeyCount(SelectKeyCountQuery(QueryIds::Ids(vec![0.into()])))
}
Expand Down
26 changes: 26 additions & 0 deletions agdb/src/query_builder/select_edge_count.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use crate::QueryIds;
use crate::SelectEdgeCountQuery;

/// Select edge count builder.
pub struct SelectEdgeCount(pub SelectEdgeCountQuery);

/// Final builder that lets you create
/// an actual query object.
pub struct SelectEdgeCountIds(pub SelectEdgeCountQuery);

impl SelectEdgeCount {
/// An id or list of ids or search query to select edge count of.
/// All ids specified must exist in the database.
pub fn ids<T: Into<QueryIds>>(mut self, ids: T) -> SelectEdgeCountIds {
self.0.ids = ids.into();

SelectEdgeCountIds(self.0)
}
}

impl SelectEdgeCountIds {
/// Returns the built `SelectEdgeCountQuery` object.
pub fn query(self) -> SelectEdgeCountQuery {
self.0
}
}
1 change: 1 addition & 0 deletions agdb/tests/openapi_feature_test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use utoipa::OpenApi;
agdb::SearchQueryAlgorithm,
agdb::SelectAliasesQuery,
agdb::SelectAllAliasesQuery,
agdb::SelectEdgeCountQuery,
agdb::SelectIndexesQuery,
agdb::SelectKeyCountQuery,
agdb::SelectKeysQuery,
Expand Down
Loading
Loading