Skip to content

Commit

Permalink
feat: excludeFromIndex allow for '*' wildcard to catch all properties…
Browse files Browse the repository at this point in the history
… on object (#451)
  • Loading branch information
praveenqlogic01 authored and JustinBeckwith committed Jul 26, 2019
1 parent 1850f25 commit 215cbee
Show file tree
Hide file tree
Showing 3 changed files with 265 additions and 5 deletions.
49 changes: 45 additions & 4 deletions src/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,9 +552,11 @@ export namespace entity {
function excludePathFromEntity(entity: EntityProto, path: string) {
const arrayIndex = path.indexOf('[]');
const entityIndex = path.indexOf('.');
const wildcardIndex = path.indexOf('.*');

const hasArrayPath = arrayIndex > -1;
const hasEntityPath = entityIndex > -1;
const hasWildCard = wildcardIndex > -1;

if (!hasArrayPath && !hasEntityPath) {
// This is the path end node. Traversal ends here in either case.
Expand Down Expand Up @@ -589,15 +591,20 @@ export namespace entity {
const firstPathPart = splitPath.shift()!;
const remainderPath = splitPath.join(delimiter).replace(/^(\.|\[\])/, '');

if (!(entity.properties && entity.properties[firstPathPart])) {
if (
!(entity.properties && entity.properties[firstPathPart]) &&
!hasWildCard
) {
// Either a primitive or an entity for which this path doesn't apply.
return;
}

if (
firstPathPartIsArray &&
// check also if the property in question is actually an array value.
entity.properties[firstPathPart].arrayValue
entity.properties[firstPathPart].arrayValue &&
// check if wildcard is not applied
!hasWildCard
) {
const array = entity.properties[firstPathPart].arrayValue;
// tslint:disable-next-line no-any
Expand All @@ -619,9 +626,43 @@ export namespace entity {
);
}
});
} else if (firstPathPartIsArray && hasWildCard && remainderPath === '*') {
const array = entity.properties[firstPathPart].arrayValue;
// tslint:disable-next-line no-any
array.values.forEach((value: any) => {
if (value.entityValue) {
excludePathFromEntity(value.entityValue, '.*');
} else {
excludePathFromEntity(value, '');
}
});
} else if (firstPathPartIsEntity) {
const parentEntity = entity.properties[firstPathPart].entityValue;
excludePathFromEntity(parentEntity, remainderPath);
if (firstPathPart === '') {
Object.keys(entity.properties).forEach(path => {
const newPath = entity.properties[path].arrayValue
? path + '[].*'
: path + '.*';
excludePathFromEntity(entity, newPath);
});
} else {
if (hasWildCard && remainderPath === '*') {
const parentEntity = entity.properties[firstPathPart].entityValue;

if (parentEntity) {
Object.keys(parentEntity.properties).forEach(path => {
const newPath = parentEntity.properties[path].arrayValue
? path + '[].*'
: path + '.*';
excludePathFromEntity(parentEntity, newPath);
});
} else {
excludePathFromEntity(entity, firstPathPart);
}
} else {
const parentEntity = entity.properties[firstPathPart].entityValue;
excludePathFromEntity(parentEntity, remainderPath);
}
}
}
}
}
Expand Down
63 changes: 63 additions & 0 deletions system-test/datastore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,69 @@ describe('Datastore', () => {
await datastore.delete(postKey);
});

it('should remove index with using wildcard in excludeFromIndexes', async () => {
const longString = Buffer.alloc(1501, '.').toString();
const postKey = datastore.key(['Post', 'post3']);
const data = {
longString,
notMetadata: true,
longStringArray: [longString],
metadata: {
longString,
otherProperty: 'value',
obj: {
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
};

const excludeFromIndexes = [
'longString',
'longStringArray[]',
'metadata.longString',
'metadata.obj.*',
'metadata.longStringArray[].*',
];

await datastore.save({
key: postKey,
data,
excludeFromIndexes,
});
const [entity] = await datastore.get(postKey);
assert.deepStrictEqual(entity[datastore.KEY], postKey);
delete entity[datastore.KEY];
assert.deepStrictEqual(entity, data);
await datastore.delete(postKey);
});

it('should save/get/delete with a key name', async () => {
const postKey = datastore.key(['Post', 'post1']);
await datastore.save({key: postKey, data: post});
Expand Down
158 changes: 157 additions & 1 deletion test/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {Transform} from 'stream';

import {google} from '../proto/datastore';
import * as ds from '../src';
import {entity, Entity, KeyProto} from '../src/entity.js';
import {entity, Entity, KeyProto, EntityProto} from '../src/entity.js';
import {Query, QueryProto} from '../src/query.js';
import {
AllocateIdsRequestResponse,
Expand Down Expand Up @@ -1390,6 +1390,162 @@ describe('Request', () => {
);
});

it('should allow exclude property indexed with "*" wildcard from root', done => {
const longString = Buffer.alloc(1501, '.').toString();
const data = {
longString,
notMetadata: true,
longStringArray: [longString],
metadata: {
longString,
otherProperty: 'value',
obj: {
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
};

const validateIndex = (data: Any) => {
if (data.arrayValue) {
data.arrayValue.values.forEach((value: Any) => {
validateIndex(value);
});
} else if (data.entityValue) {
Object.keys(data.entityValue.properties).forEach(path => {
validateIndex(data.entityValue.properties[path]);
});
} else {
assert.strictEqual(data.excludeFromIndexes, true);
}
};

request.request_ = (config: RequestConfig) => {
const properties = config.reqOpts.mutations[0].upsert.properties;
Object.keys(properties).forEach(path => {
validateIndex(properties[path]);
});
done();
};

request.save(
{
key,
data,
excludeFromIndexes: ['.*'],
},
assert.ifError
);
});

it('should allow exclude property indexed with "*" wildcard for object and array', done => {
const longString = Buffer.alloc(1501, '.').toString();
const data = {
longString,
notMetadata: true,
longStringArray: [longString],
metadata: {
longString,
otherProperty: 'value',
obj: {
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
longStringArray: [
{
longString,
nestedLongStringArray: [
{
longString,
nestedProperty: true,
},
{
longString,
},
],
},
],
},
};

const validateIndex = (data: Any) => {
if (data.arrayValue) {
data.arrayValue.values.forEach((value: Any) => {
validateIndex(value);
});
} else if (data.entityValue) {
Object.keys(data.entityValue.properties).forEach(path => {
validateIndex(data.entityValue.properties[path]);
});
} else {
assert.strictEqual(data.excludeFromIndexes, true);
}
};

request.request_ = (config: RequestConfig) => {
const properties = config.reqOpts.mutations[0].upsert.properties;
Object.keys(properties).forEach(path => {
validateIndex(properties[path]);
});
done();
};

request.save(
{
key,
data,
excludeFromIndexes: [
'longString',
'notMetadata',
'longStringArray[]',
'metadata.longString',
'metadata.otherProperty',
'metadata.obj.*',
'metadata.longStringArray[].*',
],
},
assert.ifError
);
});

it('should assign ID on keys without them', done => {
const incompleteKey = new entity.Key({path: ['Incomplete']});
const incompleteKey2 = new entity.Key({path: ['Incomplete']});
Expand Down

0 comments on commit 215cbee

Please sign in to comment.