Преглед на файлове

Implement searching on extra indices.

Kestrel преди 7 месеца
родител
ревизия
e3e2259b7f
променени са 5 файла, в които са добавени 130 реда и са изтрити 21 реда
  1. 21 2
      microrm/src/query.rs
  2. 28 16
      microrm/src/query/components.rs
  3. 3 1
      microrm/src/schema.rs
  4. 9 1
      microrm/src/schema/build.rs
  5. 69 1
      microrm/src/schema/tests.rs

+ 21 - 2
microrm/src/query.rs

@@ -6,6 +6,7 @@ use crate::{
             helpers::check_relation, Entity, EntityID, EntityPart, EntityPartList,
             EntityPartVisitor,
         },
+        index::Index,
         relation::{LocalSide, RelationData},
         IDMap, Stored,
     },
@@ -654,8 +655,26 @@ pub trait Queryable: Clone {
     where
         Self: Sized,
     {
-        components::KeyComponent::new(self, values)
+        components::IndexComponent::<_, _, <Self::EntityOutput as Entity>::Keys, _>::new(
+            self, values,
+        )
+    }
+
+    /// Filter using an arbitrary index on the entity.
+    fn indexed<EPL: EntityPartList<Entity = Self::EntityOutput>>(
+        self,
+        _index: &Index<Self::EntityOutput, EPL>,
+        values: impl QueryEquivalentList<EPL::DatumList>,
+    ) -> impl Queryable<
+        EntityOutput = Self::EntityOutput,
+        OutputContainer = Option<Stored<Self::EntityOutput>>,
+    >
+    where
+        Self: Sized,
+    {
+        components::IndexComponent::<_, _, EPL, _>::new(self, values)
     }
+
     /// Filter using an arbitrary column on the entity.
     fn with<EP: EntityPart<Entity = Self::EntityOutput>>(
         self,
@@ -682,7 +701,7 @@ pub trait Queryable: Clone {
     }
 
     // ----------------------------------------------------------------------
-    // Relationiation-following and joining methods
+    // Relation-following and joining methods
     // ----------------------------------------------------------------------
     /// Join based on an existing relation
     fn join<

+ 28 - 16
microrm/src/query/components.rs

@@ -133,22 +133,24 @@ impl<
     }
 }
 
-/// Filter on the keying index
-pub(crate) struct KeyComponent<
+/// Filter on an index
+pub(crate) struct IndexComponent<
     E: Entity,
     Parent: Queryable,
-    EL: QueryEquivalentList<<E::Keys as EntityPartList>::DatumList>,
+    EPL: EntityPartList<Entity = E>,
+    EL: QueryEquivalentList<EPL::DatumList>,
 > {
     datum: EL,
     parent: Parent,
-    _ghost: std::marker::PhantomData<E>,
+    _ghost: std::marker::PhantomData<(E, EPL)>,
 }
 
 impl<
         E: Entity,
         Parent: Queryable,
