Skip to content

Commit 656e848

Browse files
committed
Ported all tests, plus some bug fixes
1 parent da4dee3 commit 656e848

File tree

6 files changed

+9242
-3294
lines changed

6 files changed

+9242
-3294
lines changed

packages/firestore/src/core/expressions.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ export function toEvaluable<T>(expr: T): EvaluableExpr {
172172
return new CoreArrayElement(expr);
173173
} else if (expr instanceof EqAny) {
174174
return new CoreEqAny(expr);
175+
} else if (expr instanceof NotEqAny) {
176+
return new CoreNotEqAny(expr);
175177
} else if (expr instanceof IsNan) {
176178
return new CoreIsNan(expr);
177179
} else if (expr instanceof Exists) {
@@ -398,7 +400,11 @@ abstract class BigIntOrDoubleArithmetics<
398400
}
399401

400402
function valueEquals(left: Value, right: Value): boolean {
401-
return valueEqualsWithOptions(left, right, { nanEqual: false, mixIntegerDouble: true, semanticsEqual: true });
403+
return valueEqualsWithOptions(left, right, {
404+
nanEqual: false,
405+
mixIntegerDouble: true,
406+
semanticsEqual: true
407+
});
402408
}
403409

404410
export class CoreAdd extends BigIntOrDoubleArithmetics<Add> {
@@ -768,6 +774,29 @@ export class CoreEqAny implements EvaluableExpr {
768774
}
769775
}
770776

