Skip to content

Commit ad9dd11

Browse files
committed
perf(query-compiler): avoid compound selector materialization
Signed-off-by: Alexey Orlenko's AI Agent <robot@aqrln.net>
1 parent ee55ba2 commit ad9dd11

7 files changed

Lines changed: 96 additions & 11 deletions

File tree

query-compiler/core/src/query_document/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub(crate) use parser::*;
3030

3131
use crate::{
3232
query_ast::{QueryOption, QueryOptions},
33-
query_graph_builder::resolve_compound_field,
33+
query_graph_builder::is_compound_field,
3434
};
3535
use itertools::Itertools;
3636
use query_structure::Model;
@@ -91,7 +91,7 @@ impl BatchDocument {
9191

9292
where_obj.iter().any(|(key, val)| match val {
9393
// If it's a compound, then it's still considered as scalar
94-
ArgumentValue::Object(_) if resolve_compound_field(key, &model).is_some() => false,
94+
ArgumentValue::Object(_) if is_compound_field(key, &model) => false,
9595
// Otherwise, we just look for a scalar field inside the model. If it's not one, then we break.
9696
val => match model.fields().find_from_scalar(key) {
9797
Ok(sf) => match val {
@@ -319,7 +319,7 @@ fn extract_filter(where_obj: ArgumentValueObject, model: &Model) -> Vec<Selectio
319319
.into_iter()
320320
.flat_map(|(key, val)| match val {
321321
// This means our query has a compound field in the form of: {co1_col2: { col1_col2: { col1: <val>, col2: <val> } }}
322-
ArgumentValue::Object(obj) if resolve_compound_field(&key, model).is_some() => obj.into_iter().collect(),
322+
ArgumentValue::Object(obj) if is_compound_field(&key, model) => obj.into_iter().collect(),
323323
// This means our query has a scalar filter in the form of {col1: { equals: <val> }}
324324
ArgumentValue::Object(obj) => {
325325
// This is safe because it's been validated before in the `.can_compact` method.

query-compiler/core/src/query_graph_builder/extractors/filters/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn extract_unique_filter(value_map: ParsedInputMap<'_>, model: &Model) -> Qu
2323
.fields()
2424
.find_from_scalar(field_name)
2525
.is_ok_and(|field| field.unique())
26-
|| utils::resolve_compound_field(field_name, model).is_some()
26+
|| utils::is_compound_field(field_name, model)
2727
}) {
2828
return internal_extract_unique_filter(value_map, model);
2929
}
@@ -36,7 +36,7 @@ pub fn extract_unique_filter(value_map: ParsedInputMap<'_>, model: &Model) -> Qu
3636
.into_iter()
3737
.partition(|(field_name, _)| match model.fields().find_from_scalar(field_name) {
3838
Ok(field) => field.unique(),
39-
Err(_) => utils::resolve_compound_field(field_name, model).is_some(),
39+
Err(_) => utils::is_compound_field(field_name, model),
4040
});
4141
let mut unique_map = ParsedInputMap::from(unique_map);
4242
let mut rest_map = ParsedInputMap::from(rest_map);

query-compiler/core/src/query_graph_builder/extractors/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ mod utils;
66
pub(crate) use filters::*;
77
pub(crate) use query_arguments::*;
88
pub(crate) use rel_aggregations::*;
9-
pub(crate) use utils::resolve_compound_field;
9+
pub(crate) use utils::is_compound_field;
1010

1111
use crate::query_document::*;
Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,74 @@
11
use query_structure::{Model, ScalarFieldRef};
22

3+
/// Checks whether a field name resolves to a compound field without materializing the fields.
4+
pub fn is_compound_field(name: &str, model: &Model) -> bool {
5+
is_compound_id(name, model) || is_index_field(name, model)
6+
}
7+
38
/// Attempts to resolve a field name to a compound field.
49
pub fn resolve_compound_field(name: &str, model: &Model) -> Option<Vec<ScalarFieldRef>> {
510
resolve_compound_id(name, model).or_else(|| resolve_index_fields(name, model))
611
}
712

813
/// Attempts to match a given name to the (schema) name of a compound id field on the model.
914
pub fn resolve_compound_id(name: &str, model: &Model) -> Option<Vec<ScalarFieldRef>> {
10-
model.fields().compound_id().and_then(|pk| {
11-
(name == schema::compound_id_field_name(model.walker().primary_key().unwrap())).then(|| pk.collect())
12-
})
15+
model
16+
.fields()
17+
.compound_id()
18+
.and_then(|pk| is_compound_id(name, model).then(|| pk.collect()))
1319
}
1420

1521
/// Attempts to match a given name to the (schema) name of a compound indexes on the model and returns the first match.
1622
pub fn resolve_index_fields(name: &str, model: &Model) -> Option<Vec<ScalarFieldRef>> {
1723
model
1824
.unique_indexes()
19-
.find(|index| schema::compound_index_field_name(*index) == name)
25+
.find(|index| index_field_name_matches(name, *index))
2026
.map(|index| {
2127
index
2228
.fields()
2329
.map(|f| ScalarFieldRef::from((model.dm.clone(), f)))
2430
.collect()
2531
})
2632
}
33+
34+
fn is_compound_id(name: &str, model: &Model) -> bool {
35+
model.fields().compound_id().is_some()
36+
&& model.walker().primary_key().is_some_and(|pk| match pk.name() {
37+
Some(pk_name) => name == pk_name,
38+
None => compound_field_name_matches(name, pk.fields().map(|field| field.name())),
39+
})
40+
}
41+
42+
fn is_index_field(name: &str, model: &Model) -> bool {
43+
model
44+
.unique_indexes()
45+
.any(|index| index_field_name_matches(name, index))
46+
}
47+
48+
fn index_field_name_matches(name: &str, index: psl::parser_database::walkers::IndexWalker<'_>) -> bool {
49+
match index.name() {
50+
Some(index_name) => name == index_name,
51+
None => compound_field_name_matches(name, index.fields().map(|field| field.name())),
52+
}
53+
}
54+
55+
fn compound_field_name_matches<'a>(mut name: &str, fields: impl Iterator<Item = &'a str>) -> bool {
56+
let mut first = true;
57+
58+
for field in fields {
59+
if first {
60+
first = false;
61+
} else if let Some(rest) = name.strip_prefix('_') {
62+
name = rest;
63+
} else {
64+
return false;
65+
}
66+
67+
let Some(rest) = name.strip_prefix(field) else {
68+
return false;
69+
};
70+
name = rest;
71+
}
72+
73+
!first && name.is_empty()
74+
}

query-compiler/core/src/query_graph_builder/write/upsert.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ fn can_use_connector_native_upsert<'a>(
232232
fn is_unique_field(field_name: &str, model: &Model) -> bool {
233233
match model.fields().find_from_scalar(field_name) {
234234
Ok(field) => field.unique(),
235-
Err(_) => resolve_compound_field(field_name, model).is_some(),
235+
Err(_) => is_compound_field(field_name, model),
236236
}
237237
}
238238

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"modelName": "ParentModelWithCompositeId",
3+
"action": "findUnique",
4+
"query": {
5+
"arguments": {
6+
"relationLoadStrategy": "query",
7+
"where": {
8+
"a_b": {
9+
"a": 1,
10+
"b": 1
11+
}
12+
}
13+
},
14+
"selection": {
15+
"$composites": true,
16+
"$scalars": true
17+
}
18+
}
19+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: query-compiler/query-compiler/tests/queries.rs
3+
expression: pretty
4+
input_file: query-compiler/query-compiler/tests/data/query-compound-id.json
5+
snapshot_kind: text
6+
---
7+
dataMap {
8+
a: Int (a)
9+
b: Int (b)
10+
}
11+
unique (query «SELECT "public"."ParentModelWithCompositeId"."a",
12+
"public"."ParentModelWithCompositeId"."b" FROM
13+
"public"."ParentModelWithCompositeId" WHERE
14+
("public"."ParentModelWithCompositeId"."a" = $1 AND
15+
"public"."ParentModelWithCompositeId"."b" = $2) LIMIT $3 OFFSET
16+
$4»
17+
params [const(BigInt(1)), const(BigInt(1)), const(BigInt(1)),
18+
const(BigInt(0))])

0 commit comments

Comments
 (0)