-        EL: QueryEquivalentList<<E::Keys as EntityPartList>::DatumList>,
-    > KeyComponent<E, Parent, EL>
+        EPL: EntityPartList<Entity = E>,
+        EL: QueryEquivalentList<EPL::DatumList>,
+    > IndexComponent<E, Parent, EPL, EL>
 {
     pub fn new(parent: Parent, datum: EL) -> Self {
         Self {
@@ -162,8 +164,9 @@ impl<
 impl<
         E: Entity,
         Parent: Queryable,
-        EL: QueryEquivalentList<<E::Keys as EntityPartList>::DatumList>,
-    > Clone for KeyComponent<E, Parent, EL>
+        EPL: EntityPartList<Entity = E>,
+        EL: QueryEquivalentList<EPL::DatumList>,
+    > Clone for IndexComponent<E, Parent, EPL, EL>
 {
     fn clone(&self) -> Self {
         Self {
@@ -177,11 +180,17 @@ impl<
 /// this workaround is needed because we very explicitly would like EL to not be a
 /// 'static-restricted type, and it doesn't matter for the purposes of actually distinguishing
 /// between queries.
-pub(crate) struct CanonicalUniqueComponent<E: Entity, Parent: Queryable> {
-    _ghost: std::marker::PhantomData<(E, Parent)>,
+pub(crate) struct CanonicalIndexComponent<
+    E: Entity,
+    Parent: Queryable,
+    EPL: EntityPartList<Entity = E>,
+> {
+    _ghost: std::marker::PhantomData<(E, Parent, EPL)>,
 }
 
-impl<E: Entity, Parent: Queryable + 'static> Queryable for CanonicalUniqueComponent<E, Parent> {
+impl<E: Entity, Parent: Queryable + 'static, EPL: EntityPartList<Entity = E>> Queryable
+    for CanonicalIndexComponent<E, Parent, EPL>
+{
     type EntityOutput = E;
     type OutputContainer = Option<Stored<E>>;
     type StaticVersion = Self;
@@ -197,7 +206,9 @@ impl<E: Entity, Parent: Queryable + 'static> Queryable for CanonicalUniqueCompon
     }
 }
 
-impl<E: Entity, Parent: Queryable + 'static> Clone for CanonicalUniqueComponent<E, Parent> {
+impl<E: Entity, Parent: Queryable + 'static, EPL: EntityPartList<Entity = E>> Clone
+    for CanonicalIndexComponent<E, Parent, EPL>
+{
     fn clone(&self) -> Self {
         Self {
             _ghost: Default::default(),
@@ -208,12 +219,13 @@ impl<E: Entity, Parent: Queryable + 'static> Clone for CanonicalUniqueComponent<
 impl<
         E: Entity,
         Parent: Queryable,
-        EL: QueryEquivalentList<<E::Keys as EntityPartList>::DatumList>,
-    > Queryable for KeyComponent<E, Parent, EL>
+        EPL: EntityPartList<Entity = E>,
+        EL: QueryEquivalentList<EPL::DatumList>,
+    > Queryable for IndexComponent<E, Parent, EPL, EL>
 {
     type EntityOutput = E;
     type OutputContainer = Option<Stored<E>>;
-    type StaticVersion = CanonicalUniqueComponent<E, Parent::StaticVersion>;
+    type StaticVersion = CanonicalIndexComponent<E, Parent::StaticVersion, EPL>;
 
     fn build(&self) -> Query {
         let mut query = self.parent.build();
@@ -233,7 +245,7 @@ impl<
             }
         }
 
-        <E::Keys>::accept_part_visitor(&mut PartVisitor::<E>(&mut query, Default::default()));
+        EPL::accept_part_visitor(&mut PartVisitor::<E>(&mut query, Default::default()));
 
         query
     }

+ 3 - 1
microrm/src/schema.rs

@@ -12,7 +12,7 @@ use crate::{
     db::{Connection, StatementContext, StatementRow, Transaction},
     query::{self, Insertable},
     schema::datum::{Datum, DatumDiscriminator},
-    schema::entity::{Entity, EntityVisitor},
+    schema::entity::Entity,
 };
 use crate::{DBResult, Error};
 
@@ -277,9 +277,11 @@ pub trait DatabaseItem {
 
 /// Visitor trait for iterating across the types in a [`DatabaseSpec`].
 pub trait DatabaseItemVisitor {
+    /// Visit an IDMap<T> type.
     fn visit_idmap<T: Entity>(&mut self)
     where
         Self: Sized;
+    /// Visit an Index<T, PL> type.
     fn visit_index<T: Entity, PL: EntityPartList<Entity = T>>(&mut self)
     where
         Self: Sized;

+ 9 - 1
microrm/src/schema/build.rs

@@ -70,7 +70,15 @@ struct IndexInfo {
 
 impl IndexInfo {
     fn build_creation_query(&self) -> String {
-        let index_name = format!("index_{}_{}", self.table_name, self.columns.iter().cloned().reduce(|a,b| format!("{a}_{b}")).unwrap());
+        let index_name = format!(
+            "index_{}_{}",
+            self.table_name,
+            self.columns
+                .iter()
+                .cloned()
+                .reduce(|a, b| format!("{a}_{b}"))
+                .unwrap()
+        );
 
         format!(
             "create {unique} index `{index_name}` on `{table_name}`({cols})",

+ 69 - 1
microrm/src/schema/tests.rs

@@ -910,7 +910,7 @@ mod index_test {
     #[derive(Database)]
     struct TWDB {
         entries: microrm::IDMap<TwoWay>,
-        // left_index: Index<TwoWay, index_cols![TwoWay::left]>,
+        left_index: Index<TwoWay, index_cols![TwoWay::left]>,
         right_index: Index<TwoWay, index_cols![TwoWay::right]>,
     }
 
@@ -918,4 +918,72 @@ mod index_test {
     fn db_construction() {
         let db = TWDB::open_path(":memory:").expect("couldn't open in-memory db");
     }
+
+    #[test]
+    fn lookup_test() {
+        let db = TWDB::open_path(":memory:").expect("couldn't open in-memory db");
+
+        let e1 = db
+            .entries
+            .insert_and_return(TwoWay {
+                up: "up1".into(),
+                left: "left1".into(),
+                right: "right1".into(),
+            })
+            .expect("couldn't insert entry");
+        let e2 = db
+            .entries
+            .insert_and_return(TwoWay {
+                up: "up2".into(),
+                left: "left2".into(),
+                right: "right2".into(),
+            })
+            .expect("couldn't insert entry");
+
+        assert!(db
+            .entries
+            .indexed(&db.left_index, "left3")
+            .get()
+            .expect("couldn't query left index")
+            .is_none());
+        assert_eq!(
+            db.entries
+                .indexed(&db.left_index, "left2")
+                .get()
+                .expect("couldn't query left index")
+                .as_ref(),
+            Some(&e2)
+        );
+        assert_eq!(
+            db.entries
+                .indexed(&db.left_index, "left1")
+                .get()
+                .expect("couldn't query left index")
+                .as_ref(),
+            Some(&e1)
+        );
+
+        assert!(db
+            .entries
+            .indexed(&db.right_index, "right3")
+            .get()
+            .expect("couldn't query right index")
+            .is_none());
+        assert_eq!(
+            db.entries
+                .indexed(&db.right_index, "right2")
+                .get()
+                .expect("couldn't query right index")
+                .as_ref(),
+            Some(&e2)
+        );
+        assert_eq!(
+            db.entries
+                .indexed(&db.right_index, "right1")
+                .get()
+                .expect("couldn't query right index")
+                .as_ref(),
+            Some(&e1)
+        );
+    }
 }