2017-09-26 13:57:25 +00:00
|
|
|
/**
|
|
|
|
* @flow
|
|
|
|
* Query representation wrapper
|
|
|
|
*/
|
|
|
|
import DocumentSnapshot from './DocumentSnapshot';
|
|
|
|
import Path from './Path';
|
|
|
|
import QuerySnapshot from './QuerySnapshot';
|
2017-10-03 09:12:25 +00:00
|
|
|
import INTERNALS from '../../internals';
|
|
|
|
import { firestoreAutoId } from '../../utils';
|
2017-09-26 13:57:25 +00:00
|
|
|
|
|
|
|
const DIRECTIONS = {
|
2017-09-27 11:57:53 +00:00
|
|
|
ASC: 'ASCENDING',
|
|
|
|
asc: 'ASCENDING',
|
|
|
|
DESC: 'DESCENDING',
|
|
|
|
desc: 'DESCENDING',
|
2017-09-26 13:57:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const OPERATORS = {
|
|
|
|
'=': 'EQUAL',
|
|
|
|
'==': 'EQUAL',
|
|
|
|
'>': 'GREATER_THAN',
|
|
|
|
'>=': 'GREATER_THAN_OR_EQUAL',
|
2017-09-27 11:57:53 +00:00
|
|
|
'<': 'LESS_THAN',
|
|
|
|
'<=': 'LESS_THAN_OR_EQUAL',
|
2017-09-26 13:57:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
export type Direction = 'DESC' | 'desc' | 'ASC' | 'asc';
|
|
|
|
type FieldFilter = {
|
|
|
|
fieldPath: string,
|
|
|
|
operator: string,
|
|
|
|
value: any,
|
|
|
|
}
|
|
|
|
type FieldOrder = {
|
|
|
|
direction: string,
|
|
|
|
fieldPath: string,
|
|
|
|
}
|
|
|
|
type QueryOptions = {
|
2017-09-27 11:57:53 +00:00
|
|
|
endAt?: any[],
|
|
|
|
endBefore?: any[],
|
2017-09-26 13:57:25 +00:00
|
|
|
limit?: number,
|
|
|
|
offset?: number,
|
|
|
|
selectFields?: string[],
|
|
|
|
startAfter?: any[],
|
|
|
|
startAt?: any[],
|
|
|
|
}
|
|
|
|
export type Operator = '<' | '<=' | '=' | '==' | '>' | '>=';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Query
|
|
|
|
*/
|
|
|
|
export default class Query {
|
|
|
|
_fieldFilters: FieldFilter[];
|
|
|
|
_fieldOrders: FieldOrder[];
|
|
|
|
_firestore: Object;
|
2017-10-03 09:12:25 +00:00
|
|
|
_iid: number;
|
2017-09-26 13:57:25 +00:00
|
|
|
_queryOptions: QueryOptions;
|
|
|
|
_referencePath: Path;
|
|
|
|
|
|
|
|
constructor(firestore: Object, path: Path, fieldFilters?: FieldFilter[],
|
|
|
|
fieldOrders?: FieldOrder[], queryOptions?: QueryOptions) {
|
|
|
|
this._fieldFilters = fieldFilters || [];
|
|
|
|
this._fieldOrders = fieldOrders || [];
|
|
|
|
this._firestore = firestore;
|
|
|
|
this._queryOptions = queryOptions || {};
|
|
|
|
this._referencePath = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
get firestore(): Object {
|
|
|
|
return this._firestore;
|
|
|
|
}
|
|
|
|
|
|
|
|
endAt(fieldValues: any): Query {
|
|
|
|
fieldValues = [].slice.call(arguments);
|
|
|
|
// TODO: Validation
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
endAt: fieldValues,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
this._fieldOrders, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
endBefore(fieldValues: any): Query {
|
|
|
|
fieldValues = [].slice.call(arguments);
|
|
|
|
// TODO: Validation
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
endBefore: fieldValues,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
this._fieldOrders, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
get(): Promise<QuerySnapshot> {
|
|
|
|
return this._firestore._native
|
|
|
|
.collectionGet(
|
2017-09-27 11:57:53 +00:00
|
|
|
this._referencePath.relativeName,
|
2017-09-26 13:57:25 +00:00
|
|
|
this._fieldFilters,
|
|
|
|
this._fieldOrders,
|
|
|
|
this._queryOptions,
|
|
|
|
)
|
2017-09-27 11:57:53 +00:00
|
|
|
.then(nativeData => new QuerySnapshot(this._firestore, this, nativeData));
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
limit(n: number): Query {
|
|
|
|
// TODO: Validation
|
|
|
|
// validate.isInteger('n', n);
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
limit: n,
|
|
|
|
};
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
this._fieldOrders, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
offset(n: number): Query {
|
|
|
|
// TODO: Validation
|
|
|
|
// validate.isInteger('n', n);
|
|
|
|
|
2017-09-27 11:57:53 +00:00
|
|
|
/* const options = {
|
2017-09-26 13:57:25 +00:00
|
|
|
...this._queryOptions,
|
|
|
|
offset: n,
|
|
|
|
};
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
2017-09-27 11:57:53 +00:00
|
|
|
this._fieldOrders, options); */
|
|
|
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'offset'));
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
onSnapshot(onNext: () => any, onError?: () => any): () => void {
|
2017-10-03 09:12:25 +00:00
|
|
|
// TODO: Validation
|
|
|
|
const listenerId = firestoreAutoId();
|
|
|
|
|
|
|
|
const listener = (nativeQuerySnapshot) => {
|
|
|
|
const querySnapshot = new QuerySnapshot(this._firestore, this, nativeQuerySnapshot);
|
|
|
|
onNext(querySnapshot);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Listen to snapshot events
|
|
|
|
this._firestore.on(
|
|
|
|
this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`),
|
|
|
|
listener,
|
|
|
|
);
|
|
|
|
|
|
|
|
// Listen for snapshot error events
|
|
|
|
if (onError) {
|
|
|
|
this._firestore.on(
|
|
|
|
this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`),
|
|
|
|
onError,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add the native listener
|
|
|
|
this._firestore._native
|
|
|
|
.collectionOnSnapshot(
|
|
|
|
this._referencePath.relativeName,
|
|
|
|
this._fieldFilters,
|
|
|
|
this._fieldOrders,
|
|
|
|
this._queryOptions,
|
|
|
|
listenerId
|
|
|
|
);
|
2017-09-26 13:57:25 +00:00
|
|
|
|
2017-10-03 09:12:25 +00:00
|
|
|
// Return an unsubscribe method
|
|
|
|
return this._offCollectionSnapshot.bind(this, listenerId, listener);
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
orderBy(fieldPath: string, directionStr?: Direction = 'asc'): Query {
|
2017-09-27 11:57:53 +00:00
|
|
|
// TODO: Validation
|
|
|
|
// validate.isFieldPath('fieldPath', fieldPath);
|
|
|
|
// validate.isOptionalFieldOrder('directionStr', directionStr);
|
2017-09-26 13:57:25 +00:00
|
|
|
|
|
|
|
if (this._queryOptions.startAt || this._queryOptions.endAt) {
|
|
|
|
throw new Error('Cannot specify an orderBy() constraint after calling ' +
|
|
|
|
'startAt(), startAfter(), endBefore() or endAt().');
|
|
|
|
}
|
|
|
|
|
|
|
|
const newOrder = {
|
|
|
|
direction: DIRECTIONS[directionStr],
|
|
|
|
fieldPath,
|
|
|
|
};
|
|
|
|
const combinedOrders = this._fieldOrders.concat(newOrder);
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
combinedOrders, this._queryOptions);
|
|
|
|
}
|
|
|
|
|
|
|
|
select(varArgs: string[]): Query {
|
2017-09-27 11:57:53 +00:00
|
|
|
/*
|
2017-09-26 13:57:25 +00:00
|
|
|
varArgs = Array.isArray(arguments[0]) ? arguments[0] : [].slice.call(arguments);
|
|
|
|
const fieldReferences = [];
|
|
|
|
|
|
|
|
if (varArgs.length === 0) {
|
|
|
|
fieldReferences.push(DOCUMENT_NAME_FIELD);
|
|
|
|
} else {
|
|
|
|
for (let i = 0; i < varArgs.length; ++i) {
|
|
|
|
// TODO: Validation
|
|
|
|
// validate.isFieldPath(i, args[i]);
|
|
|
|
fieldReferences.push(varArgs[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
selectFields: fieldReferences,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
2017-09-27 11:57:53 +00:00
|
|
|
this._fieldOrders, options);*/
|
|
|
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'select'));
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
startAfter(fieldValues: any): Query {
|
|
|
|
fieldValues = [].slice.call(arguments);
|
|
|
|
// TODO: Validation
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
startAfter: fieldValues,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
this._fieldOrders, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
startAt(fieldValues: any): Query {
|
|
|
|
fieldValues = [].slice.call(arguments);
|
|
|
|
// TODO: Validation
|
|
|
|
const options = {
|
|
|
|
...this._queryOptions,
|
|
|
|
startAt: fieldValues,
|
|
|
|
};
|
|
|
|
|
|
|
|
return new Query(this.firestore, this._referencePath, this._fieldFilters,
|
|
|
|
this._fieldOrders, options);
|
|
|
|
}
|
|
|
|
|
|
|
|
stream(): Stream<DocumentSnapshot> {
|
2017-10-02 12:11:38 +00:00
|
|
|
throw new Error(INTERNALS.STRINGS.ERROR_UNSUPPORTED_CLASS_METHOD('Query', 'stream'));
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
where(fieldPath: string, opStr: Operator, value: any): Query {
|
|
|
|
// TODO: Validation
|
|
|
|
// validate.isFieldPath('fieldPath', fieldPath);
|
|
|
|
// validate.isFieldFilter('fieldFilter', opStr, value);
|
|
|
|
const newFilter = {
|
|
|
|
fieldPath,
|
|
|
|
operator: OPERATORS[opStr],
|
|
|
|
value,
|
|
|
|
};
|
|
|
|
const combinedFilters = this._fieldFilters.concat(newFilter);
|
|
|
|
return new Query(this.firestore, this._referencePath, combinedFilters,
|
|
|
|
this._fieldOrders, this._queryOptions);
|
|
|
|
}
|
2017-10-03 09:12:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove query snapshot listener
|
|
|
|
* @param listener
|
|
|
|
*/
|
|
|
|
_offCollectionSnapshot(listenerId: number, listener: Function) {
|
|
|
|
this._firestore.log.info('Removing onQuerySnapshot listener');
|
|
|
|
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshot:${listenerId}`), listener);
|
|
|
|
this._firestore.removeListener(this._firestore._getAppEventName(`onQuerySnapshotError:${listenerId}`), listener);
|
|
|
|
this._firestore._native
|
|
|
|
.collectionOffSnapshot(
|
|
|
|
this._referencePath.relativeName,
|
|
|
|
this._fieldFilters,
|
|
|
|
this._fieldOrders,
|
|
|
|
this._queryOptions,
|
|
|
|
listenerId
|
|
|
|
);
|
|
|
|
}
|
2017-09-26 13:57:25 +00:00
|
|
|
}
|