Skip to content

Commit

Permalink
feat: advanced indexing (#40)
Browse files Browse the repository at this point in the history
You can now declare an array of indexes (fields in schema) except array of objects and the queries will automatically use them for more efficient searches
  • Loading branch information
elribonazo authored Jan 26, 2025
2 parents fe9f58d + e3439f5 commit 63bf47e
Show file tree
Hide file tree
Showing 17 changed files with 5,567 additions and 2,012 deletions.
4,299 changes: 3,139 additions & 1,160 deletions package-lock.json

Large diffs are not rendered by default.

45 changes: 1 addition & 44 deletions packages/ridb-core/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions packages/ridb-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@ serde = { version = "1.0.195", features = ["derive"] }
serde-wasm-bindgen = "0.6.3"
console = "0.15.8"
serde_json = "1.0.111"
web-sys = { version = "0.3.69", features = ["Storage","IdbTransaction", "IdbVersionChangeEvent", "IdbObjectStoreParameters", "Event", "DomException", "IdbCursor", "IdbKeyRange", "IdbObjectStore", "IdbRequest", "IdbTransactionMode", "IdbOpenDbRequest", "console", "Window", "Request", "Response", "IdbDatabase", "IdbFactory", "DomStringList"] }
web-sys = { version = "0.3.69", features = ["Storage", "IdbTransaction", "IdbVersionChangeEvent", "IdbObjectStoreParameters","IdbIndexParameters", "Event", "DomException", "IdbCursor", "IdbKeyRange", "IdbObjectStore", "IdbRequest", "IdbTransactionMode", "IdbOpenDbRequest", "console", "Window", "Request", "Response", "IdbDatabase", "IdbFactory", "DomStringList", "IdbIndex"] }
wasm-bindgen-test = {version="^0.3.42"}
sha2 = "0.11.0-pre.4"
base64 = "0.22.1"
chacha20poly1305 = { version = "0.10.1", features = ["std"] }
rand = "0.9.0-alpha.2"
getrandom = { version = "0.2", features = ["js"] }
parking_lot = "0.12"
Expand Down
39 changes: 20 additions & 19 deletions packages/ridb-core/src/collection/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export class Collection<T extends SchemaType> {
#[derive(Clone)]
pub struct Collection {
pub(crate) name: String,
pub(crate) storage: Storage,
pub(crate) storage: Storage
}

#[wasm_bindgen]
Expand All @@ -121,7 +121,7 @@ impl Collection {
) -> Collection {
Collection {
name,
storage,
storage
}
}

Expand All @@ -140,22 +140,19 @@ impl Collection {

/// Finds and returns all documents in the collection.
///
/// This function is asynchronous and returns a `Schema` representing
/// This function is asynchronous and returns a `JsValue` representing
/// the documents found in the collection.
#[wasm_bindgen]
pub async fn find(&mut self, query: JsValue) -> Result<JsValue, JsValue> {
let result = match self.storage.internal.find(&self.name, query).await {
Ok(docs) => {
docs
},
Err(e) => {
return Err(js_sys::Error::new(&format!("Failed to find documents: {:?}", e)).into())
}
};
pub async fn find(&mut self, query_js: JsValue) -> Result<JsValue, JsValue> {
// No index available, perform a regular find
let docs = self.storage.find(
&self.name,
query_js
).await?;

// Convert the result to a JavaScript array
let array = js_sys::Array::from(&result);
let processed_array = js_sys::Array::new();
// Process and return the result
let array = js_sys::Array::from(&docs);
let processed_array = js_sys::Array::new();

// Iterate over each document in the array
for item in array.iter() {
Expand All @@ -164,16 +161,20 @@ impl Collection {
processed_array.push(&processed_item);
}

Ok(processed_array.into())
Ok(
JsValue::from(
processed_array
)
)
}

/// counts and returns all documents in the collection.
///
/// This function is asynchronous and returns a `Schema` representing
/// the documents found in the collection.
#[wasm_bindgen]
pub async fn count(&self, query: JsValue) -> Result<JsValue, JsValue> {
match self.storage.internal.count(&self.name, query).await {
pub async fn count(&self, query_js: JsValue) -> Result<JsValue, JsValue> {
match self.storage.count(&self.name, query_js).await {
Ok(count) => Ok(count),
Err(e) => Err(js_sys::Error::new(&format!("Failed to count documents: {:?}", e)).into())
}
Expand All @@ -184,7 +185,7 @@ impl Collection {
/// This function is asynchronous.
#[wasm_bindgen(js_name="findById")]
pub async fn find_by_id(&self, primary_key: JsValue) -> Result<JsValue, JsValue>{
let document = match self.storage.internal.find_document_by_id(&self.name, primary_key ).await {
let document = match self.storage.find_document_by_id(&self.name, primary_key ).await {
Ok(doc) => doc,
Err(e) => return Err(js_sys::Error::new(&format!("Failed to find document by ID: {:?}", e)).into())
};
Expand Down
113 changes: 55 additions & 58 deletions packages/ridb-core/src/database/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::collections::HashMap;
use js_sys::{Array, Object, Reflect};
use wasm_bindgen::{ JsCast, JsValue};
use wasm_bindgen::{JsCast, JsValue};
use wasm_bindgen::prelude::wasm_bindgen;
use crate::collection::Collection;
use crate::error::RIDBError;
Expand Down Expand Up @@ -145,26 +145,26 @@ pub struct Database {

#[wasm_bindgen]
impl Database {

#[wasm_bindgen(js_name = "start")]
pub async fn start(&mut self) -> Result<JsValue, JsValue> {
Logger::log(&"Starting the database...".into());
Logger::debug("DB", &"Starting the database...".into());
if !self.started {
let res = self.storage.internal.start().await?;
self.started = true;
Logger::log(&"Database started successfully.".into());
return Ok(res);
Logger::debug("DB", &"Database started successfully.".into());
Ok(res)
} else {
Ok(JsValue::from_str("Database already started"))
}
return Ok(
JsValue::from_str("Database already started")
);
}

#[wasm_bindgen(js_name = "close")]
pub async fn close(mut self) -> Result<JsValue, JsValue> {
Logger::debug(&"Closing the database...".into());
Logger::debug("DB",&"Closing the database...".into());
let res = self.storage.internal.close().await;
self.started = false;
Logger::debug(&"Database closed successfully.".into());
Logger::debug("DB",&"Database closed successfully.".into());
res
}

Expand All @@ -173,7 +173,6 @@ impl Database {
self.started
}


/// Retrieves the collections in the database.
///
/// This function returns an `Object` containing the collections.
Expand All @@ -183,29 +182,22 @@ impl Database {
/// * `Result<Object, JsValue>` - A result containing an `Object` with the collections or an error.
#[wasm_bindgen(getter)]
pub fn collections(&self) -> Result<Object, JsValue> {
Logger::debug(&"Retrieving collections...".into());
let mut collections: HashMap<String, Collection> = HashMap::new();
Logger::debug("DB",&"Retrieving collections...".into());
let object = Object::new();
for (key, _) in self.storage.schemas.iter() {
Logger::debug(&format!("Processing collection: {}", key).into());
Logger::debug("DB",&format!("Processing collection: {}", key).into());
let storage = self.storage.clone();
let collection = Collection::from(
key.clone(),
storage
);
collections.insert(
key.clone(),
collection
);
}
let object = Object::new();
for (key, collection) in collections {
Reflect::set(
&object,
&JsValue::from_str(key.as_str()),
&JsValue::from(collection)
).map_err(|e| JsValue::from(RIDBError::from(e)))?;
}
Logger::debug(&"Collections retrieved successfully.".into());
Logger::debug("DB",&"Collections retrieved successfully.".into());
Ok(object)
}

Expand All @@ -219,43 +211,15 @@ impl Database {
password: Option<String>,
storage: Option<StorageExternal>
) -> Result<Database, JsValue> {
Logger::debug(&format!("Creating database: {}", db_name).into());
let storage: StorageExternal = if let Some(storage) = storage {
Logger::debug(&"Using provided storage.".into());
storage.into()
} else {
Logger::debug(&"Creating InMemory storage.".into());
JsValue::from(InMemory::create(db_name, schemas_js.clone()).await?).into()
};


let vec_plugins_js: Vec<JsValue> = module.apply(plugins)?;
Logger::debug(&"Plugins applied.".into());
let mut vec_plugins: Vec<BasePlugin> = vec_plugins_js.into_iter()
.map(|plugin| plugin.unchecked_into::<BasePlugin>())
.collect();

Logger::debug(&"Adding defaults plugin.".into());
vec_plugins.push(DefaultsPlugin::new()?.base.clone());

Logger::debug(&"Adding migration plugin.".into());
vec_plugins.push(MigrationPlugin::new()?.base.clone());

Logger::debug(&"Adding integrity plugin.".into());
vec_plugins.push(IntegrityPlugin::new()?.base.clone());

if let Some(pass) = password {
Logger::debug(&"Adding encryption plugin.".into());
let encryption = EncryptionPlugin::new(pass)?;
vec_plugins.push(encryption.base.clone());
}

Logger::debug("DB",&format!("Creating database: {}", db_name).into());
let mut schemas: HashMap<String, Schema> = HashMap::new();
let mut migrations: HashMap<String, JsValue> = HashMap::new();
let keys = Object::keys(&schemas_js.clone()).into_iter();

for collection in keys {
let collection_string: String = collection.as_string().ok_or("Invalid collection name")?;
Logger::debug(&format!("Processing schema for collection: {}", collection_string).into());
Logger::debug("DB",&format!("Processing schema for collection: {}", collection_string).into());
let schema_type = Reflect::get(&schemas_js.clone(), &collection)?;
let schema = Schema::create(schema_type)?;
let migration = Reflect::get(&migrations_js.clone(), &collection)?;
Expand All @@ -266,7 +230,7 @@ impl Database {
.map_err(|e| RIDBError::from(e))?;

if function.is_undefined() {
Logger::debug(&format!("Migration path undefined for collection: {}, version: {}", collection_string, version).into());
Logger::debug("DB",&format!("Migration path undefined for collection: {}, version: {}", collection_string, version).into());
return Err(
JsValue::from(
format!("Required Schema {} migration path {} to not be undefined", collection_string, version)
Expand All @@ -275,22 +239,55 @@ impl Database {
}
}

schemas.insert(collection_string.clone(), schema);
migrations.insert(collection_string, migration);
schemas.insert(collection_string.clone(), schema.clone());
migrations.insert(collection_string.clone(), migration);

}


let storage: StorageExternal = if let Some(storage) = storage {
Logger::debug("DB",&"Using provided storage.".into());
storage.into()
} else {
Logger::debug("DB",&"Creating InMemory storage.".into());
JsValue::from(InMemory::create(db_name, schemas_js.clone()).await?).into()
};

let vec_plugins_js: Vec<JsValue> = module.apply(plugins)?;
Logger::debug("DB",&"Plugins applied.".into());
let mut vec_plugins: Vec<BasePlugin> = vec_plugins_js.into_iter()
.map(|plugin| plugin.unchecked_into::<BasePlugin>())
.collect();

Logger::debug("DB",&"Adding defaults plugin.".into());
vec_plugins.push(DefaultsPlugin::new()?.base.clone());

Logger::debug("DB",&"Adding migration plugin.".into());
vec_plugins.push(MigrationPlugin::new()?.base.clone());

Logger::debug("DB",&"Adding integrity plugin.".into());
vec_plugins.push(IntegrityPlugin::new()?.base.clone());

if let Some(pass) = password {
Logger::debug("DB",&"Adding encryption plugin.".into());
let encryption = EncryptionPlugin::new(pass)?;
vec_plugins.push(encryption.base.clone());
}

Logger::debug(&"Creating storage with schemas and migrations.".into());


Logger::debug("DB",&"Creating storage with schemas and migrations.".into());
let mounted_storage = Storage::create(
schemas,
migrations,
vec_plugins,
storage.clone()
).map_err(|e| JsValue::from(RIDBError::from(e)))?;

Logger::debug(&"Database created successfully.".into());
Logger::debug("DB",&"Database created successfully.".into());
let db = Database { storage:mounted_storage, started: false };

storage.start().await.unwrap();
storage.start().await?;

Ok(db)
}
Expand Down
Loading

0 comments on commit 63bf47e

Please sign in to comment.