Skip to content

Commit f23a897

Browse files
Merge 960d158 into c15333a
2 parents c15333a + 960d158 commit f23a897

File tree

18 files changed

+806
-741
lines changed

18 files changed

+806
-741
lines changed

packages/firestore/src/core/bound.ts

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
/**
2+
* @license
3+
* Copyright 2022 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { Document } from '../model/document';
19+
import { DocumentKey } from '../model/document_key';
20+
import { isReferenceValue, valueCompare, valueEquals } from '../model/values';
21+
import { Value as ProtoValue } from '../protos/firestore_proto_api';
22+
import { debugAssert } from '../util/assert';
23+
24+
import { Direction, OrderBy } from './order_by';
25+
26+
/**
27+
* Represents a bound of a query.
28+
*
29+
* The bound is specified with the given components representing a position and
30+
* whether it's just before or just after the position (relative to whatever the
31+
* query order is).
32+
*
33+
* The position represents a logical index position for a query. It's a prefix
34+
* of values for the (potentially implicit) order by clauses of a query.
35+
*
36+
* Bound provides a function to determine whether a document comes before or
37+
* after a bound. This is influenced by whether the position is just before or
38+
* just after the provided values.
39+
*/
40+
export class Bound {
41+
constructor(readonly position: ProtoValue[], readonly inclusive: boolean) {}
42+
}
43+
44+
function boundCompareToDocument(
45+
bound: Bound,
46+
orderBy: OrderBy[],
47+
doc: Document
48+
): number {
49+
debugAssert(
50+
bound.position.length <= orderBy.length,
51+
"Bound has more components than query's orderBy"
52+
);
53+
let comparison = 0;
54+
for (let i = 0; i < bound.position.length; i++) {
55+
const orderByComponent = orderBy[i];
56+
const component = bound.position[i];
57+
if (orderByComponent.field.isKeyField()) {
58+
debugAssert(
59+
isReferenceValue(component),
60+
'Bound has a non-key value where the key path is being used.'
61+
);
62+
comparison = DocumentKey.comparator(
63+
DocumentKey.fromName(component.referenceValue),
64+
doc.key
65+
);
66+
} else {
67+
const docValue = doc.data.field(orderByComponent.field);
68+
debugAssert(
69+
docValue !== null,
70+
'Field should exist since document matched the orderBy already.'
71+
);
72+
comparison = valueCompare(component, docValue);
73+
}
74+
if (orderByComponent.dir === Direction.DESCENDING) {
75+
comparison = comparison * -1;
76+
}
77+
if (comparison !== 0) {
78+
break;
79+
}
80+
}
81+
return comparison;
82+
}
83+
84+
/**
85+
* Returns true if a document sorts after a bound using the provided sort
86+
* order.
87+
*/
88+
export function boundSortsAfterDocument(
89+
bound: Bound,
90+
orderBy: OrderBy[],
91+
doc: Document
92+
): boolean {
93+
const comparison = boundCompareToDocument(bound, orderBy, doc);
94+
return bound.inclusive ? comparison >= 0 : comparison > 0;
95+
}
96+
97+
/**
98+
* Returns true if a document sorts before a bound using the provided sort
99+
* order.
100+
*/
101+
export function boundSortsBeforeDocument(
102+
bound: Bound,
103+
orderBy: OrderBy[],
104+
doc: Document
105+
): boolean {
106+
const comparison = boundCompareToDocument(bound, orderBy, doc);
107+
return bound.inclusive ? comparison <= 0 : comparison < 0;
108+
}
109+
110+
export function boundEquals(left: Bound | null, right: Bound | null): boolean {
111+
if (left === null) {
112+
return right === null;
113+
} else if (right === null) {
114+
return false;
115+
}
116+
117+
if (
118+
left.inclusive !== right.inclusive ||
119+
left.position.length !== right.position.length
120+
) {
121+
return false;
122+
}
123+
for (let i = 0; i < left.position.length; i++) {
124+
const leftPosition = left.position[i];
125+
const rightPosition = right.position[i];
126+
if (!valueEquals(leftPosition, rightPosition)) {
127+
return false;
128+
}
129+
}
130+
return true;
131+
}

0 commit comments

Comments
 (0)