Skip to content

Commit

Permalink
fix(@aws-amplify/datastore): fix for queries against numeric indexes …
Browse files Browse the repository at this point in the history
…in IDB (#11019)

* indexeddb fix for queries against numeric indexes

* fix test model to not have required stringField
  • Loading branch information
svidgen authored Feb 23, 2023
1 parent 4a9a71d commit 2c5ff37
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 8 deletions.
31 changes: 28 additions & 3 deletions packages/datastore/__tests__/Predicate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2219,6 +2219,26 @@ describe('Predicates', () => {
matches: [{ name: 'tim' }, { name: 'sam' }],
mismatches: [{ name: 'al' }, { name: 'fran' }],
},
{
gql: {
and: [{ rating: { gt: 123 } }],
},
expectedRegeneration: {
and: [{ rating: { gt: 123 } }],
},
matches: [{ rating: 124 }, { rating: 125 }],
mismatches: [{ rating: 122 }, { rating: 123 }],
},
{
gql: {
and: [{ rating: { eq: 123 } }],
},
expectedRegeneration: {
and: [{ rating: { eq: 123 } }],
},
matches: [{ rating: 123 }],
mismatches: [{ rating: 122 }, { rating: 124 }],
},
];

for (const [i, testCase] of ASTTransalationTestCases.entries()) {
Expand All @@ -2227,14 +2247,14 @@ describe('Predicates', () => {
)}`, () => {
const condition = testCase.gql;
const builder = ModelPredicateCreator.createFromAST(
BlogMeta.schema,
AuthorMeta.schema,
condition
);
const predicate = ModelPredicateCreator.getPredicates(builder)!;

const regeneratedCondition = predicateToGraphQLCondition(
predicate,
BlogMeta.schema
AuthorMeta.schema
);
const regeneratedFilter = predicateToGraphQLFilter(predicate);

Expand Down Expand Up @@ -2270,11 +2290,16 @@ describe('Predicates', () => {
matches: [{ name: 'tim' }, { name: 'sam' }],
mismatches: [{ name: 'al' }, { name: 'fran' }],
},
{
predicate: p => p.rating.eq(123),
matches: [{ rating: 123 }],
mismatches: [{ rating: 122 }, { rating: 124 }],
},
];
for (const [i, testCase] of predicateTestCases.entries()) {
test(`nested predicate builder can produce storage predicate ${i}: ${testCase.predicate}`, () => {
const builder = internals(
testCase.predicate(predicateFor(BlogMeta))
testCase.predicate(predicateFor(AuthorMeta))
).toStoragePredicate();

const predicate = ModelPredicateCreator.getPredicates(builder)!;
Expand Down
54 changes: 53 additions & 1 deletion packages/datastore/__tests__/commonAdapterTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import {
MtmJoin,
DefaultPKHasOneParent,
DefaultPKHasOneChild,
ModelWithIndexes,
} from './helpers';

export { pause };
Expand Down Expand Up @@ -68,6 +69,7 @@ export function addCommonQueryTests({
let Model: PersistentModelConstructor<Model>;
let Comment: PersistentModelConstructor<Comment>;
let Post: PersistentModelConstructor<Post>;
let ModelWithIndexes: PersistentModelConstructor<ModelWithIndexes>;

/**
* Creates the given number of models, with `field1` populated to
Expand Down Expand Up @@ -101,10 +103,11 @@ export function addCommonQueryTests({
'https://0.0.0.0/does/not/exist/graphql';

const classes = initSchema(testSchema());
({ Comment, Model, Post } = classes as {
({ Comment, Model, Post, ModelWithIndexes } = classes as {
Comment: PersistentModelConstructor<Comment>;
Model: PersistentModelConstructor<Model>;
Post: PersistentModelConstructor<Post>;
ModelWithIndexes: PersistentModelConstructor<ModelWithIndexes>;
});

// start() ensures storageAdapter is set
Expand Down Expand Up @@ -243,6 +246,55 @@ export function addCommonQueryTests({
);
expect(results.length).toEqual(2);
});

it('should match eq on indexed string', async () => {
const saved = await DataStore.save(
new ModelWithIndexes({
stringField: 'expected value',
})
);

await DataStore.save(
new ModelWithIndexes({
stringField: 'decoy value',
})
);

const retrieved = await DataStore.query(ModelWithIndexes, m =>
m.stringField.eq('expected value')
);
expect(retrieved.map(m => m.stringField)).toEqual(['expected value']);
});

it('should match eq on indexed int', async () => {
for (const intField of [1, 2, 3]) {
await DataStore.save(
new ModelWithIndexes({
intField,
})
);
}

const retrieved = await DataStore.query(ModelWithIndexes, m =>
m.intField.eq(2)
);
expect(retrieved.map(m => m.intField)).toEqual([2]);
});

it('should match eq on indexed float', async () => {
for (const floatField of [1.25, 1.5, 1.75]) {
await DataStore.save(
new ModelWithIndexes({
floatField,
})
);
}

const retrieved = await DataStore.query(ModelWithIndexes, m =>
m.floatField.eq(1.5)
);
expect(retrieved.map(m => m.floatField)).toEqual([1.5]);
});
});

describe('Common `save()` cases', () => {
Expand Down
97 changes: 97 additions & 0 deletions packages/datastore/__tests__/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ export function getDataStore({
LegacyJSONComment,
CompositePKParent,
CompositePKChild,
ModelWithIndexes,
} = classes as {
ModelWithBoolean: PersistentModelConstructor<ModelWithBoolean>;
Blog: PersistentModelConstructor<Blog>;
Expand All @@ -1043,6 +1044,7 @@ export function getDataStore({
LegacyJSONComment: PersistentModelConstructor<LegacyJSONComment>;
CompositePKParent: PersistentModelConstructor<CompositePKParent>;
CompositePKChild: PersistentModelConstructor<CompositePKChild>;
ModelWithIndexes: PersistentModelConstructor<ModelWithIndexes>;
};

return {
Expand Down Expand Up @@ -1075,6 +1077,7 @@ export function getDataStore({
LegacyJSONComment,
CompositePKParent,
CompositePKChild,
ModelWithIndexes,
};
}

Expand Down Expand Up @@ -1640,6 +1643,22 @@ export declare class ModelWithBoolean {
): ModelWithBoolean;
}

export declare class ModelWithIndexes {
public readonly id: string;
public readonly stringField?: string;
public readonly intField?: number;
public readonly floatField?: number;
public readonly createdAt?: string;
public readonly updatedAt?: string;

constructor(init: ModelInit<ModelWithIndexes>);

static copyOf(
src: ModelWithIndexes,
mutator: (draft: MutableModel<ModelWithIndexes>) => void | ModelWithIndexes
): ModelWithIndexes;
}

export function testSchema(): Schema {
return {
enums: {},
Expand Down Expand Up @@ -3469,6 +3488,84 @@ export function testSchema(): Schema {
},
],
},
ModelWithIndexes: {
name: 'ModelWithIndexes',
fields: {
id: {
name: 'id',
isArray: false,
type: 'ID',
isRequired: true,
attributes: [],
},
stringField: {
name: 'stringField',
isArray: false,
type: 'String',
isRequired: false,
attributes: [],
},
floatField: {
name: 'floatField',
isArray: false,
type: 'Float',
isRequired: false,
attributes: [],
},
intField: {
name: 'intField',
isArray: false,
type: 'Int',
isRequired: false,
attributes: [],
},
createdAt: {
name: 'createdAt',
isArray: false,
type: 'AWSDateTime',
isRequired: false,
attributes: [],
isReadOnly: true,
},
updatedAt: {
name: 'updatedAt',
isArray: false,
type: 'AWSDateTime',
isRequired: false,
attributes: [],
isReadOnly: true,
},
},
syncable: true,
pluralName: 'ModelWithIndexess',
attributes: [
{
type: 'model',
properties: {},
},
{
type: 'key',
properties: {
name: 'byStringField',
fields: ['stringField'],
},
},
{
type: 'key',
properties: {
name: 'byIntField',
fields: ['intField'],
},
},
{
type: 'key',
properties: {
name: 'byFloatField',
fields: ['floatField'],
},
},
],
},
},
nonModels: {
Metadata: {
Expand Down
8 changes: 4 additions & 4 deletions packages/datastore/src/storage/adapter/IndexedDBAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,12 +550,12 @@ class IndexedDBAdapter implements Adapter {
for (const name of store.indexNames) {
const idx = store.index(name);
const keypath = Array.isArray(idx.keyPath) ? idx.keyPath : [idx.keyPath];
const matchingPredicateValues: string[] = [];
const matchingPredicateValues: (string | number)[] = [];

for (const field of keypath) {
const p = predicateIndex.get(field);
if (p) {
matchingPredicateValues.push(String(p.operand));
if (p && p.operand !== null && p.operand !== undefined) {
matchingPredicateValues.push(p.operand);
} else {
break;
}
Expand Down Expand Up @@ -1238,7 +1238,7 @@ class IndexedDBAdapter implements Adapter {
* @returns An array or string, depending on and given key,
* that is ensured to be compatible with the IndexedDB implementation's nuances.
*/
private canonicalKeyPath = (keyArr: string[]) => {
private canonicalKeyPath = (keyArr: (string | number)[]) => {
if (this.safariCompatabilityMode) {
return keyArr.length > 1 ? keyArr : keyArr[0];
}
Expand Down

0 comments on commit 2c5ff37

Please sign in to comment.