777+
export class CoreNotEqAny implements EvaluableExpr {
778+
constructor(private expr: NotEqAny) {}
779+
780+
evaluate(
781+
context: EvaluationContext,
782+
input: PipelineInputOutput
783+
): Value | undefined {
784+
const inverse = new CoreEqAny(new EqAny(this.expr.left, this.expr.others));
785+
const result = inverse.evaluate(context, input);
786+
if (result === undefined) {
787+
return undefined;
788+
}
789+
return { booleanValue: !result.booleanValue };
790+
}
791+
792+
static fromProtoToApiObj(value: ProtoFunction): EqAny {
793+
return new EqAny(
794+
exprFromProto(value.args![0]),
795+
value.args!.slice(1).map(exprFromProto)
796+
);
797+
}
798+
}
799+
771800
export class CoreIsNan implements EvaluableExpr {
772801
constructor(private expr: IsNan) {}
773802

packages/firestore/src/core/pipeline_run.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
DocumentsSource,
2020
Exists,
2121
Field,
22+
FirestoreError,
2223
Limit,
2324
Offset,
2425
Ordering,
@@ -39,6 +40,7 @@ import { Query, queryMatches, queryMatchesAllDocuments } from './query';
3940
import { isPipeline, QueryOrPipeline } from './pipeline-util';
4041
import { DOCUMENT_KEY_NAME } from '../model/path';
4142
import { JsonProtoSerializer } from '../remote/serializer';
43+
import { Code } from '../util/error';
4244

4345
export class CorePipeline {
4446
constructor(
@@ -233,6 +235,22 @@ function evaluateDocuments(
233235
stage: DocumentsSource,
234236
input: Array<PipelineInputOutput>
235237
): Array<PipelineInputOutput> {
238+
if (stage.docPaths.length === 0) {
239+
throw new FirestoreError(
240+
Code.INVALID_ARGUMENT,
241+
'Empty document paths are not allowed in DocumentsSource'
242+
);
243+
}
244+
if (stage.docPaths) {
245+
const uniqueDocPaths = new Set(stage.docPaths);
246+
if (uniqueDocPaths.size !== stage.docPaths.length) {
247+
throw new FirestoreError(
248+
Code.INVALID_ARGUMENT,
249+
'Duplicate document paths are not allowed in DocumentsSource'
250+
);
251+
}
252+
}
253+
236254
return input.filter(input => {
237255
return (
238256
input.isFoundDocument() &&

packages/firestore/src/lite-api/expressions.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,7 +2056,10 @@ export class Constant extends Expr {
20562056

20572057
private _protoValue?: ProtoValue;
20582058

2059-
private constructor(readonly value: any, readonly options?: {preferIntegers: boolean}) {
2059+
private constructor(
2060+
readonly value: any,
2061+
readonly options?: { preferIntegers: boolean }
2062+
) {
20602063
super();
20612064
}
20622065

@@ -2068,7 +2071,7 @@ export class Constant extends Expr {
20682071
*/
20692072
static of(value: number): Constant;
20702073

2071-
static of(value: number, options?: {preferIntegers: boolean}): Constant;
2074+
static of(value: number, options?: { preferIntegers: boolean }): Constant;
20722075

20732076
/**
20742077
* Creates a `Constant` instance for a string value.
@@ -2176,7 +2179,7 @@ export class Constant extends Expr {
21762179
*/
21772180
static of(value: VectorValue): Constant;
21782181

2179-
static of(value: any, options?: {preferIntegers: boolean}): Constant {
2182+
static of(value: any, options?: { preferIntegers: boolean }): Constant {
21802183
return new Constant(value, options);
21812184
}
21822185

@@ -2555,7 +2558,7 @@ export class EqAny extends FirestoreFunction implements FilterCondition {
25552558
* @beta
25562559
*/
25572560
export class NotEqAny extends FirestoreFunction implements FilterCondition {
2558-
constructor(private left: Expr, private others: Expr[]) {
2561+
constructor(readonly left: Expr, readonly others: Expr[]) {
25592562
super('not_eq_any', [left, new ListOfExprs(others)]);
25602563
}
25612564
filterable = true as const;

packages/firestore/src/model/values.ts

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ export function typeOrder(value: Value): TypeOrder {
102102
}
103103

104104
export interface EqualOptions {
105-
nanEqual: boolean,
106-
mixIntegerDouble: boolean,
107-
semanticsEqual: boolean
105+
nanEqual: boolean;
106+
mixIntegerDouble: boolean;
107+
semanticsEqual: boolean;
108108
}
109109

110110
/** Tests `left` and `right` for equality based on the backend semantics. */
@@ -202,23 +202,25 @@ export function numberEquals(
202202
);
203203
}
204204

205-
let n1:number, n2:number;
206-
if ('doubleValue' in left && 'doubleValue' in right) {
207-
n1 = normalizeNumber(left.doubleValue!);
208-
n2 = normalizeNumber(right.doubleValue!);
209-
} else if(options?.mixIntegerDouble) {
210-
n1 = normalizeNumber(left.integerValue ?? left.doubleValue);
211-
n2 = normalizeNumber(right.integerValue ?? right.doubleValue);
212-
} else {
213-
return false;
214-
}
205+
let n1: number, n2: number;
206+
if ('doubleValue' in left && 'doubleValue' in right) {
207+
n1 = normalizeNumber(left.doubleValue!);
208+
n2 = normalizeNumber(right.doubleValue!);
209+
} else if (options?.mixIntegerDouble) {
210+
n1 = normalizeNumber(left.integerValue ?? left.doubleValue);
211+
n2 = normalizeNumber(right.integerValue ?? right.doubleValue);
212+
} else {
213+
return false;
214+
}
215215

216-
if (n1 === n2) {
217-
return options?.semanticsEqual ? true : isNegativeZero(n1) === isNegativeZero(n2);
218-
} else {
219-
const nanEqual = options === undefined ? true : options.nanEqual;
220-
return nanEqual ? isNaN(n1) && isNaN(n2) : false;
221-
}
216+
if (n1 === n2) {
217+
return options?.semanticsEqual
218+
? true
219+
: isNegativeZero(n1) === isNegativeZero(n2);
220+
} else {
221+
const nanEqual = options === undefined ? true : options.nanEqual;
222+
return nanEqual ? isNaN(n1) && isNaN(n2) : false;
223+
}
222224
}
223225

224226
function objectEquals(

0 commit comments

Comments
 (0)