Skip to content

Commit d52acf5

Browse files
authored
BE-434: HashQL: Refactor entity storage path resolution with centralized schema mapping (#8496)
1 parent 90e1630 commit d52acf5

16 files changed

Lines changed: 644 additions & 404 deletions

File tree

libs/@local/hashql/core/src/symbol/sym.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ hashql_macros::define_symbols! {
99
archived,
1010
archived_by_id,
1111
bar,
12+
base_url,
1213
BaseUrl,
1314
bit_and,
1415
bit_not,
@@ -110,6 +111,7 @@ hashql_macros::define_symbols! {
110111
unknown,
111112
Url,
112113
vectors,
114+
version,
113115
web_id,
114116
// [tidy] sort alphabetically end
115117

libs/@local/hashql/mir/src/body/place.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,12 @@ pub struct Projection<'heap> {
447447
pub kind: ProjectionKind<'heap>,
448448
}
449449

450+
impl AsRef<Self> for Projection<'_> {
451+
fn as_ref(&self) -> &Self {
452+
self
453+
}
454+
}
455+
450456
/// A projection operation that navigates within structured data.
451457
///
452458
/// Projections allow places to reference nested data within structured types.

libs/@local/hashql/mir/src/pass/execution/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ mod island;
1313
mod placement;
1414
mod splitting;
1515
mod statement_placement;
16+
pub mod storage;
1617
mod target;
1718
mod terminator_placement;
19+
mod vertex;
1820

1921
use core::{alloc::Allocator, assert_matches};
2022

@@ -25,6 +27,7 @@ pub use self::{
2527
island::{Island, IslandId, IslandVec},
2628
placement::error::PlacementDiagnosticCategory,
2729
target::TargetId,
30+
vertex::VertexType,
2831
};
2932
use self::{
3033
fusion::BasicBlockFusion,

libs/@local/hashql/mir/src/pass/execution/statement_placement/common.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use crate::{
1616
local::Local,
1717
location::Location,
1818
operand::Operand,
19+
place::Projection,
1920
rvalue::RValue,
2021
statement::{Assign, Statement, StatementKind},
2122
terminator::TerminatorKind,
@@ -29,6 +30,7 @@ use crate::{
2930
execution::{
3031
Cost,
3132
cost::{StatementCostVec, TraversalCostVec},
33+
storage::{Access, EntityPath},
3234
},
3335
},
3436
visit::Visitor,
@@ -319,3 +321,17 @@ where
319321
Ok(())
320322
}
321323
}
324+
325+
/// Determines which backend can access an entity field projection.
326+
///
327+
/// Walks the projection path through the entity schema to determine whether the field is stored in
328+
/// Postgres (as a column or JSONB path) or in the embedding store. Returns `None` if the path
329+
/// doesn't map to any supported backend storage.
330+
///
331+
/// For example:
332+
/// - `entity.properties.foo` → `Some(Access::Postgres(Direct))` (JSONB)
333+
/// - `entity.encodings.vectors` → `Some(Access::Embedding(Direct))`
334+
/// - `entity.metadata.record_id.entity_id.web_id` → `Some(Access::Postgres(Direct))`
335+
pub(crate) fn entity_projection_access(projections: &[Projection<'_>]) -> Option<Access> {
336+
EntityPath::resolve(projections).map(|(path, _)| path.access())
337+
}

libs/@local/hashql/mir/src/pass/execution/statement_placement/embedding/mod.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use core::alloc::Allocator;
22

3-
use hashql_core::{
4-
id::{Id as _, bit_vec::DenseBitSet},
5-
symbol::sym,
6-
};
3+
use hashql_core::id::{Id as _, bit_vec::DenseBitSet};
74

85
use super::{
96
StatementPlacement,
@@ -14,9 +11,10 @@ use crate::{
1411
context::MirContext,
1512
pass::{
1613
execution::{
17-
Cost,
14+
Cost, VertexType,
1815
cost::{StatementCostVec, TraversalCostVec},
19-
statement_placement::lookup::{Access, entity_projection_access},
16+
statement_placement::common::entity_projection_access,
17+
storage::Access,
2018
},
2119
transform::Traversals,
2220
},
@@ -35,22 +33,19 @@ fn is_supported_place<'heap>(
3533
// For GraphReadFilter bodies, local 1 is the filter argument (vertex). Check if the
3634
// projection path maps to an Embedding-accessible field.
3735
if matches!(body.source, Source::GraphReadFilter(_)) && place.local.as_usize() == 1 {
38-
let local_type = body.local_decls[place.local].r#type;
39-
let type_name = context
40-
.env
41-
.r#type(local_type)
42-
.kind
43-
.opaque()
44-
.map_or_else(|| unreachable!(), |opaque| opaque.name);
45-
46-
if type_name == sym::path::Entity {
47-
return matches!(
48-
entity_projection_access(&place.projections),
49-
Some(Access::Embedding(_))
50-
);
51-
}
36+
let decl = &body.local_decls[place.local];
37+
let Some(vertex_type) = VertexType::from_local(context.env, decl) else {
38+
unimplemented!("lookup for declared type")
39+
};
5240

53-
unimplemented!("unimplemented lookup for declared type")
41+
match vertex_type {
42+
VertexType::Entity => {
43+
return matches!(
44+
entity_projection_access(&place.projections),
45+
Some(Access::Embedding(_))
46+
);
47+
}
48+
}
5449
}
5550

5651
domain.contains(place.local)

libs/@local/hashql/mir/src/pass/execution/statement_placement/lookup/entity.rs

Lines changed: 0 additions & 135 deletions
This file was deleted.

libs/@local/hashql/mir/src/pass/execution/statement_placement/lookup/mod.rs

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)