-
Notifications
You must be signed in to change notification settings - Fork 652
feat: Firestore Search #7824
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: Firestore Search #7824
Changes from all commits
ddd840c
61e6119
265178a
56ed760
e22c5fe
f53e050
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -23,11 +23,14 @@ import { | |||||
| fieldOrExpression, | ||||||
| isFirestoreValue, | ||||||
| isString, | ||||||
| toField, | ||||||
| valueToDefaultExpr, | ||||||
| vectorToExpr, | ||||||
| } from './pipeline-util'; | ||||||
| import {HasUserData, Serializer, validateUserInput} from '../serializer'; | ||||||
| import {cast} from '../util'; | ||||||
| import {GeoPoint} from '../geo-point'; | ||||||
| import {OptionsUtil} from './options-util'; | ||||||
|
|
||||||
| /** | ||||||
| * @beta | ||||||
|
|
@@ -3100,6 +3103,85 @@ export abstract class Expression | |||||
| ]).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the result of this `expression` is between | ||||||
| * the `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ```typescript | ||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * field('tireWidth').between(constant(2.2), constant(2.4)) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(field('tireWidth').greaterThanOrEqual(contant(2.2)), field('tireWidth').lessThanOrEqual(constant(2.4))) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param lowerBound - An `Expression` that evaluates to the lower bound (inclusive) of the range. | ||||||
| * @param upperBound - An `Expression` that evaluates to the upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| between(lowerBound: Expression, upperBound: Expression): BooleanExpression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the result of this `expression` is between | ||||||
| * the `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ``` | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Applies to all other docs like this
Suggested change
|
||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * field('tireWidth').between(2.2, 2.4) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(field('tireWidth').greaterThanOrEqual(2.2), field('tireWidth').lessThanOrEqual(2.4)) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param lowerBound - Lower bound (inclusive) of the range. | ||||||
| * @param upperBound - Upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| between(lowerBound: unknown, upperBound: unknown): BooleanExpression; | ||||||
|
|
||||||
| between(lowerBound: unknown, upperBound: unknown): BooleanExpression { | ||||||
|
Comment on lines
+3140
to
+3142
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we make these |
||||||
| return new FunctionExpression('between', [ | ||||||
| this, | ||||||
| valueToDefaultExpr(lowerBound), | ||||||
| valueToDefaultExpr(upperBound), | ||||||
| ]).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to an HTML-formatted text snippet that renders terms matching | ||||||
| * the search query in `<b>bold</b>`. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param rquery Define the search query using the search DTS (TODO(search) link). | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this supposed to say 'search DSL'? |
||||||
| */ | ||||||
| snippet(rquery: string): Expression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to an HTML-formatted text snippet that renders terms matching | ||||||
| * the search query in `<b>bold</b>`. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param options Define how snippeting behaves. | ||||||
| */ | ||||||
| snippet(options: firestore.Pipelines.SnippetOptions): Expression; | ||||||
|
|
||||||
| snippet( | ||||||
| queryOrOptions: string | firestore.Pipelines.SnippetOptions, | ||||||
| ): Expression { | ||||||
| const options: firestore.Pipelines.SnippetOptions = isString(queryOrOptions) | ||||||
| ? {rquery: queryOrOptions} | ||||||
| : queryOrOptions; | ||||||
| const rquery = options.rquery; | ||||||
| const internalOptions = { | ||||||
| maxSnippetWidth: options.maxSnippetWidth, | ||||||
| maxSnippets: options.maxSnippets, | ||||||
| separator: options.separator, | ||||||
| }; | ||||||
| return new SnippetExpression([this, constant(rquery)], internalOptions); | ||||||
| } | ||||||
|
|
||||||
| // TODO(new-expression): Add new expression method definitions above this line | ||||||
|
|
||||||
| /** | ||||||
|
|
@@ -3351,6 +3433,35 @@ export class Field | |||||
| readonly expressionType: firestore.Pipelines.ExpressionType = 'Field'; | ||||||
| selectable = true as const; | ||||||
|
|
||||||
| /** | ||||||
| * Perform a full-text search on this field. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param rquery Define the search query using the rquery DTS. | ||||||
| */ | ||||||
| matches(rquery: string | Expression): BooleanExpression { | ||||||
| return new FunctionExpression('matches', [ | ||||||
| this, | ||||||
| valueToDefaultExpr(rquery), | ||||||
| ]).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to the distance in meters between the location specified | ||||||
| * by this field and the query location. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param location - Compute distance to this GeoPoint. | ||||||
| */ | ||||||
| geoDistance(location: GeoPoint | Expression): Expression { | ||||||
| return new FunctionExpression('geo_distance', [ | ||||||
| this, | ||||||
| valueToDefaultExpr(location), | ||||||
| ]).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @beta | ||||||
| * @internal | ||||||
|
|
@@ -3686,6 +3797,51 @@ export class FunctionExpression extends Expression { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * SnippetExpression extends from FunctionExpression because it | ||||||
| * supports options and requires the options util. | ||||||
| */ | ||||||
| export class SnippetExpression extends FunctionExpression { | ||||||
| /** | ||||||
| * @private | ||||||
| * @internal | ||||||
| */ | ||||||
| get _optionsUtil(): OptionsUtil { | ||||||
| return new OptionsUtil({ | ||||||
| maxSnippetWidth: { | ||||||
| serverName: 'max_snippet_width', | ||||||
| }, | ||||||
| maxSnippets: { | ||||||
| serverName: 'max_snippets', | ||||||
| }, | ||||||
| separator: { | ||||||
| serverName: 'separator', | ||||||
| }, | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @hideconstructor | ||||||
| */ | ||||||
| constructor( | ||||||
| params: Expression[], | ||||||
| private _options?: {}, | ||||||
| ) { | ||||||
| super('snippet', params); | ||||||
| } | ||||||
|
|
||||||
| _toProto(serializer: Serializer): api.IValue { | ||||||
| const proto = super._toProto(serializer); | ||||||
| proto.functionValue!.options = this._optionsUtil.getOptionsProto( | ||||||
| serializer, | ||||||
| this._options ?? {}, | ||||||
| {}, | ||||||
| ); | ||||||
|
|
||||||
| return proto; | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * @beta | ||||||
| * This class defines the base class for Firestore `Pipeline` functions, which can be evaluated within pipeline | ||||||
|
|
@@ -10258,6 +10414,201 @@ export function isType( | |||||
| return fieldOrExpression(fieldNameOrExpression).isType(type); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Perform a full-text search on the specified field. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param searchField Search the specified field. | ||||||
| * @param rquery Define the search query using the search DTS. | ||||||
| */ | ||||||
| export function matches( | ||||||
| searchField: string | Field, | ||||||
| rquery: string | Expression, | ||||||
| ): BooleanExpression { | ||||||
| return toField(searchField).matches(rquery); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Perform a full-text search on the document. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param rquery Define the search query using the rquery DTS. | ||||||
| */ | ||||||
| export function documentMatches( | ||||||
| rquery: string | Expression, | ||||||
| ): BooleanExpression { | ||||||
| return new FunctionExpression('document_matches', [ | ||||||
| valueToDefaultExpr(rquery), | ||||||
| ]).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to the search score that reflects the topicality of the document | ||||||
| * to all of the text predicates (`queryMatch`) | ||||||
| * in the search query. If `SearchOptions.query` is not set or does not contain | ||||||
| * any text predicates, then this topicality score will always be `0`. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| */ | ||||||
| export function score(): Expression { | ||||||
| return new FunctionExpression('score', []).asBoolean(); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to an HTML-formatted text snippet that highlights terms matching | ||||||
| * the search query in `<b>bold</b>`. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param searchField Search the specified field for matching terms. | ||||||
| * @param rquery Define the search query using the search DTS (TODO(search) link). | ||||||
| */ | ||||||
| export function snippet( | ||||||
| searchField: string | Field, | ||||||
| rquery: string, | ||||||
| ): Expression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to an HTML-formatted text snippet that highlights terms matching | ||||||
| * the search query in `<b>bold</b>`. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param searchField Search the specified field for matching terms. | ||||||
| * @param options Define the search query using the search DTS (TODO(search) link). | ||||||
| */ | ||||||
| export function snippet( | ||||||
| searchField: string | Field, | ||||||
| options: firestore.Pipelines.SnippetOptions, | ||||||
| ): Expression; | ||||||
| export function snippet( | ||||||
| field: string | Field, | ||||||
| queryOrOptions: string | firestore.Pipelines.SnippetOptions, | ||||||
| ): Expression { | ||||||
| return toField(field).snippet( | ||||||
| isString(queryOrOptions) ? {rquery: queryOrOptions} : queryOrOptions, | ||||||
| ); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates to the distance in meters between the location in the specified | ||||||
| * field and the query location. | ||||||
| * | ||||||
| * @remarks This Expression can only be used within a `Search` stage. | ||||||
| * | ||||||
| * @param fieldName - Specifies the field in the document which contains | ||||||
| * the first GeoPoint for distance computation. | ||||||
| * @param location - Compute distance to this GeoPoint. | ||||||
| */ | ||||||
| export function geoDistance( | ||||||
| fieldName: string | Field, | ||||||
| location: GeoPoint | Expression, | ||||||
| ): Expression { | ||||||
| return toField(fieldName).geoDistance(location); | ||||||
| } | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the value in the field specified by `fieldName` is between | ||||||
| * the evaluated values for `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ``` | ||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * between('tireWidth', constant(2.2), constant(2.4)) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(greaterThanOrEqual('tireWidth', constant(2.2)), lessThanOrEqual('tireWidth', constant(2.4))) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param fieldName - Evaluate if the value stored in this field is between the lower and upper bounds. | ||||||
| * @param lowerBound - Lower bound (inclusive) of the range. | ||||||
| * @param upperBound - Upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| export function between( | ||||||
| fieldName: string, | ||||||
| lowerBound: Expression, | ||||||
| upperBound: Expression, | ||||||
| ): BooleanExpression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the value in the field specified by `fieldName` is between | ||||||
| * the values for `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ``` | ||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * between('tireWidth', 2.2, 2.4) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(greaterThanOrEqual('tireWidth', 2.2), lessThanOrEqual('tireWidth', 2.4)) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param fieldName - Evaluate if the value stored in this field is between the lower and upper bounds. | ||||||
| * @param lowerBound - Lower bound (inclusive) of the range. | ||||||
| * @param upperBound - Upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| export function between( | ||||||
| fieldName: string, | ||||||
| lowerBound: unknown, | ||||||
| upperBound: unknown, | ||||||
| ): BooleanExpression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the result of the specified `expression` is between | ||||||
| * the results of `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ``` | ||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * between(field('tireWidth'), constant(2.2), constant(2.4)) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(greaterThanOrEqual(field('tireWidth'), constant(2.2)), lessThanOrEqual(field('tireWidth'), constant(2.4))) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param expression - Evaluate if the result of this expression is between the lower and upper bounds. | ||||||
| * @param lowerBound - Lower bound (inclusive) of the range. | ||||||
| * @param upperBound - Upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| export function between( | ||||||
| expression: Expression, | ||||||
| lowerBound: Expression, | ||||||
| upperBound: Expression, | ||||||
| ): BooleanExpression; | ||||||
|
|
||||||
| /** | ||||||
| * Evaluates if the result of the specified `expression` is between | ||||||
| * the `lowerBound` (inclusive) and `upperBound` (inclusive). | ||||||
| * | ||||||
| * @example | ||||||
| * ``` | ||||||
| * // Evaluate if the 'tireWidth' is between 2.2 and 2.4 | ||||||
| * between(field('tireWidth'), 2.2, 2.4) | ||||||
| * | ||||||
| * // This is functionally equivalent to | ||||||
| * and(greaterThanOrEqual(field('tireWidth'), 2.2), lessThanOrEqual(field('tireWidth'), 2.4)) | ||||||
| * ``` | ||||||
| * | ||||||
| * @param expression - Evaluate if the result of this expression is between the lower and upper bounds. | ||||||
| * @param lowerBound - Lower bound (inclusive) of the range. | ||||||
| * @param upperBound - Upper bound (inclusive) of the range. | ||||||
| */ | ||||||
| export function between( | ||||||
| expression: Expression, | ||||||
| lowerBound: unknown, | ||||||
| upperBound: unknown, | ||||||
| ): BooleanExpression; | ||||||
|
|
||||||
| export function between( | ||||||
| expression: Expression | string, | ||||||
| lowerBound: unknown, | ||||||
| upperBound: unknown, | ||||||
| ): BooleanExpression { | ||||||
| return fieldOrExpression(expression).between(lowerBound, upperBound); | ||||||
| } | ||||||
|
|
||||||
| // TODO(new-expression): Add new top-level expression function definitions above this line | ||||||
|
|
||||||
| /** | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add
@returnstags and usage examples to expression docs where those are missing