1use crate::{
7 db::{
8 predicate::{CoercionId, CompareOp, MissingRowPolicy, Predicate},
9 query::{
10 builder::aggregate::AggregateExpr,
11 explain::{
12 ExplainAccessPath, ExplainExecutionNodeDescriptor, ExplainExecutionNodeType,
13 ExplainOrderPushdown, ExplainPlan, ExplainPredicate,
14 },
15 expr::{FilterExpr, SortExpr},
16 intent::{QueryError, model::QueryModel},
17 plan::{AccessPlannedQuery, LoadSpec, QueryMode},
18 },
19 },
20 traits::{EntityKind, EntityValue, FieldValue, SingletonEntity},
21 value::Value,
22};
23use core::marker::PhantomData;
24
25#[derive(Debug)]
34struct StructuralQuery {
35 intent: QueryModel<'static, Value>,
36}
37
38impl StructuralQuery {
39 #[must_use]
40 const fn new(
41 model: &'static crate::model::entity::EntityModel,
42 consistency: MissingRowPolicy,
43 ) -> Self {
44 Self {
45 intent: QueryModel::new(model, consistency),
46 }
47 }
48
49 #[must_use]
50 const fn mode(&self) -> QueryMode {
51 self.intent.mode()
52 }
53
54 #[must_use]
55 fn has_explicit_order(&self) -> bool {
56 self.intent.has_explicit_order()
57 }
58
59 #[must_use]
60 const fn has_grouping(&self) -> bool {
61 self.intent.has_grouping()
62 }
63
64 #[must_use]
65 const fn load_spec(&self) -> Option<LoadSpec> {
66 match self.intent.mode() {
67 QueryMode::Load(spec) => Some(spec),
68 QueryMode::Delete(_) => None,
69 }
70 }
71
72 #[must_use]
73 fn filter(mut self, predicate: Predicate) -> Self {
74 self.intent = self.intent.filter(predicate);
75 self
76 }
77
78 fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
79 let Self { intent } = self;
80 let intent = intent.filter_expr(expr)?;
81
82 Ok(Self { intent })
83 }
84
85 fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
86 let Self { intent } = self;
87 let intent = intent.sort_expr(expr)?;
88
89 Ok(Self { intent })
90 }
91
92 #[must_use]
93 fn order_by(mut self, field: impl AsRef<str>) -> Self {
94 self.intent = self.intent.order_by(field);
95 self
96 }
97
98 #[must_use]
99 fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
100 self.intent = self.intent.order_by_desc(field);
101 self
102 }
103
104 #[must_use]
105 fn distinct(mut self) -> Self {
106 self.intent = self.intent.distinct();
107 self
108 }
109
110 #[cfg(feature = "sql")]
111 #[must_use]
112 fn select_fields<I, S>(mut self, fields: I) -> Self
113 where
114 I: IntoIterator<Item = S>,
115 S: Into<String>,
116 {
117 self.intent = self.intent.select_fields(fields);
118 self
119 }
120
121 fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
122 let Self { intent } = self;
123 let intent = intent.push_group_field(field.as_ref())?;
124
125 Ok(Self { intent })
126 }
127
128 #[must_use]
129 fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
130 self.intent = self.intent.push_group_aggregate(aggregate);
131 self
132 }
133
134 #[must_use]
135 fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
136 self.intent = self.intent.grouped_limits(max_groups, max_group_bytes);
137 self
138 }
139
140 fn having_group(
141 self,
142 field: impl AsRef<str>,
143 op: CompareOp,
144 value: Value,
145 ) -> Result<Self, QueryError> {
146 let field = field.as_ref().to_owned();
147 let Self { intent } = self;
148 let intent = intent.push_having_group_clause(&field, op, value)?;
149
150 Ok(Self { intent })
151 }
152
153 fn having_aggregate(
154 self,
155 aggregate_index: usize,
156 op: CompareOp,
157 value: Value,
158 ) -> Result<Self, QueryError> {
159 let Self { intent } = self;
160 let intent = intent.push_having_aggregate_clause(aggregate_index, op, value)?;
161
162 Ok(Self { intent })
163 }
164
165 #[must_use]
166 fn by_id(self, id: Value) -> Self {
167 let Self { intent } = self;
168 Self {
169 intent: intent.by_id(id),
170 }
171 }
172
173 #[must_use]
174 fn by_ids<I>(self, ids: I) -> Self
175 where
176 I: IntoIterator<Item = Value>,
177 {
178 let Self { intent } = self;
179 Self {
180 intent: intent.by_ids(ids),
181 }
182 }
183
184 #[must_use]
185 fn only(self, id: Value) -> Self {
186 let Self { intent } = self;
187
188 Self {
189 intent: intent.only(id),
190 }
191 }
192
193 #[must_use]
194 fn delete(mut self) -> Self {
195 self.intent = self.intent.delete();
196 self
197 }
198
199 #[must_use]
200 fn limit(mut self, limit: u32) -> Self {
201 self.intent = self.intent.limit(limit);
202 self
203 }
204
205 #[must_use]
206 fn offset(mut self, offset: u32) -> Self {
207 self.intent = self.intent.offset(offset);
208 self
209 }
210
211 fn build_plan(&self) -> Result<AccessPlannedQuery, QueryError> {
212 self.intent.build_plan_model()
213 }
214}
215
216#[derive(Debug)]
225struct PlannedQueryCore {
226 model: &'static crate::model::entity::EntityModel,
227 plan: AccessPlannedQuery,
228}
229
230impl PlannedQueryCore {
231 #[must_use]
232 const fn new(
233 model: &'static crate::model::entity::EntityModel,
234 plan: AccessPlannedQuery,
235 ) -> Self {
236 Self { model, plan }
237 }
238
239 #[must_use]
240 fn explain(&self) -> ExplainPlan {
241 self.plan.explain_with_model(self.model)
242 }
243
244 #[must_use]
246 fn plan_hash_hex(&self) -> String {
247 self.plan.fingerprint().to_string()
248 }
249}
250
251#[derive(Debug)]
260pub struct PlannedQuery<E: EntityKind> {
261 inner: PlannedQueryCore,
262 _marker: PhantomData<E>,
263}
264
265impl<E: EntityKind> PlannedQuery<E> {
266 #[must_use]
267 const fn from_inner(inner: PlannedQueryCore) -> Self {
268 Self {
269 inner,
270 _marker: PhantomData,
271 }
272 }
273
274 #[must_use]
275 pub fn explain(&self) -> ExplainPlan {
276 self.inner.explain()
277 }
278
279 #[must_use]
281 pub fn plan_hash_hex(&self) -> String {
282 self.inner.plan_hash_hex()
283 }
284}
285
286#[derive(Clone, Debug)]
295struct CompiledQueryCore {
296 model: &'static crate::model::entity::EntityModel,
297 entity_path: &'static str,
298 plan: AccessPlannedQuery,
299}
300
301impl CompiledQueryCore {
302 #[must_use]
303 const fn new(
304 model: &'static crate::model::entity::EntityModel,
305 entity_path: &'static str,
306 plan: AccessPlannedQuery,
307 ) -> Self {
308 Self {
309 model,
310 entity_path,
311 plan,
312 }
313 }
314
315 #[must_use]
316 fn explain(&self) -> ExplainPlan {
317 self.plan.explain_with_model(self.model)
318 }
319
320 #[must_use]
322 fn plan_hash_hex(&self) -> String {
323 self.plan.fingerprint().to_string()
324 }
325
326 #[must_use]
327 #[cfg(feature = "sql")]
328 fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
329 self.plan.projection_spec(self.model)
330 }
331
332 #[must_use]
333 fn into_inner(self) -> AccessPlannedQuery {
334 self.plan
335 }
336}
337
338#[derive(Clone, Debug)]
347pub struct CompiledQuery<E: EntityKind> {
348 inner: CompiledQueryCore,
349 _marker: PhantomData<E>,
350}
351
352impl<E: EntityKind> CompiledQuery<E> {
353 #[must_use]
354 const fn from_inner(inner: CompiledQueryCore) -> Self {
355 Self {
356 inner,
357 _marker: PhantomData,
358 }
359 }
360
361 #[must_use]
362 pub fn explain(&self) -> ExplainPlan {
363 self.inner.explain()
364 }
365
366 #[must_use]
368 pub fn plan_hash_hex(&self) -> String {
369 self.inner.plan_hash_hex()
370 }
371
372 #[must_use]
373 #[cfg(feature = "sql")]
374 pub(in crate::db) fn projection_spec(&self) -> crate::db::query::plan::expr::ProjectionSpec {
375 self.inner.projection_spec()
376 }
377
378 pub(in crate::db) fn into_executable(self) -> crate::db::executor::ExecutablePlan<E> {
380 assert!(
381 self.inner.entity_path == E::PATH,
382 "compiled query entity mismatch: compiled for '{}', requested '{}'",
383 self.inner.entity_path,
384 E::PATH,
385 );
386
387 crate::db::executor::ExecutablePlan::new(self.into_inner())
388 }
389
390 #[must_use]
391 pub(in crate::db) fn into_inner(self) -> AccessPlannedQuery {
392 self.inner.into_inner()
393 }
394}
395
396#[derive(Debug)]
408pub struct Query<E: EntityKind> {
409 inner: StructuralQuery,
410 _marker: PhantomData<E>,
411}
412
413impl<E: EntityKind> Query<E> {
414 const fn from_inner(inner: StructuralQuery) -> Self {
416 Self {
417 inner,
418 _marker: PhantomData,
419 }
420 }
421
422 #[must_use]
426 pub const fn new(consistency: MissingRowPolicy) -> Self {
427 Self::from_inner(StructuralQuery::new(E::MODEL, consistency))
428 }
429
430 #[must_use]
432 pub const fn mode(&self) -> QueryMode {
433 self.inner.mode()
434 }
435
436 #[must_use]
437 pub(crate) fn has_explicit_order(&self) -> bool {
438 self.inner.has_explicit_order()
439 }
440
441 #[must_use]
442 pub(crate) const fn has_grouping(&self) -> bool {
443 self.inner.has_grouping()
444 }
445
446 #[must_use]
447 pub(crate) const fn load_spec(&self) -> Option<LoadSpec> {
448 self.inner.load_spec()
449 }
450
451 #[must_use]
453 pub fn filter(mut self, predicate: Predicate) -> Self {
454 self.inner = self.inner.filter(predicate);
455 self
456 }
457
458 pub fn filter_expr(self, expr: FilterExpr) -> Result<Self, QueryError> {
460 let Self { inner, .. } = self;
461 let inner = inner.filter_expr(expr)?;
462
463 Ok(Self::from_inner(inner))
464 }
465
466 pub fn sort_expr(self, expr: SortExpr) -> Result<Self, QueryError> {
468 let Self { inner, .. } = self;
469 let inner = inner.sort_expr(expr)?;
470
471 Ok(Self::from_inner(inner))
472 }
473
474 #[must_use]
476 pub fn order_by(mut self, field: impl AsRef<str>) -> Self {
477 self.inner = self.inner.order_by(field);
478 self
479 }
480
481 #[must_use]
483 pub fn order_by_desc(mut self, field: impl AsRef<str>) -> Self {
484 self.inner = self.inner.order_by_desc(field);
485 self
486 }
487
488 #[must_use]
490 pub fn distinct(mut self) -> Self {
491 self.inner = self.inner.distinct();
492 self
493 }
494
495 #[cfg(feature = "sql")]
497 #[must_use]
498 pub(in crate::db) fn select_fields<I, S>(mut self, fields: I) -> Self
499 where
500 I: IntoIterator<Item = S>,
501 S: Into<String>,
502 {
503 self.inner = self.inner.select_fields(fields);
504 self
505 }
506
507 pub fn group_by(self, field: impl AsRef<str>) -> Result<Self, QueryError> {
509 let Self { inner, .. } = self;
510 let inner = inner.group_by(field)?;
511
512 Ok(Self::from_inner(inner))
513 }
514
515 #[must_use]
517 pub fn aggregate(mut self, aggregate: AggregateExpr) -> Self {
518 self.inner = self.inner.aggregate(aggregate);
519 self
520 }
521
522 #[must_use]
524 pub fn grouped_limits(mut self, max_groups: u64, max_group_bytes: u64) -> Self {
525 self.inner = self.inner.grouped_limits(max_groups, max_group_bytes);
526 self
527 }
528
529 pub fn having_group(
531 self,
532 field: impl AsRef<str>,
533 op: CompareOp,
534 value: Value,
535 ) -> Result<Self, QueryError> {
536 let Self { inner, .. } = self;
537 let inner = inner.having_group(field, op, value)?;
538
539 Ok(Self::from_inner(inner))
540 }
541
542 pub fn having_aggregate(
544 self,
545 aggregate_index: usize,
546 op: CompareOp,
547 value: Value,
548 ) -> Result<Self, QueryError> {
549 let Self { inner, .. } = self;
550 let inner = inner.having_aggregate(aggregate_index, op, value)?;
551
552 Ok(Self::from_inner(inner))
553 }
554
555 pub(crate) fn by_id(self, id: E::Key) -> Self {
557 let Self { inner, .. } = self;
558
559 Self::from_inner(inner.by_id(id.to_value()))
560 }
561
562 pub(crate) fn by_ids<I>(self, ids: I) -> Self
564 where
565 I: IntoIterator<Item = E::Key>,
566 {
567 let Self { inner, .. } = self;
568
569 Self::from_inner(inner.by_ids(ids.into_iter().map(|id| id.to_value())))
570 }
571
572 #[must_use]
574 pub fn delete(mut self) -> Self {
575 self.inner = self.inner.delete();
576 self
577 }
578
579 #[must_use]
586 pub fn limit(mut self, limit: u32) -> Self {
587 self.inner = self.inner.limit(limit);
588 self
589 }
590
591 #[must_use]
597 pub fn offset(mut self, offset: u32) -> Self {
598 self.inner = self.inner.offset(offset);
599 self
600 }
601
602 pub fn explain(&self) -> Result<ExplainPlan, QueryError> {
604 let plan = self.planned()?;
605
606 Ok(plan.explain())
607 }
608
609 pub fn plan_hash_hex(&self) -> Result<String, QueryError> {
614 let plan = self.inner.build_plan()?;
615
616 Ok(plan.fingerprint().to_string())
617 }
618
619 pub fn explain_execution(&self) -> Result<ExplainExecutionNodeDescriptor, QueryError>
621 where
622 E: EntityValue,
623 {
624 let executable = self.plan()?.into_executable();
625
626 executable
627 .explain_load_execution_node_descriptor()
628 .map_err(QueryError::execute)
629 }
630
631 pub fn explain_execution_text(&self) -> Result<String, QueryError>
633 where
634 E: EntityValue,
635 {
636 Ok(self.explain_execution()?.render_text_tree())
637 }
638
639 pub fn explain_execution_json(&self) -> Result<String, QueryError>
641 where
642 E: EntityValue,
643 {
644 Ok(self.explain_execution()?.render_json_canonical())
645 }
646
647 pub fn explain_execution_verbose(&self) -> Result<String, QueryError>
649 where
650 E: EntityValue,
651 {
652 let executable = self.plan()?.into_executable();
653 let descriptor = executable
654 .explain_load_execution_node_descriptor()
655 .map_err(QueryError::execute)?;
656 let route_diagnostics = executable
657 .explain_load_execution_verbose_diagnostics()
658 .map_err(QueryError::execute)?;
659 let explain = self.explain()?;
660
661 let mut lines = vec![descriptor.render_text_tree_verbose()];
663 lines.extend(route_diagnostics);
664
665 lines.push(format!(
667 "diagnostic.descriptor.has_top_n_seek={}",
668 contains_execution_node_type(&descriptor, ExplainExecutionNodeType::TopNSeek)
669 ));
670 lines.push(format!(
671 "diagnostic.descriptor.has_index_range_limit_pushdown={}",
672 contains_execution_node_type(
673 &descriptor,
674 ExplainExecutionNodeType::IndexRangeLimitPushdown,
675 )
676 ));
677 lines.push(format!(
678 "diagnostic.descriptor.has_index_predicate_prefilter={}",
679 contains_execution_node_type(
680 &descriptor,
681 ExplainExecutionNodeType::IndexPredicatePrefilter,
682 )
683 ));
684 lines.push(format!(
685 "diagnostic.descriptor.has_residual_predicate_filter={}",
686 contains_execution_node_type(
687 &descriptor,
688 ExplainExecutionNodeType::ResidualPredicateFilter,
689 )
690 ));
691
692 lines.push(format!("diagnostic.plan.mode={:?}", explain.mode()));
694 lines.push(format!(
695 "diagnostic.plan.order_pushdown={}",
696 plan_order_pushdown_label(explain.order_pushdown())
697 ));
698 lines.push(format!(
699 "diagnostic.plan.predicate_pushdown={}",
700 plan_predicate_pushdown_label(explain.predicate(), explain.access())
701 ));
702 lines.push(format!("diagnostic.plan.distinct={}", explain.distinct()));
703 lines.push(format!("diagnostic.plan.page={:?}", explain.page()));
704 lines.push(format!(
705 "diagnostic.plan.consistency={:?}",
706 explain.consistency()
707 ));
708
709 Ok(lines.join("\n"))
710 }
711
712 pub fn planned(&self) -> Result<PlannedQuery<E>, QueryError> {
714 let plan = self.inner.build_plan()?;
715 let _projection = plan.projection_spec(E::MODEL);
716
717 Ok(PlannedQuery::from_inner(PlannedQueryCore::new(
718 E::MODEL,
719 plan,
720 )))
721 }
722
723 pub fn plan(&self) -> Result<CompiledQuery<E>, QueryError> {
727 let plan = self.inner.build_plan()?;
728 let _projection = plan.projection_spec(E::MODEL);
729
730 Ok(CompiledQuery::from_inner(CompiledQueryCore::new(
731 E::MODEL,
732 E::PATH,
733 plan,
734 )))
735 }
736}
737
738fn contains_execution_node_type(
739 descriptor: &ExplainExecutionNodeDescriptor,
740 target: ExplainExecutionNodeType,
741) -> bool {
742 descriptor.node_type() == target
743 || descriptor
744 .children()
745 .iter()
746 .any(|child| contains_execution_node_type(child, target))
747}
748
749fn plan_order_pushdown_label(order_pushdown: &ExplainOrderPushdown) -> String {
750 match order_pushdown {
751 ExplainOrderPushdown::MissingModelContext => "missing_model_context".to_string(),
752 ExplainOrderPushdown::EligibleSecondaryIndex { index, prefix_len } => {
753 format!("eligible(index={index},prefix_len={prefix_len})",)
754 }
755 ExplainOrderPushdown::Rejected(reason) => format!("rejected({reason:?})"),
756 }
757}
758
759fn plan_predicate_pushdown_label(
760 predicate: &ExplainPredicate,
761 access: &ExplainAccessPath,
762) -> String {
763 let access_label = match access {
764 ExplainAccessPath::ByKey { .. } => "by_key",
765 ExplainAccessPath::ByKeys { keys } if keys.is_empty() => "empty_access_contract",
766 ExplainAccessPath::ByKeys { .. } => "by_keys",
767 ExplainAccessPath::KeyRange { .. } => "key_range",
768 ExplainAccessPath::IndexPrefix { .. } => "index_prefix",
769 ExplainAccessPath::IndexMultiLookup { .. } => "index_multi_lookup",
770 ExplainAccessPath::IndexRange { .. } => "index_range",
771 ExplainAccessPath::FullScan => "full_scan",
772 ExplainAccessPath::Union(_) => "union",
773 ExplainAccessPath::Intersection(_) => "intersection",
774 };
775 if matches!(predicate, ExplainPredicate::None) {
776 return "none".to_string();
777 }
778 if matches!(access, ExplainAccessPath::FullScan) {
779 if explain_predicate_contains_non_strict_compare(predicate) {
780 return "fallback(non_strict_compare_coercion)".to_string();
781 }
782 if explain_predicate_contains_empty_prefix_starts_with(predicate) {
783 return "fallback(starts_with_empty_prefix)".to_string();
784 }
785 if explain_predicate_contains_is_null(predicate) {
786 return "fallback(is_null_full_scan)".to_string();
787 }
788 if explain_predicate_contains_text_scan_operator(predicate) {
789 return "fallback(text_operator_full_scan)".to_string();
790 }
791
792 return format!("fallback({access_label})");
793 }
794
795 format!("applied({access_label})")
796}
797
798fn explain_predicate_contains_non_strict_compare(predicate: &ExplainPredicate) -> bool {
799 match predicate {
800 ExplainPredicate::Compare { coercion, .. } => coercion.id != CoercionId::Strict,
801 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
802 .iter()
803 .any(explain_predicate_contains_non_strict_compare),
804 ExplainPredicate::Not(inner) => explain_predicate_contains_non_strict_compare(inner),
805 ExplainPredicate::None
806 | ExplainPredicate::True
807 | ExplainPredicate::False
808 | ExplainPredicate::IsNull { .. }
809 | ExplainPredicate::IsNotNull { .. }
810 | ExplainPredicate::IsMissing { .. }
811 | ExplainPredicate::IsEmpty { .. }
812 | ExplainPredicate::IsNotEmpty { .. }
813 | ExplainPredicate::TextContains { .. }
814 | ExplainPredicate::TextContainsCi { .. } => false,
815 }
816}
817
818fn explain_predicate_contains_is_null(predicate: &ExplainPredicate) -> bool {
819 match predicate {
820 ExplainPredicate::IsNull { .. } => true,
821 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => {
822 children.iter().any(explain_predicate_contains_is_null)
823 }
824 ExplainPredicate::Not(inner) => explain_predicate_contains_is_null(inner),
825 ExplainPredicate::None
826 | ExplainPredicate::True
827 | ExplainPredicate::False
828 | ExplainPredicate::Compare { .. }
829 | ExplainPredicate::IsNotNull { .. }
830 | ExplainPredicate::IsMissing { .. }
831 | ExplainPredicate::IsEmpty { .. }
832 | ExplainPredicate::IsNotEmpty { .. }
833 | ExplainPredicate::TextContains { .. }
834 | ExplainPredicate::TextContainsCi { .. } => false,
835 }
836}
837
838fn explain_predicate_contains_empty_prefix_starts_with(predicate: &ExplainPredicate) -> bool {
839 match predicate {
840 ExplainPredicate::Compare {
841 op: CompareOp::StartsWith,
842 value: Value::Text(prefix),
843 ..
844 } => prefix.is_empty(),
845 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
846 .iter()
847 .any(explain_predicate_contains_empty_prefix_starts_with),
848 ExplainPredicate::Not(inner) => explain_predicate_contains_empty_prefix_starts_with(inner),
849 ExplainPredicate::None
850 | ExplainPredicate::True
851 | ExplainPredicate::False
852 | ExplainPredicate::Compare { .. }
853 | ExplainPredicate::IsNull { .. }
854 | ExplainPredicate::IsNotNull { .. }
855 | ExplainPredicate::IsMissing { .. }
856 | ExplainPredicate::IsEmpty { .. }
857 | ExplainPredicate::IsNotEmpty { .. }
858 | ExplainPredicate::TextContains { .. }
859 | ExplainPredicate::TextContainsCi { .. } => false,
860 }
861}
862
863fn explain_predicate_contains_text_scan_operator(predicate: &ExplainPredicate) -> bool {
864 match predicate {
865 ExplainPredicate::Compare {
866 op: CompareOp::EndsWith,
867 ..
868 }
869 | ExplainPredicate::TextContains { .. }
870 | ExplainPredicate::TextContainsCi { .. } => true,
871 ExplainPredicate::And(children) | ExplainPredicate::Or(children) => children
872 .iter()
873 .any(explain_predicate_contains_text_scan_operator),
874 ExplainPredicate::Not(inner) => explain_predicate_contains_text_scan_operator(inner),
875 ExplainPredicate::Compare { .. }
876 | ExplainPredicate::None
877 | ExplainPredicate::True
878 | ExplainPredicate::False
879 | ExplainPredicate::IsNull { .. }
880 | ExplainPredicate::IsNotNull { .. }
881 | ExplainPredicate::IsMissing { .. }
882 | ExplainPredicate::IsEmpty { .. }
883 | ExplainPredicate::IsNotEmpty { .. } => false,
884 }
885}
886
887impl<E> Query<E>
888where
889 E: EntityKind + SingletonEntity,
890 E::Key: Default,
891{
892 pub(crate) fn only(self) -> Self {
894 let Self { inner, .. } = self;
895
896 Self::from_inner(inner.only(E::Key::default().to_value()))
897 }
898}