-
Notifications
You must be signed in to change notification settings - Fork 101
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: introduce EntityFilter class with support for and/or filters (#…
…1061) * or filters interface * implementation of new filter * Remove duplicate code * Parse with type guard * Remove the newFilter variable * Add filter and enable chaining * test add filter * Add another unit test * Add system-test stubs * Add system-tests for the OR filter * Move things around * Added a unit test * Add a unit test for OR filters * Just use filter method * new warning test * Revert "new warning test" This reverts commit 37400a6. * Now removes deprecation warning properly * Add a test for new warning * Add setAncestor Adds a new setAncestor method for ensuring only one ancestor is set for the query at a time. This will avoid errors that result because of setting multiple ancestors. Also deprecate hasAncestor because it will lead to warnings like this. Add parser logic to use the value provided in setAncestor for query sent to backend. * Basic unit tests for setAncestor Added tests for the query proto, one to make sure the query structure is right when setting ancestor once and one to make sure the query structure is right when setting ancestor twice. Also added a unit test to make sure that ancestor is set the right way internally when using setAncestor. * change expected result for OR query This code change adjusts the expected result for running an OR query. Old result used to correspond with AND, but now corresponds to OR. * Fix a test by not requiring the done callback A test is timing out because we are waiting for done to be called. This fix does not require done to be called. * Revert "Fix a test by not requiring the done callback" This reverts commit 1159b37. * Revert "Basic unit tests for setAncestor" This reverts commit 86841d6. * Revert "Add setAncestor" This reverts commit e84582f. * Separate filters and new filters internally This commit is done to avoid a breaking typescript change which could have the potential to affect some users who read `filters` on a query as its type had been changed to be more flexible, but is now back to what it was. * Move AND/OR into their own separate function AND and OR should not be static functions of the filter class because then the user has to type Filter.AND instead of AND for example. * Eliminate unused imports Artifacts of having imports laying around and moving functionality between files * Revert "Add a test for new warning" This reverts commit bc15f32. * Revert "Now removes deprecation warning properly" This reverts commit db02a50. * Modify test cases to capture nuances in data We add additional asserts to the data in order to capture the nuances of the composite operator. For example, for the OR test we make sure the filter doesn’t always require both conditions to be true. * Added comments to code that was refactored Code for building the `filter` property of the query proto was pulled into the `Filter` object. Comments indicate how legacy functionality was maintained and which lines of code perform which task. * rename NewFilter to entity filter rename new filter to entity filter to eliminate the need for an internal rename that causes confusion * Switch around last and first These test cases have mistakes in their names. We should change them to reflect the position of `hasAncestor`. * Change the comment to reflect the new name The name of the base class is now EntityFilter. Adjust this comment so that it correctly matches the parameter type. * rename and move constant map up rename composite filter functions so that they don’t look like constants and move a map up so that it doesn’t have to be initialized every time. * Rename newFilters to entityFilters
- Loading branch information
1 parent
d854b57
commit 8fc58c0
Showing
7 changed files
with
398 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"); | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
import {Operator, Filter as IFilter} from './query'; | ||
import {entity} from './entity'; | ||
|
||
const OP_TO_OPERATOR = new Map([ | ||
['=', 'EQUAL'], | ||
['>', 'GREATER_THAN'], | ||
['>=', 'GREATER_THAN_OR_EQUAL'], | ||
['<', 'LESS_THAN'], | ||
['<=', 'LESS_THAN_OR_EQUAL'], | ||
['HAS_ANCESTOR', 'HAS_ANCESTOR'], | ||
['!=', 'NOT_EQUAL'], | ||
['IN', 'IN'], | ||
['NOT_IN', 'NOT_IN'], | ||
]); | ||
|
||
enum CompositeOperator { | ||
AND = 'AND', | ||
OR = 'OR', | ||
} | ||
|
||
export function and(filters: EntityFilter[]): CompositeFilter { | ||
return new CompositeFilter(filters, CompositeOperator.AND); | ||
} | ||
|
||
export function or(filters: EntityFilter[]): CompositeFilter { | ||
return new CompositeFilter(filters, CompositeOperator.OR); | ||
} | ||
|
||
/** | ||
* A Filter is a class that contains data for a filter that can be translated | ||
* into a proto when needed. | ||
* | ||
* @see {@link https://cloud.google.com/datastore/docs/concepts/queries#filters| Filters Reference} | ||
* | ||
*/ | ||
export abstract class EntityFilter { | ||
/** | ||
* Gets the proto for the filter. | ||
* | ||
*/ | ||
// eslint-disable-next-line | ||
abstract toProto(): any; | ||
} | ||
|
||
/** | ||
* A PropertyFilter is a filter that gets applied to a query directly. | ||
* | ||
* @see {@link https://cloud.google.com/datastore/docs/concepts/queries#property_filters| Property filters Reference} | ||
* | ||
* @class | ||
*/ | ||
export class PropertyFilter extends EntityFilter implements IFilter { | ||
name: string; | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
val: any; | ||
op: Operator; | ||
|
||
/** | ||
* Build a Property Filter object. | ||
* | ||
* @param {string} Property | ||
* @param {Operator} operator | ||
* @param {any} val | ||
*/ | ||
constructor(property: string, operator: Operator, val: any) { | ||
super(); | ||
this.name = property; | ||
this.op = operator; | ||
this.val = val; | ||
} | ||
|
||
private encodedValue(): any { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
let value: any = {}; | ||
if (this.name === '__key__') { | ||
value.keyValue = entity.keyToKeyProto(this.val); | ||
} else { | ||
value = entity.encodeValue(this.val, this.name); | ||
} | ||
return value; | ||
} | ||
|
||
/** | ||
* Gets the proto for the filter. | ||
* | ||
*/ | ||
// eslint-disable-next-line | ||
toProto(): any { | ||
const value = new PropertyFilter( | ||
this.name, | ||
this.op, | ||
this.val | ||
).encodedValue(); | ||
return { | ||
propertyFilter: { | ||
property: { | ||
name: this.name, | ||
}, | ||
op: OP_TO_OPERATOR.get(this.op), | ||
value, | ||
}, | ||
}; | ||
} | ||
} | ||
|
||
/** | ||
* A CompositeFilter is a filter that combines other filters and applies that | ||
* combination to a query. | ||
* | ||
* @see {@link https://cloud.google.com/datastore/docs/concepts/queries#composite_filters| Composite filters Reference} | ||
* | ||
* @class | ||
*/ | ||
class CompositeFilter extends EntityFilter { | ||
filters: EntityFilter[]; | ||
op: string; | ||
|
||
/** | ||
* Build a Composite Filter object. | ||
* | ||
* @param {EntityFilter[]} filters | ||
*/ | ||
constructor(filters: EntityFilter[], op: CompositeOperator) { | ||
super(); | ||
this.filters = filters; | ||
this.op = op; | ||
} | ||
|
||
/** | ||
* Gets the proto for the filter. | ||
* | ||
*/ | ||
// eslint-disable-next-line | ||
toProto(): any { | ||
return { | ||
compositeFilter: { | ||
filters: this.filters.map(filter => filter.toProto()), | ||
op: this.op, | ||
}, | ||
}; | ||
} | ||
} | ||
|
||
export function isFilter(filter: any): filter is EntityFilter { | ||
return (filter as EntityFilter).toProto !== undefined; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.