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

H-3285, H-3286: Optionally return set of distinct values for entity (type) queries #5029

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
55 changes: 55 additions & 0 deletions apps/hash-graph/libs/api/src/rest/entity.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Web routes for CRU operations on entities.

use alloc::sync::Arc;
use std::collections::HashMap;

use authorization::{
backend::{ModifyRelationshipOperation, PermissionAssertion},
Expand Down Expand Up @@ -36,6 +37,7 @@ use graph::{
subgraph::{edges::GraphResolveDepths, temporal_axes::QueryTemporalAxesUnresolved},
};
use graph_types::{
account::{CreatedById, EditionCreatedById},
knowledge::{
entity::{
ActorType, Entity, EntityEditionId, EntityEditionProvenance, EntityEmbedding, EntityId,
Expand All @@ -54,6 +56,7 @@ use graph_types::{
};
use serde::{Deserialize, Serialize};
use temporal_client::TemporalClient;
use type_system::url::VersionedUrl;
use utoipa::{OpenApi, ToSchema};
use validation::ValidateEntityComponents;

Expand Down Expand Up @@ -477,6 +480,10 @@ fn generate_sorting_paths(

#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[expect(
clippy::struct_excessive_bools,
reason = "Parameter struct deserialized from JSON"
)]
struct GetEntitiesRequest<'q, 's, 'p> {
#[serde(borrow)]
filter: Filter<'q, Entity>,
Expand All @@ -489,6 +496,14 @@ struct GetEntitiesRequest<'q, 's, 'p> {
cursor: Option<EntityQueryCursor<'s>>,
#[serde(default)]
include_count: bool,
#[serde(default)]
include_web_ids: bool,
#[serde(default)]
include_created_by_ids: bool,
#[serde(default)]
include_edition_created_by_ids: bool,
#[serde(default)]
include_type_ids: bool,
}

#[utoipa::path(
Expand Down Expand Up @@ -558,6 +573,10 @@ where
include_drafts: request.include_drafts,
include_count: request.include_count,
temporal_axes: request.temporal_axes,
include_web_ids: request.include_web_ids,
include_created_by_ids: request.include_created_by_ids,
include_edition_created_by_ids: request.include_edition_created_by_ids,
include_type_ids: request.include_type_ids,
},
)
.await
Expand All @@ -566,13 +585,21 @@ where
entities: response.entities,
cursor: response.cursor.map(EntityQueryCursor::into_owned),
count: response.count,
web_ids: response.web_ids,
created_by_ids: response.created_by_ids,
edition_created_by_ids: response.edition_created_by_ids,
type_ids: response.type_ids,
})
})
.map_err(report_to_response)
}

#[derive(Debug, Deserialize, ToSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[expect(
clippy::struct_excessive_bools,
reason = "Parameter struct deserialized from JSON"
)]
struct GetEntitySubgraphRequest<'q, 's, 'p> {
#[serde(borrow)]
filter: Filter<'q, Entity>,
Expand All @@ -586,6 +613,14 @@ struct GetEntitySubgraphRequest<'q, 's, 'p> {
cursor: Option<EntityQueryCursor<'s>>,
#[serde(default)]
include_count: bool,
#[serde(default)]
include_web_ids: bool,
#[serde(default)]
include_created_by_ids: bool,
#[serde(default)]
include_edition_created_by_ids: bool,
#[serde(default)]
include_type_ids: bool,
}

#[derive(Serialize, ToSchema)]
Expand All @@ -595,6 +630,18 @@ struct GetEntitySubgraphResponse<'r> {
#[serde(borrow)]
cursor: Option<EntityQueryCursor<'r>>,
count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
web_ids: Option<HashMap<OwnedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
created_by_ids: Option<HashMap<CreatedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
type_ids: Option<HashMap<VersionedUrl, usize>>,
}

