Merge pull request #10 from M1Les/m1les/fix/non-unique-idx

Implement support for non-unique indices
This commit is contained in:
Sudhar123 2021-06-16 11:11:36 -07:00 коммит произвёл GitHub
Родитель de4f145f9e f681b2230d
Коммит 0af298110c
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 470 добавлений и 54 удалений

14
dist/NoSQLProviderTestsPack.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

10
dist/src/InMemoryProvider.d.ts поставляемый
Просмотреть файл

@ -50,7 +50,17 @@ declare class InMemoryIndex extends DbIndexFTSFromRangeQueries {
getOnly(key: KeyType, reverseOrSortOrder?: boolean | QuerySortOrder, limit?: number, offset?: number): Promise<ItemType[]>;
getRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean, reverseOrSortOrder?: boolean | QuerySortOrder, limit?: number, offset?: number): Promise<ItemType[]>;
getKeysForRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean): Promise<any[]>;
/**
* Utility function to simplify offset/limit checks and allow a negative offset. Retrieves values associated with the given key
* @param key primary key
* @param limit
* @param offset can be neagtive, treated the same way as 0
* @param reverse
* @returns value associated with given key, undefined if the key is not found.
*/
private _getKeyValues;
private _getKeysForRange;
private _getKeyCountForRange;
countAll(): Promise<number>;
countOnly(key: KeyType): Promise<number>;
countRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean): Promise<number>;

2
dist/src/NoSqlProviderUtils.d.ts поставляемый
Просмотреть файл

@ -11,9 +11,11 @@ declare global {
documentMode: number;
}
}
export declare const MAX_COUNT = 4294967295;
export declare function isIE(): boolean;
export declare function isSafari(): boolean;
export declare function arrayify<T>(obj: T | T[]): T[];
export declare function trimArray<TValue>(array: Array<TValue>, trimLength: number): Array<TValue>;
export declare function getSerializedKeyForKeypath(obj: any, keyPathRaw: KeyPathType): string | undefined;
export declare function getKeyForKeypath(obj: any, keyPathRaw: KeyPathType): KeyType | undefined;
export declare function getValueForSingleKeypath(obj: any, singleKeyPath: string): any;

Просмотреть файл

