Skip to content

Commit 7a6256a

Browse files
committed
improve on compound key API
1 parent 8e9193f commit 7a6256a

File tree

6 files changed

+69
-67
lines changed

6 files changed

+69
-67
lines changed

src/cursor.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::{
33
utils::{
44
map_cursor_advance_err, map_cursor_advance_until_err,
55
map_cursor_advance_until_primary_key_err, map_cursor_delete_err, map_cursor_update_err,
6-
slice_to_array,
76
},
87
};
98
use std::marker::PhantomData;
@@ -105,9 +104,6 @@ impl<Err> Cursor<Err> {
105104

106105
/// Advance this [`Cursor`] until the provided key
107106
///
108-
/// Note that if this [`Cursor`] was built from an [`Index`], then you need to
109-
/// encode the [`Array`] yourself.
110-
///
111107
/// Internally, this uses [`IDBCursor::continue`](https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor/continue).
112108
pub async fn advance_until(&mut self, key: &JsValue) -> crate::Result<(), Err> {
113109
let Some(sys) = &self.sys else {
@@ -133,13 +129,13 @@ impl<Err> Cursor<Err> {
133129
/// Internally, this uses [`IDBCursor::continuePrimaryKey`](https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor/continuePrimaryKey).
134130
pub async fn advance_until_primary_key(
135131
&mut self,
136-
index_key: &[&JsValue],
132+
index_key: &JsValue,
137133
primary_key: &JsValue,
138134
) -> crate::Result<(), Err> {
139135
let Some(sys) = &self.sys else {
140136
return Err(crate::Error::CursorCompleted);
141137
};
142-
sys.continue_primary_key(&slice_to_array(index_key), primary_key)
138+
sys.continue_primary_key(&index_key, primary_key)
143139
.map_err(map_cursor_advance_until_primary_key_err)?;
144140
if transaction_request(self.req.clone()).await?.is_null() {
145141
self.sys = None;

src/database.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{transaction::TransactionBuilder, utils::str_slice_to_array, ObjectStore};
22
use std::marker::PhantomData;
3-
use web_sys::{IdbDatabase, IdbObjectStoreParameters};
3+
use web_sys::{js_sys::JsString, IdbDatabase, IdbObjectStoreParameters};
44

55
/// Wrapper for [`IDBDatabase`](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase)
66
#[derive(Debug)]
@@ -123,11 +123,21 @@ impl<'a, Err> ObjectStoreBuilder<'a, Err> {
123123
.map(ObjectStore::from_sys)
124124
}
125125

126+
/// Set the key path for out-of-line keys
127+
///
128+
/// If you want to use a compound primary key made of multiple attributes, please see [`ObjectStoreBuilder::compound_key_path`].
129+
///
130+
/// Internally, this [sets this setting](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore#keypath).
131+
pub fn key_path(mut self, path: &str) -> Self {
132+
self.options.key_path(Some(&JsString::from(path)));
133+
self
134+
}
135+
126136
/// Set the key path for out-of-line keys
127137
///
128138
/// Internally, this [sets this setting](https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase/createObjectStore#keypath).
129-
pub fn key_path(mut self, path: &[&str]) -> Self {
130-
self.options.key_path(Some(&str_slice_to_array(path)));
139+
pub fn compound_key_path(mut self, paths: &[&str]) -> Self {
140+
self.options.key_path(Some(&str_slice_to_array(paths)));
131141
self
132142
}
133143

src/index.rs

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
use crate::{
22
transaction::transaction_request,
33
utils::{
4-
array_to_vec, make_key_range_from_slice, map_count_err, map_count_res, map_get_err,
5-
none_if_undefined, slice_to_array,
4+
array_to_vec, make_key_range, map_count_err, map_count_res, map_get_err, none_if_undefined,
65
},
76
};
87
use futures_util::future::{Either, FutureExt};
@@ -11,6 +10,10 @@ use web_sys::{wasm_bindgen::JsValue, IdbIndex};
1110

1211
/// Wrapper for [`IDBIndex`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex),
1312
/// for use in transactions
13+
///
14+
/// Most of the functions here take a [`JsValue`] as the key(s) to use in the index. If the index was
15+
/// built with a compound key, then you should use eg. `js_sys::Array::from_iter([key_1, key_2])` as
16+
/// the key.
1417
pub struct Index<Err> {
1518
sys: IdbIndex,
1619
_phantom: PhantomData<Err>,
@@ -27,8 +30,8 @@ impl<Err> Index<Err> {
2730
/// Checks whether the provided key (for this index) already exists
2831
///
2932
/// Internally, this uses [`IDBIndex::count`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/count).
30-
pub fn contains(&self, key: &[&JsValue]) -> impl Future<Output = crate::Result<bool, Err>> {
31-
match self.sys.count_with_key(&slice_to_array(key)) {
33+
pub fn contains(&self, key: &JsValue) -> impl Future<Output = crate::Result<bool, Err>> {
34+
match self.sys.count_with_key(key) {
3235
Ok(count_req) => Either::Right(
3336
transaction_request(count_req).map(|res| res.map(map_count_res).map(|n| n != 0)),
3437
),
@@ -39,11 +42,11 @@ impl<Err> Index<Err> {
3942
/// Count all the keys (for this index) in the provided range
4043
///
4144
/// Internally, this uses [`IDBIndex::count`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/count).
42-
pub fn count_in<'a>(
45+
pub fn count_in(
4346
&self,
44-
range: impl RangeBounds<[&'a JsValue]>,
47+
range: impl RangeBounds<JsValue>,
4548
) -> impl Future<Output = crate::Result<usize, Err>> {
46-
let range = match make_key_range_from_slice(range) {
49+
let range = match make_key_range(range) {
4750
Ok(range) => range,
4851
Err(e) => return Either::Left(std::future::ready(Err(e))),
4952
};
@@ -58,11 +61,8 @@ impl<Err> Index<Err> {
5861
/// Get the object with key `key` for this index
5962
///
6063
/// Internally, this uses [`IDBIndex::get`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/get).
61-
pub fn get(
62-
&self,
63-
key: &[&JsValue],
64-
) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
65-
match self.sys.get(&slice_to_array(key)) {
64+
pub fn get(&self, key: &JsValue) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
65+
match self.sys.get(key) {
6666
Ok(get_req) => {
6767
Either::Right(transaction_request(get_req).map(|res| res.map(none_if_undefined)))
6868
}
@@ -75,11 +75,11 @@ impl<Err> Index<Err> {
7575
/// Note that the unbounded range is not a valid range for IndexedDB.
7676
///
7777
/// Internally, this uses [`IDBIndex::get`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/get).
78-
pub fn get_first_in<'a>(
78+
pub fn get_first_in(
7979
&self,
80-
range: impl RangeBounds<[&'a JsValue]>,
80+
range: impl RangeBounds<JsValue>,
8181
) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
82-
let range = match make_key_range_from_slice(range) {
82+
let range = match make_key_range(range) {
8383
Ok(range) => range,
8484
Err(e) => return Either::Left(std::future::ready(Err(e))),
8585
};
@@ -116,12 +116,12 @@ impl<Err> Index<Err> {
116116
/// results of `limit`, ordered by this index
117117
///
118118
/// Internally, this uses [`IDBIndex::getAll`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAll).
119-
pub fn get_all_in<'a>(
119+
pub fn get_all_in(
120120
&self,
121-
range: impl RangeBounds<[&'a JsValue]>,
121+
range: impl RangeBounds<JsValue>,
122122
limit: Option<u32>,
123123
) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
124-
let range = match make_key_range_from_slice(range) {
124+
let range = match make_key_range(range) {
125125
Ok(range) => range,
126126
Err(e) => return Either::Left(std::future::ready(Err(e))),
127127
};
@@ -140,11 +140,11 @@ impl<Err> Index<Err> {
140140
/// Get the first existing key (for this index) in the provided range
141141
///
142142
/// Internally, this uses [`IDBIndex::getKey`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getKey).
143-
pub fn get_first_key_in<'a>(
143+
pub fn get_first_key_in(
144144
&self,
145-
range: impl RangeBounds<[&'a JsValue]>,
145+
range: impl RangeBounds<JsValue>,
146146
) -> impl Future<Output = crate::Result<Option<JsValue>, Err>> {
147-
let range = match make_key_range_from_slice(range) {
147+
let range = match make_key_range(range) {
148148
Ok(range) => range,
149149
Err(e) => return Either::Left(std::future::ready(Err(e))),
150150
};
@@ -181,12 +181,12 @@ impl<Err> Index<Err> {
181181
/// ordered by this index
182182
///
183183
/// Internally, this uses [`IDBIndex::getAllKeys`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex/getAllKeys).
184-
pub fn get_all_keys_in<'a>(
184+
pub fn get_all_keys_in(
185185
&self,
186-
range: impl RangeBounds<[&'a JsValue]>,
186+
range: impl RangeBounds<JsValue>,
187187
limit: Option<u32>,
188188
) -> impl Future<Output = crate::Result<Vec<JsValue>, Err>> {
189-
let range = match make_key_range_from_slice(range) {
189+
let range = match make_key_range(range) {
190190
Ok(range) => range,
191191
Err(e) => return Either::Left(std::future::ready(Err(e))),
192192
};

src/object_store.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
use futures_util::future::{Either, FutureExt};
1111
use std::{future::Future, marker::PhantomData, ops::RangeBounds};
1212
use web_sys::{
13-
js_sys::Array, wasm_bindgen::JsValue, IdbCursorDirection, IdbIndexParameters, IdbObjectStore,
13+
js_sys::JsString, wasm_bindgen::JsValue, IdbCursorDirection, IdbIndexParameters, IdbObjectStore,
1414
};
1515

1616
/// Wrapper for [`IDBObjectStore`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore),
@@ -34,17 +34,40 @@ impl<Err> ObjectStore<Err> {
3434
/// Note that this method can only be called from within an `on_upgrade_needed` callback. It returns
3535
/// a builder, and calling the `create` method on this builder will perform the actual creation.
3636
///
37+
/// If you want to make an index that searches multiple columns, please use [`ObjectStore::build_compound_index`].
38+
///
39+
/// Internally, this uses [`IDBObjectStore::createIndex`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/createIndex).
40+
pub fn build_index<'a>(&self, name: &'a str, key_path: &str) -> IndexBuilder<'a, Err> {
41+
IndexBuilder {
42+
store: self.sys.clone(),
43+
name,
44+
key_path: JsString::from(key_path).into(),
45+
options: IdbIndexParameters::new(),
46+
_phantom: PhantomData,
47+
}
48+
}
49+
50+
/// Build a compound index over this object store
51+
///
52+
/// Note that this method can only be called from within an `on_upgrade_needed` callback. It returns
53+
/// a builder, and calling the `create` method on this builder will perform the actual creation.
54+
///
3755
/// Interesting points about indices:
3856
/// - It is not possible to index `bool` in IndexedDB.
3957
/// - If your index uses a column that does not exist, then the object will not be recorded in the index.
40-
/// This is useful for unique compound indices, usually when you'd have indexed a `bool` column otherwise.
58+
/// This is useful for unique compound indices, usually when you would have conditionally indexed a `bool` column otherwise.
59+
/// - You cannot build a compound multi-entry index, it needs to be a regular index.
4160
///
4261
/// Internally, this uses [`IDBObjectStore::createIndex`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/createIndex).
43-
pub fn build_index<'a>(&self, name: &'a str, key_path: &[&str]) -> IndexBuilder<'a, Err> {
62+
pub fn build_compound_index<'a>(
63+
&self,
64+
name: &'a str,
65+
key_paths: &[&str],
66+
) -> IndexBuilder<'a, Err> {
4467
IndexBuilder {
4568
store: self.sys.clone(),
4669
name,
47-
key_path: str_slice_to_array(key_path),
70+
key_path: str_slice_to_array(key_paths).into(),
4871
options: IdbIndexParameters::new(),
4972
_phantom: PhantomData,
5073
}
@@ -389,7 +412,7 @@ impl<Err> ObjectStore<Err> {
389412
pub struct IndexBuilder<'a, Err> {
390413
store: IdbObjectStore,
391414
name: &'a str,
392-
key_path: Array,
415+
key_path: JsValue,
393416
options: IdbIndexParameters,
394417
_phantom: PhantomData<Err>,
395418
}

src/utils.rs

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,6 @@ pub(crate) fn array_to_vec(v: JsValue) -> Vec<JsValue> {
4343
res
4444
}
4545

46-
pub(crate) fn slice_to_array(s: &[&JsValue]) -> Array {
47-
let res = Array::new_with_length(u32::try_from(s.len()).unwrap());
48-
for (i, v) in s.iter().enumerate() {
49-
res.set(u32::try_from(i).unwrap(), (*v).clone());
50-
}
51-
res
52-
}
53-
5446
pub(crate) fn str_slice_to_array(s: &[&str]) -> Array {
5547
let res = Array::new_with_length(u32::try_from(s.len()).unwrap());
5648
for (i, v) in s.iter().enumerate() {
@@ -187,25 +179,6 @@ pub(crate) fn map_cursor_update_err<Err>(err: JsValue) -> crate::Error<Err> {
187179
}
188180
}
189181

190-
fn bound_map<T, U>(b: Bound<T>, f: impl FnOnce(T) -> U) -> Bound<U> {
191-
// TODO: replace with Bound::map once https://github.com/rust-lang/rust/issues/86026 is stable
192-
match b {
193-
Bound::Unbounded => Bound::Unbounded,
194-
Bound::Included(b) => Bound::Included(f(b)),
195-
Bound::Excluded(b) => Bound::Excluded(f(b)),
196-
}
197-
}
198-
199-
pub(crate) fn make_key_range_from_slice<'a, Err>(
200-
range: impl RangeBounds<[&'a JsValue]>,
201-
) -> crate::Result<JsValue, Err> {
202-
let range: (Bound<JsValue>, Bound<JsValue>) = (
203-
bound_map(range.start_bound(), |s| slice_to_array(s).into()),
204-
bound_map(range.end_bound(), |s| slice_to_array(s).into()),
205-
);
206-
make_key_range(range)
207-
}
208-
209182
pub(crate) fn make_key_range<Err>(range: impl RangeBounds<JsValue>) -> crate::Result<JsValue, Err> {
210183
match (range.start_bound(), range.end_bound()) {
211184
(Bound::Unbounded, Bound::Unbounded) => return Err(crate::Error::InvalidRange),

tests/js.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ async fn smoke_test() {
5050
let db = evt.database();
5151
db.build_object_store("objects").create()?;
5252
db.build_object_store("things")
53-
.key_path(&["foo", "bar"])
53+
.compound_key_path(&["foo", "bar"])
5454
.create()?;
5555
let stuffs = db.build_object_store("stuffs").auto_increment().create()?;
56-
stuffs.build_index("contents", &[""]).create()?;
56+
stuffs.build_index("contents", "").create()?;
5757
Ok(())
5858
})
5959
.await

0 commit comments

Comments
 (0)