Skip to content

Commit 62697f8

Browse files
BertholetDamiendbertholet-openmindthomastay
authored
Fix removeRange in InMemoryProvider (#67)
* Fix removeRange in InMemoryProvider * Bump minor version to 0.8.1 * Add error log * Add unit test coverage * Suupport non unique index scenario for removeRange * Add FF --------- Co-authored-by: Damien Bertholet <dbertholet@openmindt.com> Co-authored-by: Thomas Tay <thomastayac@gmail.com>
1 parent 2b48454 commit 62697f8

3 files changed

Lines changed: 187 additions & 7 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@microsoft/objectstoreprovider",
3-
"version": "0.8.1",
3+
"version": "0.8.2",
44
"description": "A cross-browser object store library",
55
"author": "Mukundan Kavanur Kidambi <mukav@microsoft.com>",
66
"scripts": {

src/InMemoryProvider.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,14 @@ export interface StoreData {
6464

6565
export interface ILiveConsumerConfigs {
6666
usePushForGetRange: boolean;
67+
usePrimaryKeyForGetKeysForRange: boolean;
6768
}
6869

6970
export type GetLiveConsumerConfigsFn = () => ILiveConsumerConfigs;
7071

7172
const defaultLiveConsumerConfigs: ILiveConsumerConfigs = {
7273
usePushForGetRange: false,
74+
usePrimaryKeyForGetKeysForRange: false,
7375
};
7476

7577
export class InMemoryProvider extends DbProvider {
@@ -915,23 +917,51 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
915917
lowRangeExclusive?: boolean,
916918
highRangeExclusive?: boolean
917919
): string[] {
920+
const usePrimaryKey = this.getLiveConfigs().usePrimaryKeyForGetKeysForRange;
918921
const keyLow = serializeKeyToString(keyLowRange, this._keyPath);
919922
const keyHigh = serializeKeyToString(keyHighRange, this._keyPath);
920923
const iterator = this._indexTree.entries();
921-
const keys = [];
924+
const keys: string[] = [];
925+
922926
for (const entry of iterator) {
923-
const key = entry.key;
927+
let key = entry.key;
924928
if (key === undefined) {
925929
continue;
926930
}
927931

928-
if (
932+
const isMatch =
929933
(key > keyLow || (key === keyLow && !lowRangeExclusive)) &&
930-
(key < keyHigh || (key === keyHigh && !highRangeExclusive))
934+
(key < keyHigh || (key === keyHigh && !highRangeExclusive));
935+
if (!isMatch) {
936+
continue;
937+
}
938+
939+
// If the current index is not the primary one. We need to find primary key for each value and return that instead.
940+
if (
941+
usePrimaryKey &&
942+
entry.value &&
943+
this._keyPath !== this._primaryKeyPath
931944
) {
945+
for (const value of entry.value) {
946+
key = getSerializedKeyForKeypath(value, this._primaryKeyPath) ?? key;
947+
948+
if (key === entry.key) {
949+
this.logger.warn(
950+
`getSerializedKeyForKeypath returned undefined key in InMemoryIndex for table: ${this.tableName}, with index: ${this._indexSchema?.name}`
951+
);
952+
}
953+
954+
if (!keys.includes(key)) {
955+
keys.push(key);
956+
}
957+
}
958+
}
959+
// Otherwise, we can just use the key as is.
960+
else {
932961
keys.push(key);
933962
}
934963
}
964+
935965
return keys;
936966
}
937967

src/tests/ObjectStoreProvider.spec.ts

Lines changed: 152 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,26 @@ function openProvider(
3535

3636
switch (providerName) {
3737
case "memory-rbtree":
38-
provider = new InMemoryProvider("red-black-tree", supportsRollback);
38+
provider = new InMemoryProvider(
39+
"red-black-tree",
40+
supportsRollback,
41+
undefined,
42+
() => ({
43+
usePushForGetRange: false,
44+
usePrimaryKeyForGetKeysForRange: true,
45+
})
46+
);
3947
break;
4048
case "memory-btree":
41-
provider = new InMemoryProvider("b+tree", supportsRollback);
49+
provider = new InMemoryProvider(
50+
"b+tree",
51+
supportsRollback,
52+
undefined,
53+
() => ({
54+
usePushForGetRange: false,
55+
usePrimaryKeyForGetKeysForRange: true,
56+
})
57+
);
4258
break;
4359
case "indexeddb":
4460
provider = new IndexedDbProvider();
@@ -1886,6 +1902,140 @@ describe("ObjectStoreProvider", function () {
18861902
);
18871903
});
18881904

1905+
it("Remove range (all removed with index)", (done) => {
1906+
// Not working with index, need to be fix in the future.
1907+
if (provName === "indexeddbfakekeys") {
1908+
done();
1909+
return;
1910+
}
1911+
1912+
openProvider(
1913+
provName,
1914+
{
1915+
version: 1,
1916+
stores: [
1917+
{
1918+
name: "test",
1919+
primaryKeyPath: "id",
1920+
indexes: [
1921+
{
1922+
name: "index",
1923+
keyPath: "a",
1924+
},
1925+
],
1926+
},
1927+
],
1928+
},
1929+
true
1930+
)
1931+
.then((prov) => {
1932+
return prov
1933+
.put(
1934+
"test",
1935+
[1, 2, 3, 4, 5].map((i) => {
1936+
return { id: "a" + i, a: "index_value_a_" + i };
1937+
})
1938+
)
1939+
.then(() => {
1940+
return prov.getAll("test", undefined).then((rets) => {
1941+
assert(!!rets);
1942+
assert.equal(rets.length, 5);
1943+
return prov
1944+
.removeRange(
1945+
"test",
1946+
"index",
1947+
"index_value_a_1",
1948+
"index_value_a_5"
1949+
)
1950+
.then(() => {
1951+
return prov
1952+
.getAll("test", undefined)
1953+
.then((retVals) => {
1954+
const rets = retVals as TestObj[];
1955+
assert(!!rets);
1956+
assert.equal(rets.length, 0);
1957+
});
1958+
});
1959+
});
1960+
})
1961+
.then(() => prov.close())
1962+
.catch((e) => prov.close().then(() => Promise.reject(e)));
1963+
})
1964+
.then(
1965+
() => done(),
1966+
(err) => done(err)
1967+
);
1968+
});
1969+
1970+
it("Remove range (all removed with index not unique)", (done) => {
1971+
// Not working with index, need to be fix in the future.
1972+
if (provName === "indexeddbfakekeys") {
1973+
done();
1974+
return;
1975+
}
1976+
1977+
openProvider(
1978+
provName,
1979+
{
1980+
version: 1,
1981+
stores: [
1982+
{
1983+
name: "test",
1984+
primaryKeyPath: "id",
1985+
indexes: [
1986+
{
1987+
name: "index",
1988+
keyPath: "a",
1989+
unique: false,
1990+
},
1991+
],
1992+
},
1993+
],
1994+
},
1995+
true
1996+
)
1997+
.then((prov) => {
1998+
return prov
1999+
.put(
2000+
"test",
2001+
[1, 1, 2, 3, 4, 5].map((i, index) => {
2002+
return {
2003+
id: index,
2004+
a: "index_value_a_" + i,
2005+
};
2006+
})
2007+
)
2008+
.then(() => {
2009+
return prov.getAll("test", undefined).then((rets) => {
2010+
assert(!!rets);
2011+
assert.equal(rets.length, 6);
2012+
return prov
2013+
.removeRange(
2014+
"test",
2015+
"index",
2016+
"index_value_a_1",
2017+
"index_value_a_5"
2018+
)
2019+
.then(() => {
2020+
return prov
2021+
.getAll("test", undefined)
2022+
.then((retVals) => {
2023+
const rets = retVals as TestObj[];
2024+
assert(!!rets);
2025+
assert.equal(rets.length, 0);
2026+
});
2027+
});
2028+
});
2029+
})
2030+
.then(() => prov.close())
2031+
.catch((e) => prov.close().then(() => Promise.reject(e)));
2032+
})
2033+
.then(
2034+
() => done(),
2035+
(err) => done(err)
2036+
);
2037+
});
2038+
18892039
it("Invalid Key Type", (done) => {
18902040
openProvider(
18912041
provName,

0 commit comments

Comments
 (0)