#[utoipa::path(
Expand Down Expand Up @@ -666,6 +713,10 @@ where
include_drafts: request.include_drafts,
include_count: request.include_count,
temporal_axes: request.temporal_axes,
include_web_ids: request.include_web_ids,
include_created_by_ids: request.include_created_by_ids,
include_edition_created_by_ids: request.include_edition_created_by_ids,
include_type_ids: request.include_type_ids,
},
)
.await
Expand All @@ -674,6 +725,10 @@ where
subgraph: response.subgraph.into(),
cursor: response.cursor.map(EntityQueryCursor::into_owned),
count: response.count,
web_ids: response.web_ids,
created_by_ids: response.created_by_ids,
edition_created_by_ids: response.edition_created_by_ids,
type_ids: response.type_ids,
})
})
.map_err(report_to_response)
Expand Down
11 changes: 11 additions & 0 deletions apps/hash-graph/libs/api/src/rest/entity_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use graph::{
subgraph::identifier::EntityTypeVertexId,
};
use graph_types::{
account::EditionCreatedById,
ontology::{
EntityTypeEmbedding, EntityTypeId, EntityTypeMetadata, EntityTypeWithMetadata,
OntologyTemporalMetadata, OntologyTypeClassificationMetadata, OntologyTypeMetadata,
Expand Down Expand Up @@ -749,6 +750,13 @@ where
struct GetEntityTypeSubgraphResponse {
subgraph: Subgraph,
cursor: Option<EntityTypeVertexId>,
count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
web_ids: Option<HashMap<OwnedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schema(nullable = false)]
edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
}

#[utoipa::path(
Expand Down Expand Up @@ -809,6 +817,9 @@ where
Json(GetEntityTypeSubgraphResponse {
subgraph: Subgraph::from(response.subgraph),
cursor: response.cursor,
count: response.count,
web_ids: response.web_ids,
edition_created_by_ids: response.edition_created_by_ids,
})
})
}
Expand Down
2 changes: 2 additions & 0 deletions apps/hash-graph/libs/graph/src/store/fetcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ where
limit: None,
include_drafts: true,
include_count: false,
include_web_ids: false,
include_edition_created_by_ids: false,
},
)
.await
Expand Down
44 changes: 30 additions & 14 deletions apps/hash-graph/libs/graph/src/store/knowledge.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
use alloc::borrow::Cow;
use core::{error::Error, fmt};
use std::collections::HashSet;
use std::collections::{HashMap, HashSet};

use authorization::{schema::EntityRelationAndSubject, zanzibar::Consistency};
use error_stack::Report;
use futures::TryFutureExt;
use graph_types::{
account::AccountId,
account::{AccountId, CreatedById, EditionCreatedById},
knowledge::{
entity::{Entity, EntityEmbedding, EntityId, EntityUuid, ProvidedEntityEditionProvenance},
link::LinkData,
Expand Down Expand Up @@ -214,19 +214,19 @@ pub struct ValidateEntityParams<'a> {
pub components: ValidateEntityComponents,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[derive(Debug)]
#[expect(clippy::struct_excessive_bools, reason = "Parameter struct")]
pub struct GetEntitiesParams<'a> {
#[serde(borrow)]
pub filter: Filter<'a, Entity>,
pub temporal_axes: QueryTemporalAxesUnresolved,
#[serde(borrow)]
pub sorting: EntityQuerySorting<'static>,
pub limit: Option<usize>,
pub include_drafts: bool,
#[serde(default)]
pub include_count: bool,
pub include_web_ids: bool,
pub include_created_by_ids: bool,
pub include_edition_created_by_ids: bool,
pub include_type_ids: bool,
}

#[derive(Debug, Serialize)]
Expand All @@ -236,29 +236,45 @@ pub struct GetEntitiesResponse<'r> {
pub entities: Vec<Entity>,
pub cursor: Option<EntityQueryCursor<'r>>,
pub count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub web_ids: Option<HashMap<OwnedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub created_by_ids: Option<HashMap<CreatedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub type_ids: Option<HashMap<VersionedUrl, usize>>,
}