@ -6,7 +6,7 @@
* NoSqlProvider provider setup for a non-persisted in-memory database backing provider.
*/
import { attempt, isError, each, includes, compact, map, find, values, flatten } from 'lodash';
import { attempt, isError, each, includes, compact, map, find, values, flatten, dropRight, takeRight, drop, take } from 'lodash';
import { DbIndexFTSFromRangeQueries, getFullTextIndexWordsForItem } from './FullTextSearchHelpers';
import {
StoreSchema, DbProvider, DbSchema, DbTransaction,
@ -14,12 +14,12 @@ import {
} from './NoSqlProvider';
import {
arrayify, serializeKeyToString, formListOfSerializedKeys,
getSerializedKeyForKeypath, getValueForSingleKeypath
getSerializedKeyForKeypath, getValueForSingleKeypath, MAX_COUNT, trimArray
} from './NoSqlProviderUtils';
import { TransactionToken, TransactionLockHelper } from './TransactionLockHelper';
import {
empty, RedBlackTreeStructure, set, iterateFromIndex,
iterateKeysFromFirst, get, iterateKeysFromLast, has, remove
iterateKeysFromFirst, get, iterateKeysFromLast, has, remove, iterateFromFirst
} from '@collectable/red-black-tree';
export interface StoreData {
data: Map<string, ItemType>;
@ -405,20 +405,50 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
}
getAll(reverseOrSortOrder?: boolean | QuerySortOrder, limit?: number, offset?: number): Promise<ItemType[]> {
limit = limit ? limit : this._rbIndex._size;
limit = limit ? limit :
this.isUniqueIndex() ? this._rbIndex._size :
MAX_COUNT;
offset = offset ? offset : 0;
const data = new Array<ItemType>(limit);
const reverse = (reverseOrSortOrder === true || reverseOrSortOrder === QuerySortOrder.Reverse);
const iterator = iterateFromIndex(reverse, offset, this._rbIndex);
// when index is not unique, we cannot use offset as a starting index
const iterator = iterateFromIndex(reverse, this.isUniqueIndex() ? offset : 0, this._rbIndex);
let i = 0;
for (const item of iterator) {
data[i] = item.value[0];
i++;
// when index is not unique, each node may contain multiple items
if (!this.isUniqueIndex()){
let count = item.value.length;
const minOffsetCount = Math.min(count, offset);
count -= minOffsetCount;
offset -= minOffsetCount;
// we have skipped all values in this index, go to the next one
if (count === 0) {
continue;
}
const values = this._getKeyValues(item.key, limit - i, item.value.length - count, reverse)
values.forEach((v, j) => {
data[i + j] = v;
});
i += values.length;
} else {
data[i] = item.value[0];
i++;
}
if (i >= limit) {
break;
}
}
return Promise.resolve(data);
// if index is not unique, trim the empty slots in data
// if we used MAX_COUNT to construct it.
if (!this.isUniqueIndex() && i!==limit) {
return Promise.resolve(trimArray(data, i));
} else {
return Promise.resolve(data);
}
}
getOnly(key: KeyType, reverseOrSortOrder?: boolean | QuerySortOrder, limit?: number, offset?: number)
@ -428,35 +458,56 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
getRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean,
reverseOrSortOrder?: boolean | QuerySortOrder, limit?: number, offset?: number): Promise<ItemType[]> {
const values = attempt(() => {
const reverse = reverseOrSortOrder === true || reverseOrSortOrder === QuerySortOrder.Reverse;
limit = limit ? limit : this._rbIndex._size;
offset = offset ? offset : 0;
const keyLow = serializeKeyToString(keyLowRange, this._keyPath);
const keyHigh = serializeKeyToString(keyHighRange, this._keyPath);
const iterator = reverse ? iterateKeysFromLast(this._rbIndex) : iterateKeysFromFirst(this._rbIndex);
let values = [] as ItemType[];
for (const key of iterator) {
if (
(key > keyLow || (key === keyLow && !lowRangeExclusive)) &&
(key < keyHigh || (key === keyHigh && !highRangeExclusive))) {
if (offset > 0) {
offset--;
continue;
const values = attempt(() => {
const reverse = reverseOrSortOrder === true || reverseOrSortOrder === QuerySortOrder.Reverse;
limit = limit ? limit :
this.isUniqueIndex() ? this._rbIndex._size :
MAX_COUNT;
offset = offset ? offset : 0;
const keyLow = serializeKeyToString(keyLowRange, this._keyPath);
const keyHigh = serializeKeyToString(keyHighRange, this._keyPath);
const iterator = reverse ? iterateKeysFromLast(this._rbIndex) : iterateKeysFromFirst(this._rbIndex);
let values = [] as ItemType[];
for (const key of iterator) {
if (
(key > keyLow || (key === keyLow && !lowRangeExclusive)) &&
(key < keyHigh || (key === keyHigh && !highRangeExclusive))) {
if (offset > 0) {
if (this.isUniqueIndex()) {
offset--;
continue;
} else {
const idxValues = get(key, this._rbIndex) as ItemType[];
offset -= idxValues.length;
// if offset >= 0, we skipped just enough, or we still need to skip more
// if offset < 0, we need to get some of the values from the index
if (offset >= 0) {
continue;
}
}
}
if (values.length >= limit) {
break;
}
if (this.isUniqueIndex()) {
values = values.concat(get(key, this._rbIndex) as ItemType[]);
} else {
values = values.concat(this._getKeyValues(key, limit - values.length, Math.abs(offset), reverse));
if (offset < 0) {
offset = 0;
}
}
}
if (values.length >= limit) {
break;
}
values = values.concat(get(key, this._rbIndex) as ItemType[]);
}
return values;
});
if (isError(values)) {
return Promise.reject(values);
}
return values;
});
if (isError(values)) {
return Promise.reject(values);
}
return Promise.resolve(values);
return Promise.resolve(values);
}
getKeysForRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean)
@ -470,6 +521,44 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
return Promise.resolve(keys);
}
/**
* Utility function to simplify offset/limit checks and allow a negative offset. Retrieves values associated with the given key
* @param key primary key
* @param limit
* @param offset can be neagtive, treated the same way as 0
* @param reverse
* @returns value associated with given key, undefined if the key is not found.
*/
private _getKeyValues(key: string, limit: number, offset: number, reverse: boolean): ItemType[] {
if (limit <= 0) {
return [];
}
const idxValues = get(key, this._rbIndex) as ItemType[];
// get may return undefined, if the key is not found
if (!idxValues) {
return idxValues;
}
if (offset >= idxValues.length) {
return [];
}
// Perf optimisation. No items to skip, and limit is at least the number of items we have in the index.
// we know that we will need the whole index values array to fulfill the results,
// skip using take/drop, return the whole array immediately.
if (offset <= 0 && limit >= idxValues.length) {
return reverse ? idxValues.slice().reverse() : idxValues;
}
const itemsToDrop = Math.min(limit, offset);
const itemsToTake = Math.min(limit, idxValues.length - offset);
return reverse ?
takeRight(dropRight(idxValues, itemsToDrop), itemsToTake)
: take(drop(idxValues, itemsToDrop), itemsToTake);
}
// Warning: This function can throw, make sure to trap.
private _getKeysForRange(keyLowRange: KeyType, keyHighRange: KeyType,
lowRangeExclusive?: boolean, highRangeExclusive?: boolean): string[] {
@ -485,8 +574,43 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
return keys;
}
// Warning: This function can throw, make sure to trap.
private _getKeyCountForRange(keyLowRange: KeyType, keyHighRange: KeyType,
lowRangeExclusive?: boolean, highRangeExclusive?: boolean): number {
const keyLow = serializeKeyToString(keyLowRange, this._keyPath);
const keyHigh = serializeKeyToString(keyHighRange, this._keyPath);
const iterator = iterateFromFirst(this._rbIndex);
let keyCount = 0;
for (const item of iterator) {
if ((item.key > keyLow || (item.key === keyLow && !lowRangeExclusive)) && (item.key < keyHigh || (item.key === keyHigh && !highRangeExclusive))) {
if (this.isUniqueIndex()) {
keyCount++;
} else {
keyCount += item.value.length;
}
}
}
return keyCount;
}
countAll(): Promise<number> {
return Promise.resolve(this._rbIndex._size);
if (this.isUniqueIndex()) {
return Promise.resolve(this._rbIndex._size);
} else {
const keyCount = attempt(() => {
const iterator = iterateFromFirst(this._rbIndex);
let keyCount = 0;
for (const item of iterator) {
keyCount += item.value.length;
}
return keyCount;
});
if (isError(keyCount)) {
return Promise.reject(keyCount);
}
return Promise.resolve(keyCount);
}
}
countOnly(key: KeyType): Promise<number> {
@ -495,13 +619,27 @@ class InMemoryIndex extends DbIndexFTSFromRangeQueries {
countRange(keyLowRange: KeyType, keyHighRange: KeyType, lowRangeExclusive?: boolean, highRangeExclusive?: boolean)
: Promise<number> {
const keys = attempt(() => {
return this._getKeysForRange(keyLowRange, keyHighRange, lowRangeExclusive, highRangeExclusive);
});
if (isError(keys)) {
return Promise.reject(keys);
}
if (this.isUniqueIndex()) {
const keys = attempt(() => {
return this._getKeysForRange(keyLowRange, keyHighRange, lowRangeExclusive, highRangeExclusive);
});
return Promise.resolve(keys.length);
if (isError(keys)) {
return Promise.reject(keys);
}
return Promise.resolve(keys.length);
} else {
const keyCount = attempt(() => {
return this._getKeyCountForRange(keyLowRange, keyHighRange, lowRangeExclusive, highRangeExclusive);
});
if (isError(keyCount)) {
return Promise.reject(keyCount);
}
return Promise.resolve(keyCount);
}
}
}
}

Просмотреть файл

@ -17,6 +17,9 @@ declare global {
}
}
// Max array length (uint): 2^32 - 1
export const MAX_COUNT = 4294967295;
export function isIE() {
return (typeof (document) !== 'undefined' && document.all !== null && document.documentMode <= 11) ||
(typeof (navigator) !== 'undefined' && !!navigator.userAgent && navigator.userAgent.indexOf('Edge/') !== -1);
@ -32,6 +35,18 @@ export function arrayify<T>(obj: T | T[]): T[] {
return isArray(obj) ? <T[]>obj : [<T>obj];
}
export function trimArray<TValue>(array: Array<TValue>, trimLength: number): Array<TValue> {
if (trimLength < 0 || array.length < trimLength) {
return array.slice();
}
let ret = new Array<TValue>(trimLength);
for(let j = 0; j < trimLength; j++) {
ret[j] = array[j];
}
return ret;
}
// Constant string for joining compound keypaths for websql and IE indexeddb. There may be marginal utility in using a more obscure
// string sequence.
const keypathJoinerString = '%&';

Просмотреть файл

@ -1,5 +1,5 @@
import * as assert from 'assert';
import { find, each, times, values, keys, some } from 'lodash';
import { find, each, times, values, keys, some, filter } from 'lodash';
import { KeyComponentType, DbSchema, DbProvider, openListOfProviders, QuerySortOrder, FullTextTermResolution, IDBCloseConnectionPayload, OnCloseHandler } from '../NoSqlProvider';
@ -469,6 +469,223 @@ describe('NoSqlProvider', function () {
});
};
var nonUniqueTester = (prov: DbProvider, indexName: string | undefined, compound: boolean,
setter: (obj: any, indexval1: string, indexval2: string) => void) => {
var putters = [1, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5].map((v, idx) => {
var obj: TestObj = { val: 'val' + v };
if (indexName) {
obj.id = 'id' + (idx + 1);
}
setter(obj, 'indexa' + v, 'indexb' + v);
return prov.put('test', obj);
});
return Promise.all(putters).then(() => {
let formIndex = (i: number, i2: number = i): string | string[] => {
if (compound) {
return ['indexa' + i, 'indexb' + i2];
} else {
return 'indexa' + i;
}
};
let t0 = prov.getMultiple('test', compound ? formIndex(1, 1) : 'indexa1', indexName).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 3, 'getMultiple');
[1].forEach(v => { assert(find(ret, r => r.val === 'val' + v), 'cant find ' + v); });
});
let t1 = prov.getAll('test', indexName).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 12, 'getAll');
[1, 2, 3, 4, 5].forEach(v => { assert(find(ret, r => r.val === 'val' + v), 'cant find ' + v); });
});
let t1count = prov.countAll('test', indexName).then(ret => {
assert.equal(ret, 12, 'countAll');
});
let t1b = prov.getAll('test', indexName, false, 3).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 3, 'getAll lim3');
[1].forEach(v => { assert(filter(ret, r => r.val === 'val' + v).length === 3, 'cant find enough ' + v); });
});
let t1c = prov.getAll('test', indexName, false, 3, 1).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 3, 'getAll lim3 off1');
[2, 3, 4].forEach(v => { assert(find(ret, r => r.id === 'id' + v), 'cant find id ' + v); });
});
let t2 = prov.getOnly('test', indexName, formIndex(3)).then(ret => {
assert.equal(ret.length, 3, 'getOnly');
assert.equal((ret[0] as TestObj).val, 'val3');
assert.equal((ret[1] as TestObj).val, 'val3');
assert.equal((ret[2] as TestObj).val, 'val3');
});
let t2count = prov.countOnly('test', indexName, formIndex(3)).then(ret => {
assert.equal(ret, 3, 'countOnly');
});
let t3 = prov.getRange('test', indexName, formIndex(2), formIndex(4)).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 7, 'getRange++');
[4, 5, 6, 7, 8, 9, 10].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t3count = prov.countRange('test', indexName, formIndex(2), formIndex(4)).then(ret => {
assert.equal(ret, 7, 'countRange++');
});
let t3b = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false, false, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange++ lim1');
[2].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let t3b2 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false, false, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange++ lim1');
[2].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let t3b3 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false,
QuerySortOrder.Forward, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange++ lim1');
[2].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let t3b4 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false,
QuerySortOrder.Reverse, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange++ lim1 rev');
[4].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let t3c = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false, false, 1, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange++ lim1 off1');
[5].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t3d = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false, false, 2, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange++ lim2 off1');
[5, 6].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t3d2 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false,
QuerySortOrder.Forward, 2, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange++ lim2 off1');
[5, 6].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t3d3 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false, true, 2, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange++ lim2 off1 rev');
assert.equal((ret[0] as TestObj).val, 'val4');
[9, 8].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t3d4 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, false,
QuerySortOrder.Reverse, 2, 1)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange++ lim2 off1 rev');
assert.equal((ret[0] as TestObj).val, 'val4');
[9, 8].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t4 = prov.getRange('test', indexName, formIndex(2), formIndex(4), true, false).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 5, 'getRange-+');
[6, 7, 8, 9, 10].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t4count = prov.countRange('test', indexName, formIndex(2), formIndex(4), true, false).then(ret => {
assert.equal(ret, 5, 'countRange-+');
});
let t5 = prov.getRange('test', indexName, formIndex(2), formIndex(4), false, true).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 5, 'getRange+-');
[4, 5, 6, 7, 8].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t5count = prov.countRange('test', indexName, formIndex(2), formIndex(4), false, true).then(ret => {
assert.equal(ret, 5, 'countRange+-');
});
let t6 = prov.getRange('test', indexName, formIndex(2), formIndex(4), true, true).then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 3, 'getRange--');
[6, 7, 8].forEach(v => { assert(find(ret, r => r.id === 'id' + v)); });
});
let t6count = prov.countRange('test', indexName, formIndex(2), formIndex(4), true, true).then(ret => {
assert.equal(ret, 3, 'countRange--');
});
return Promise.all([t0, t1, t1count, t1b, t1c, t2, t2count, t3, t3count, t3b, t3b2, t3b3, t3b4, t3c, t3d, t3d2, t3d3,
t3d4, t4, t4count, t5, t5count, t6, t6count]).then(() => {
if (compound) {
let tt1 = prov.getRange('test', indexName, formIndex(2, 2), formIndex(4, 3))
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange2++');
[2, 3].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let tt1count = prov.countRange('test', indexName, formIndex(2, 2), formIndex(4, 3))
.then(ret => {
assert.equal(ret, 2, 'countRange2++');
});
let tt2 = prov.getRange('test', indexName, formIndex(2, 2), formIndex(4, 3), false, true)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 2, 'getRange2+-');
[2, 3].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let tt2count = prov.countRange('test', indexName, formIndex(2, 2), formIndex(4, 3), false, true)
.then(ret => {
assert.equal(ret, 2, 'countRange2+-');
});
let tt3 = prov.getRange('test', indexName, formIndex(2, 2), formIndex(4, 3), true, false)
.then(retVal => {
const ret = retVal as TestObj[];
assert.equal(ret.length, 1, 'getRange2-+');
[3].forEach(v => { assert(find(ret, r => r.val === 'val' + v)); });
});
let tt3count = prov.countRange('test', indexName, formIndex(2, 2), formIndex(4, 3), true, false)
.then(ret => {
assert.equal(ret, 1, 'countRange2-+');
});
return Promise.all([tt1, tt1count, tt2, tt2count, tt3, tt3count]).then(() => {
return prov.close();
});
} else {
return prov.close();
}
});
});
};
it('Simple primary key put/get/getAll', (done) => {
openProvider(provName, {
version: 1,
@ -1178,6 +1395,28 @@ describe('NoSqlProvider', function () {
}).then(() => done(), (err) => done(err));
});
it('Simple non-unique index put/get, getAll, getOnly, and getRange (includeData)', (done) => {
openProvider(provName, {
version: 1,
stores: [
{
name: 'test',
primaryKeyPath: 'id',
indexes: [
{
name: 'index',
keyPath: 'a',
unique: false,
includeDataInIndex: true
}
]
}
]
}, true).then(prov => {
return nonUniqueTester(prov, 'index', false, (obj, v) => { obj.a = v; });
}).then(() => done(), (err) => done(err));
});
describe('Transaction Semantics', () => {
it('Testing transaction expiration', (done) => {
openProvider(provName, {
@ -2572,7 +2811,8 @@ describe('NoSqlProvider', function () {
indexes: [{
name: 'i',
keyPath: 'txt',
fullText: true
fullText: true,
unique: false
}]
}
]
@ -2580,6 +2820,7 @@ describe('NoSqlProvider', function () {
return prov.put('test', [
{ id: 'a1', txt: 'the quick brown fox jumps over the lăzy dog who is a bro with brows' },
{ id: 'a2', txt: 'bob likes his dog' },
{ id: 'a8', txt: 'mark marley' },
{ id: 'a3', txt: 'tes>ter' },
{
id: 'a4',
@ -2599,7 +2840,9 @@ describe('NoSqlProvider', function () {
// Test data to make sure that we don't search for empty strings (... used to put empty string to the index)
id: 'a7',
txt: 'User1, User2, User3 ...'
}
},
{ id: 'a9', txt: 'mark dunnford' },
{ id: 'a10', txt: 'alice cooper' },
]).then(() => {
const p1 = prov.fullTextSearch('test', 'i', 'brown').then((res: any[]) => {
assert.equal(res.length, 1);
@ -2716,9 +2959,17 @@ describe('NoSqlProvider', function () {
const p31 = prov.fullTextSearch('test', 'i', '!@#$%$', FullTextTermResolution.Or).then(res => {
assert.equal(res.length, 0);
});
const p32 = prov.fullTextSearch('test', 'i', 'mark').then((res: any[]) => {
assert.equal(res.length, 2);
assert.ok(some(res, r => r.id === 'a8') && some(res, r => r.id === 'a9'));
});
const p33 = prov.fullTextSearch('test', 'i', 'mark', FullTextTermResolution.Or, 10).then((res: any[]) => {
assert.equal(res.length, 2);
assert.ok(some(res, r => r.id === 'a8') && some(res, r => r.id === 'a9'));
});
return Promise.all([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31]).then(() => {
p21, p22, p23, p24, p25, p26, p27, p28, p29, p30, p31, p32, p33]).then(() => {
return prov.close();
});
});