Skip to content

Commit

Permalink
feat: api improvements (#28)
Browse files Browse the repository at this point in the history
* refactor(sdk): Entity version query + remove dead code

* fix(api): Space filter not being properly applied

* feat(api): Add basic pagination

* misc: Add logs if running in debug mode

* docs: Update entity filter docs

* style: clippy+fmt

* fix(sdk): Query builder bug

* fix: tests
  • Loading branch information
cvauclair authored Feb 20, 2025
1 parent aadb80a commit 6c4bc91
Show file tree
Hide file tree
Showing 17 changed files with 382 additions and 1,173 deletions.
54 changes: 24 additions & 30 deletions api/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ type Entity {
"""Entity description (if available)"""
description: String

"""Entity name (if available)"""
"""Entity cover (if available)"""
cover: String

"""Entity name (if available)"""
"""Entity blocks (if available)"""
blocks: [Entity!]!

"""Types of the entity (which are entities themselves)"""
types: [Entity!]!

"""
The space ID of the entity (note: the same entity can exist in multiple spaces)
"""
Expand All @@ -30,14 +33,14 @@ type Entity {
updatedAt: String!
updatedAtBlock: String!

"""Types of the entity (which are entities themselves)"""
types: [Entity!]!

"""Attributes of the entity"""
attributes(filter: AttributeFilter): [Triple!]!

"""Relations outgoing from the entity"""
relations(where: EntityRelationFilter): [Relation!]!

"""Versions of the entity, ordered chronologically"""
versions: [EntityVersion!]!
}

"""Filter the entities by attributes and their values and value types"""
Expand Down Expand Up @@ -75,8 +78,6 @@ input EntityFilter {
idNotIn: [String!]

"""Exact match for the entity types"""
types: [String!]
typesNot: [String!]
typesContains: [String!]
typesNotContains: [String!]
attributes: [EntityAttributeFilter!]
Expand All @@ -99,7 +100,13 @@ input EntityRelationFilter {

"""Filter the relations by the entity they point to"""
to: EntityFilter
attributes: [EntityAttributeFilter!]
}

type EntityVersion {
id: String!

"""Attributes of the entity"""
attributes(filter: AttributeFilter): [Triple!]!
}

type Options {
Expand All @@ -115,54 +122,38 @@ enum OrderDirection {

type Query {
"""Returns a single entity identified by its ID and space ID"""
entity(id: String!, spaceId: String!): Entity
entity(id: String!, spaceId: String!, versionId: String): Entity

"""
Returns multiple entities according to the provided space ID and filter
"""
entities(spaceId: String!, orderBy: String, orderDirection: OrderDirection, where: EntityFilter): [Entity!]!

"""Returns a single relation identified by its ID and space ID"""
relation(id: String!, spaceId: String!): Relation
relation(id: String!, spaceId: String!, versionId: String): Relation

"""
Returns multiple relations according to the provided space ID and filter
"""
relations(spaceId: String!, orderBy: String, orderDirection: OrderDirection, where: RelationFilter): [Relation!]!
}

"""
Relation object
Note: Relations are also entities, but they have a different structure in the database.
In other words, the Relation object is a "view" on a relation entity. All relations
can also be queried as entities.
"""
"""Relation object"""
type Relation {
"""Relation ID"""
id: String!

"""Relation name (if available)"""
name: String
createdAt: String!
createdAtBlock: String!
updatedAt: String!
updatedAtBlock: String!

"""Attributes of the relation"""
attributes: [Triple!]!
"""Entity of the relation"""
entity: Entity!

"""Relation type of the relation"""
relationType: [Entity!]!
relationType: Entity!

"""Entity from which the relation originates"""
from: Entity!

"""Entity to which the relation points"""
to: Entity!

"""Relations outgoing from the relation"""
relations(spaceId: String!, where: EntityRelationFilter): [Relation!]!
}

"""Relation filter input object"""
Expand Down Expand Up @@ -202,6 +193,9 @@ type Triple {
"""Options of the triple (if any)"""
options: Options!

"""Space ID of the triple"""
spaceId: String!

"""Name of the attribute (if available)"""
name: String
}
Expand Down
4 changes: 3 additions & 1 deletion api/src/schema/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,9 @@ impl Entity {
) -> FieldResult<Vec<EntityVersion>> {
Ok(self
.node
.versions(&executor.context().0, self.space_id.clone())
.versions(&executor.context().0)
.space_id(prop_filter::value(&self.space_id))
.send()
.await?
.into_iter()
.map(|version| {
Expand Down
17 changes: 10 additions & 7 deletions api/src/schema/entity_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,16 @@ use super::triple::ValueType;
///
/// ```graphql
/// query {
/// entities(where: {
/// space_id: "BJqiLPcSgfF8FRxkFr76Uy",
/// types_contain: ["XG26vy98XAA6cR6DosTALk", "XG26vy98XAA6cR6DosTALk"],
/// attributes_contain: [
/// {id: "XG26vy98XAA6cR6DosTALk", value: "value", value_type: TEXT},
/// ]
/// })
/// entities(
/// where: {
/// typesContains: ["XG26vy98XAA6cR6DosTALk", "XG26vy98XAA6cR6DosTALk"],
/// attributes: [
/// {attribute: "LuBWqZAu6pz54eiJS5mLv8", value: "Bob", valueType: TEXT},
/// ]
/// }
/// ) {
/// id
/// }
/// }
/// ```
#[derive(Debug, GraphQLInputObject)]
Expand Down
57 changes: 37 additions & 20 deletions api/src/schema/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,16 @@ impl Query {
space_id: String,
version_id: Option<String>,
) -> FieldResult<Option<Entity>> {
tracing::info!("version_id: {:?}", version_id);
let version_index = if let Some(version_id) = version_id {
mapping::get_version_index(&executor.context().0, version_id).await?
} else {
None
};
tracing::info!("version_index: {:?}", version_index);

Entity::load(&executor.context().0, id, space_id, version_index).await
}

// TODO: Add order_by and order_direction
#[allow(clippy::too_many_arguments)]
/// Returns multiple entities according to the provided space ID and filter
async fn entities<'a, S: ScalarValue>(
&'a self,
Expand All @@ -43,11 +41,15 @@ impl Query {
order_by: Option<String>,
order_direction: Option<OrderDirection>,
r#where: Option<EntityFilter>,
) -> Vec<Entity> {
first: Option<i32>,
skip: Option<i32>,
) -> FieldResult<Vec<Entity>> {
let mut query = entity_node::find_many(&executor.context().0);

if let Some(r#where) = r#where {
query = query.with_filter(r#where.into());
let filter = entity_node::EntityFilter::from(r#where).with_space_id(&space_id);

query = query.with_filter(filter);
}

match (order_by, order_direction) {
Expand All @@ -60,21 +62,23 @@ impl Query {
_ => {}
}

// if let Some(order_by) = order_by {
// query = query.order_by(&order_by);
// }
if let Some(first) = first {
if first > 1000 {
return Err("Cannot query more than 1000 entities at once".into());
}
query = query.limit(first as usize);
}

// if let Some(order_direction) = order_direction {
// query = query.order_direction(order_direction.into());
// }
if let Some(skip) = skip {
query = query.skip(skip as usize);
}

query
Ok(query
.send()
.await
.expect("Failed to find entities")
.await?
.into_iter()
.map(|entity| Entity::new(entity, space_id.clone(), None))
.collect::<Vec<_>>()
.collect::<Vec<_>>())
}

/// Returns a single relation identified by its ID and space ID
Expand All @@ -95,6 +99,7 @@ impl Query {
}

// TODO: Add order_by and order_direction
#[allow(clippy::too_many_arguments)]
/// Returns multiple relations according to the provided space ID and filter
async fn relations<'a, S: ScalarValue>(
&'a self,
Expand All @@ -103,19 +108,31 @@ impl Query {
_order_by: Option<String>,
_order_direction: Option<OrderDirection>,
r#where: Option<RelationFilter>,
) -> Vec<Relation> {
first: Option<i32>,
skip: Option<i32>,
) -> FieldResult<Vec<Relation>> {
let mut query = relation_node::find_many(&executor.context().0);

if let Some(r#where) = r#where {
query = r#where.apply_filter(query);
}

query
if let Some(first) = first {
if first > 1000 {
return Err("Cannot query more than 1000 relations at once".into());
}
query = query.limit(first as usize);
}

if let Some(skip) = skip {
query = query.skip(skip as usize);
}

Ok(query
.send()
.await
.expect("Failed to find relations")
.await?
.into_iter()
.map(|relation| Relation::new(relation, space_id.clone(), None))
.collect()
.collect())
}
}
5 changes: 2 additions & 3 deletions sdk/src/mapping/attributes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde::Deserialize;
use crate::{error::DatabaseError, indexer_ids, models::BlockMetadata};

use super::{
query_utils::{Query, QueryPart, VersionFilter},
query_utils::{query_part, Query, QueryPart, VersionFilter},
AttributeNode, Triple, TriplesConversionError, Value,
};

Expand Down Expand Up @@ -401,8 +401,7 @@ impl FindOneQuery {
"(:Entity {id: $entity_id}) -[r:ATTRIBUTE {space_id: $space_id}]-> (n:Attribute)",
)
.merge(self.space_version.into_query_part("r"))
.with_clause("collect(n{.*}) AS attrs")
.return_clause("attrs")
.with_clause("collect(n{.*}) AS attrs", query_part::return_query("attrs"))
.params("entity_id", self.entity_id)
.params("space_id", self.space_id)
}
Expand Down
Loading

0 comments on commit 6c4bc91

Please sign in to comment.