Skip to content

Port QueryEngine #6153

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

Merged
merged 7 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions packages/firestore/src/core/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { compareDocumentsByField, Document } from '../model/document';
import { DocumentKey } from '../model/document_key';
import { FieldPath, ResourcePath } from '../model/path';
import { debugAssert, debugCast, fail } from '../util/assert';
import { isNullOrUndefined } from '../util/types';

import {
Bound,
Expand Down Expand Up @@ -155,7 +154,7 @@ export function asCollectionQueryAtPath(
* Returns true if this query does not specify any query constraints that
* could remove results.
*/
export function matchesAllDocuments(query: Query): boolean {
export function queryMatchesAllDocuments(query: Query): boolean {
return (
query.filters.length === 0 &&
query.limit === null &&
Expand All @@ -167,14 +166,6 @@ export function matchesAllDocuments(query: Query): boolean {
);
}

export function hasLimitToFirst(query: Query): boolean {
return !isNullOrUndefined(query.limit) && query.limitType === LimitType.First;
}

export function hasLimitToLast(query: Query): boolean {
return !isNullOrUndefined(query.limit) && query.limitType === LimitType.Last;
}

export function getFirstOrderByField(query: Query): FieldPath | null {
return query.explicitOrderBy.length > 0
? query.explicitOrderBy[0].field
Expand Down Expand Up @@ -393,7 +384,7 @@ export function queryWithAddedOrderBy(query: Query, orderBy: OrderBy): Query {

export function queryWithLimit(
query: Query,
limit: number,
limit: number | null,
limitType: LimitType
): Query {
return new QueryImpl(
Expand Down
23 changes: 10 additions & 13 deletions packages/firestore/src/core/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,7 @@ import { DocumentSet } from '../model/document_set';
import { TargetChange } from '../remote/remote_event';
import { debugAssert, fail } from '../util/assert';

import {
hasLimitToFirst,
hasLimitToLast,
newQueryComparator,
Query,
queryMatches
} from './query';
import { LimitType, newQueryComparator, Query, queryMatches } from './query';
import { OnlineState } from './types';
import {
ChangeType,
Expand Down Expand Up @@ -146,11 +140,13 @@ export class View {
// Note that this should never get used in a refill (when previousChanges is
// set), because there will only be adds -- no deletes or updates.
const lastDocInLimit =
hasLimitToFirst(this.query) && oldDocumentSet.size === this.query.limit
this.query.limitType === LimitType.First &&
oldDocumentSet.size === this.query.limit
? oldDocumentSet.last()
: null;
const firstDocInLimit =
hasLimitToLast(this.query) && oldDocumentSet.size === this.query.limit
this.query.limitType === LimitType.Last &&
oldDocumentSet.size === this.query.limit
? oldDocumentSet.first()
: null;

Expand Down Expand Up @@ -228,11 +224,12 @@ export class View {
});

// Drop documents out to meet limit/limitToLast requirement.
if (hasLimitToFirst(this.query) || hasLimitToLast(this.query)) {
if (this.query.limit !== null) {
while (newDocumentSet.size > this.query.limit!) {
const oldDoc = hasLimitToFirst(this.query)
? newDocumentSet.last()
: newDocumentSet.first();
const oldDoc =
this.query.limitType === LimitType.First
? newDocumentSet.last()
: newDocumentSet.first();
newDocumentSet = newDocumentSet.delete(oldDoc!.key);
newMutatedKeys = newMutatedKeys.delete(oldDoc!.key);
changeSet.track({ type: ChangeType.Removed, doc: oldDoc! });
Expand Down
6 changes: 4 additions & 2 deletions packages/firestore/src/lite-api/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
findFilterOperator,
getFirstOrderByField,
getInequalityFilterField,
hasLimitToLast,
isCollectionGroupQuery,
LimitType,
Query as InternalQuery,
Expand Down Expand Up @@ -66,7 +65,10 @@ import {
export function validateHasExplicitOrderByForLimitToLast(
query: InternalQuery
): void {
if (hasLimitToLast(query) && query.explicitOrderBy.length === 0) {
if (
query.limitType === LimitType.Last &&
query.explicitOrderBy.length === 0
) {
throw new FirestoreError(
Code.UNIMPLEMENTED,
'limitToLast() queries require specifying at least one orderBy() clause'
Expand Down
4 changes: 2 additions & 2 deletions packages/firestore/src/lite-api/reference_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
} from '@firebase/firestore-types';
import { getModularInstance } from '@firebase/util';

import { hasLimitToLast } from '../core/query';
import { LimitType } from '../core/query';
import { DeleteMutation, Precondition } from '../model/mutation';
import {
invokeBatchGetDocumentsRpc,
Expand Down Expand Up @@ -172,7 +172,7 @@ export function getDocs<T>(query: Query<T>): Promise<QuerySnapshot<T>> {
)
);

if (hasLimitToLast(query._query)) {
if (query._query.limitType === LimitType.Last) {
// Limit to last queries reverse the orderBy constraint that was
// specified by the user. As such, we need to reverse the order of the
// results to return the documents in the expected order.
Expand Down
9 changes: 9 additions & 0 deletions packages/firestore/src/local/index_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,13 @@ export interface IndexManager {
transaction: PersistenceTransaction,
documents: DocumentMap
): PersistencePromise<void>;

/**
* Iterates over all field indexes that are used to serve the given target,
* and returns the minimum offset of them all.
*/
getMinOffset(
transaction: PersistenceTransaction,
target: Target
): PersistencePromise<IndexOffset>;
}
46 changes: 38 additions & 8 deletions packages/firestore/src/local/indexeddb_index_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import {
fieldIndexToString,
IndexKind,
IndexOffset,
indexOffsetComparator,
IndexSegment
} from '../model/field_index';
import { FieldPath, ResourcePath } from '../model/path';
Expand Down Expand Up @@ -469,15 +470,22 @@ export class IndexedDbIndexManager implements IndexManager {
transaction: PersistenceTransaction,
target: Target
): PersistencePromise<IndexType> {
// TODO(orqueries): We should look at the subtargets here
return this.getFieldIndex(transaction, target).next(index => {
if (!index) {
return IndexType.NONE as IndexType;
let indexType = IndexType.FULL;
return PersistencePromise.forEach(
this.getSubTargets(target),
(target: Target) => {
return this.getFieldIndex(transaction, target).next(index => {
if (!index) {
indexType = IndexType.NONE;
} else if (
indexType !== IndexType.NONE &&
index.fields.length < targetGetSegmentCount(target)
) {
indexType = IndexType.PARTIAL;
}
});
}
return index.fields.length < targetGetSegmentCount(target)
? IndexType.PARTIAL
: IndexType.FULL;
});
).next(() => indexType);
}

/**
Expand Down Expand Up @@ -972,6 +980,28 @@ export class IndexedDbIndexManager implements IndexManager {
}
return ranges;
}

getMinOffset(
transaction: PersistenceTransaction,
target: Target
): PersistencePromise<IndexOffset> {
let offset: IndexOffset | undefined;
return PersistencePromise.forEach(
this.getSubTargets(target),
(target: Target) => {
return this.getFieldIndex(transaction, target).next(index => {
if (!index) {
offset = IndexOffset.min();
} else if (
!offset ||
indexOffsetComparator(index.indexState.offset, offset) < 0
) {
offset = index.indexState.offset;
}
});
}
).next(() => offset!);
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/src/local/indexeddb_schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { EncodedResourcePath } from './encoded_resource_path';
import { DbTimestampKey } from './indexeddb_sentinels';

// TODO(indexing): Remove this constant
const INDEXING_ENABLED = false;
export const INDEXING_ENABLED = false;

export const INDEXING_SCHEMA_VERSION = 14;

Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/src/local/local_documents_view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class LocalDocumentsView {
constructor(
readonly remoteDocumentCache: RemoteDocumentCache,
readonly mutationQueue: MutationQueue,
readonly indexManager: IndexManager
private readonly indexManager: IndexManager
) {}

/**
Expand Down
2 changes: 1 addition & 1 deletion packages/firestore/src/local/local_store_impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class LocalStoreImpl implements LocalStore {
this.indexManager
);
this.remoteDocuments.setIndexManager(this.indexManager);
this.queryEngine.setLocalDocumentsView(this.localDocuments);
this.queryEngine.initialize(this.localDocuments, this.indexManager);
}

collectGarbage(garbageCollector: LruGarbageCollector): Promise<LruResults> {
Expand Down
7 changes: 7 additions & 0 deletions packages/firestore/src/local/memory_index_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ export class MemoryIndexManager implements IndexManager {
return PersistencePromise.resolve<string | null>(null);
}

getMinOffset(
transaction: PersistenceTransaction,
target: Target
): PersistencePromise<IndexOffset> {
return PersistencePromise.resolve(IndexOffset.min());
}

updateCollectionGroup(
transaction: PersistenceTransaction,
collectionGroup: string,
Expand Down
Loading