sphereql_graphql/lib.rs
1//! GraphQL integration for sphereQL spatial queries.
2//!
3//! Provides an [`async-graphql`] schema with queries for cone, shell, band,
4//! wedge, and region lookups, k-nearest-neighbor search, real-time
5//! subscriptions via a broadcast event bus, and the full category
6//! enrichment surface (concept paths, drill-down, domain groups, stats).
7
8pub mod category;
9pub mod category_types;
10pub mod context;
11pub mod query;
12pub mod subscription;
13pub mod types;
14
15pub use category::*;
16pub use category_types::*;
17pub use context::*;
18pub use query::*;
19pub use subscription::*;
20pub use types::*;
21
22use std::sync::Arc;
23
24/// Merged GraphQL query root combining the spatial-only resolvers and
25/// the category-enrichment resolvers.
26#[derive(async_graphql::MergedObject, Default)]
27pub struct MergedQueryRoot(SphericalQueryRoot, CategoryQueryRoot);
28
29impl MergedQueryRoot {
30 pub fn new() -> Self {
31 Self::default()
32 }
33}
34
35impl Default for SphericalQueryRoot {
36 fn default() -> Self {
37 SphericalQueryRoot
38 }
39}
40
41impl Default for CategoryQueryRoot {
42 fn default() -> Self {
43 CategoryQueryRoot
44 }
45}
46
47/// Schema flavor that exposes both spatial and category queries.
48pub type UnifiedSchema =
49 async_graphql::Schema<MergedQueryRoot, async_graphql::EmptyMutation, SphericalSubscriptionRoot>;
50
51/// Build a [`UnifiedSchema`] from all four context resources: the
52/// spatial point index, the spatial event bus, the category-enrichment
53/// pipeline, and a [`TextEmbedder`](sphereql_embed::text_embedder::TextEmbedder)
54/// for resolvers that take text queries.
55///
56/// To run a spatial-only deployment, keep using [`build_schema`]; the
57/// pipeline + embedder context entries are unused there.
58///
59/// To run a category-only deployment, pass a no-op
60/// [`PointIndex`](crate::query::PointIndex) created via
61/// [`create_default_index`] alongside the real pipeline; spatial
62/// resolvers will return empty results but won't error.
63pub fn build_unified_schema(
64 index: PointIndex,
65 event_bus: SpatialEventBus,
66 pipeline: CategoryPipelineHandle,
67 embedder: EmbedderHandle,
68) -> UnifiedSchema {
69 async_graphql::Schema::build(
70 MergedQueryRoot::new(),
71 async_graphql::EmptyMutation,
72 SphericalSubscriptionRoot,
73 )
74 .data(index)
75 .data(event_bus)
76 .data(pipeline)
77 .data(embedder)
78 .finish()
79}
80
81/// Convenience wrapper: build a unified schema from an in-memory list of
82/// [`CategorizedItemInput`]s and the default no-op embedder.
83///
84/// Intended for tests, examples, and quickstarts. Production callers
85/// should construct the pipeline themselves (so they control projection
86/// kind / config) and supply a real
87/// [`TextEmbedder`](sphereql_embed::text_embedder::TextEmbedder) before
88/// calling [`build_unified_schema`].
89pub fn build_unified_schema_from_items(
90 items: &[CategorizedItemInput],
91) -> Result<UnifiedSchema, sphereql_embed::pipeline::PipelineError> {
92 let pipeline = build_pipeline_handle_from_items(items)?;
93 Ok(build_unified_schema(
94 create_default_index(),
95 SpatialEventBus::new(16),
96 pipeline,
97 default_no_embedder_handle(),
98 ))
99}
100
101// Touch Arc so the import is used in the public re-export path tests
102// downstream might rely on.
103#[allow(dead_code)]
104fn _ensure_arc_in_scope() -> Arc<()> {
105 Arc::new(())
106}