#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[derive(Debug)]
#[expect(clippy::struct_excessive_bools, reason = "Parameter struct")]
pub struct GetEntitySubgraphParams<'a> {
#[serde(borrow)]
pub filter: Filter<'a, Entity>,
pub temporal_axes: QueryTemporalAxesUnresolved,
pub graph_resolve_depths: GraphResolveDepths,
#[serde(borrow)]
pub sorting: EntityQuerySorting<'static>,
pub limit: Option<usize>,
pub include_drafts: bool,
#[serde(default)]
pub include_count: bool,
pub include_web_ids: bool,
pub include_created_by_ids: bool,
pub include_edition_created_by_ids: bool,
pub include_type_ids: bool,
}

#[derive(Debug)]
pub struct GetEntitySubgraphResponse<'r> {
pub subgraph: Subgraph,
pub cursor: Option<EntityQueryCursor<'r>>,
pub count: Option<usize>,
pub web_ids: Option<HashMap<OwnedById, usize>>,
pub created_by_ids: Option<HashMap<CreatedById, usize>>,
pub edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
pub type_ids: Option<HashMap<VersionedUrl, usize>>,
}

#[derive(Debug, Deserialize)]
Expand Down
21 changes: 20 additions & 1 deletion apps/hash-graph/libs/graph/src/store/ontology.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@ use authorization::schema::{
};
use error_stack::Result;
use graph_types::{
account::AccountId,
account::{AccountId, EditionCreatedById},
ontology::{
DataTypeMetadata, DataTypeWithMetadata, EntityTypeMetadata, EntityTypeWithMetadata,
OntologyTemporalMetadata, OntologyTypeClassificationMetadata, PropertyTypeMetadata,
PropertyTypeWithMetadata, ProvidedOntologyEditionProvenance,
},
owned_by_id::OwnedById,
Embedding,
};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -534,6 +535,7 @@ pub struct CreateEntityTypeParams<R> {
#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[expect(clippy::struct_excessive_bools, reason = "Parameter struct")]
pub struct GetEntityTypeSubgraphParams<'p> {
#[serde(borrow)]
pub filter: Filter<'p, EntityTypeWithMetadata>,
Expand All @@ -544,13 +546,19 @@ pub struct GetEntityTypeSubgraphParams<'p> {
pub include_drafts: bool,
#[serde(default)]
pub include_count: bool,
#[serde(default)]
pub include_web_ids: bool,
#[serde(default)]
pub include_edition_created_by_ids: bool,
}

#[derive(Debug)]
pub struct GetEntityTypeSubgraphResponse {
pub subgraph: Subgraph,
pub cursor: Option<EntityTypeVertexId>,
pub count: Option<usize>,
pub web_ids: Option<HashMap<OwnedById, usize>>,
pub edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
}

#[derive(Debug, Deserialize)]
Expand All @@ -566,6 +574,7 @@ pub struct CountEntityTypesParams<'p> {
#[derive(Debug, Deserialize)]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
#[expect(clippy::struct_excessive_bools, reason = "Parameter struct")]
pub struct GetEntityTypesParams<'p> {
#[serde(borrow)]
pub filter: Filter<'p, EntityTypeWithMetadata>,
Expand All @@ -577,6 +586,10 @@ pub struct GetEntityTypesParams<'p> {
pub limit: Option<usize>,
#[serde(default)]
pub include_count: bool,
#[serde(default)]
pub include_web_ids: bool,
#[serde(default)]
pub include_edition_created_by_ids: bool,
}

#[derive(Debug, Serialize)]
Expand All @@ -586,6 +599,12 @@ pub struct GetEntityTypesResponse {
pub entity_types: Vec<EntityTypeWithMetadata>,
pub cursor: Option<EntityTypeVertexId>,
pub count: Option<usize>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub web_ids: Option<HashMap<OwnedById, usize>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "utoipa", schema(nullable = false))]
pub edition_created_by_ids: Option<HashMap<EditionCreatedById, usize>>,
}

#[derive(Debug, Deserialize)]
Expand Down
Loading
Loading