@nozbe/watermelondb
Version:
Build powerful React Native and React web apps that scale from hundreds to tens of thousands of records and remain fast
366 lines (344 loc) • 13.5 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = void 0;
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
var _initializerDefineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/initializerDefineProperty"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _applyDecoratedDescriptor2 = _interopRequireDefault(require("@babel/runtime/helpers/applyDecoratedDescriptor"));
var _initializerWarningHelper2 = _interopRequireDefault(require("@babel/runtime/helpers/initializerWarningHelper"));
var _allPromises = _interopRequireDefault(require("../utils/fp/allPromises"));
var _invariant = _interopRequireDefault(require("../utils/common/invariant"));
var _rx = require("../utils/rx");
var _Result = require("../utils/fp/Result");
var _fp = require("../utils/fp");
var _subscriptions = require("../utils/subscriptions");
var _lazy = _interopRequireDefault(require("../decorators/lazy"));
var _subscribeToCount = _interopRequireDefault(require("../observation/subscribeToCount"));
var _subscribeToQuery = _interopRequireDefault(require("../observation/subscribeToQuery"));
var _subscribeToQueryWithColumns = _interopRequireDefault(require("../observation/subscribeToQueryWithColumns"));
var Q = _interopRequireWildcard(require("../QueryDescription"));
var _helpers = require("./helpers");
var _class, _descriptor, _descriptor2, _descriptor3, _class2;
/* eslint-disable no-use-before-define */
// import from decorarators break the app on web production WTF ¯\_(ツ)_/¯
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var Query = exports.default = (_class = (_class2 = /*#__PURE__*/function () {
// Used by withObservables to differentiate between object types
/**
* Collection associated with this query
*/
// TODO: Should this be public API? QueryDescription structure changes quite a bit...
// Note: Don't use this directly, use Collection.query(...)
function Query(collection, clauses) {
(0, _initializerDefineProperty2.default)(this, "_cachedSubscribable", _descriptor, this);
(0, _initializerDefineProperty2.default)(this, "_cachedCountSubscribable", _descriptor2, this);
(0, _initializerDefineProperty2.default)(this, "_cachedCountThrottledSubscribable", _descriptor3, this);
this.collection = collection;
this._rawDescription = Q.buildQueryDescription(clauses);
this.description = Q.queryWithoutDeleted(this._rawDescription);
}
/*:: extend: ArrayOrSpreadFn<Clause, Query<Record>> */
/**
* Returns a new Query that contains all clauses (conditions, sorting, etc.) from this Query
* as well as the ones passed as arguments.
*
* You can pass conditions as multiple arguments or a single array.
*/
// $FlowFixMe
var _proto = Query.prototype;
_proto.extend = function (...args) {
var clauses = (0, _fp.fromArrayOrSpread)(args, 'Collection.query', 'Clause');
var {
collection: collection
} = this;
var {
where: where,
sortBy: sortBy,
take: take,
skip: skip,
joinTables: joinTables,
nestedJoinTables: nestedJoinTables,
lokiTransform: lokiTransform,
sql: sql
} = this._rawDescription;
(0, _invariant.default)(!sql, 'Cannot extend an unsafe SQL query');
// TODO: Move this & tests to QueryDescription
return new Query(collection, [Q.experimentalJoinTables(joinTables)].concat((0, _toConsumableArray2.default)(nestedJoinTables.map(function ({
from: from,
to: to
}) {
return Q.experimentalNestedJoin(from, to);
})), (0, _toConsumableArray2.default)(where), (0, _toConsumableArray2.default)(sortBy), (0, _toConsumableArray2.default)(take ? [Q.take(take)] : []), (0, _toConsumableArray2.default)(skip ? [Q.skip(skip)] : []), (0, _toConsumableArray2.default)(lokiTransform ? [Q.unsafeLokiTransform(lokiTransform)] : []), (0, _toConsumableArray2.default)(clauses)));
}
/**
* `query.pipe(fn)` is a FP convenience for `fn(query)`
*/;
_proto.pipe = function (transform) {
return transform(this);
}
/**
* Fetches the list of records matching this query
*
* Tip: For convenience, you can also use `await query`
*/;
_proto.fetch = function () {
var _this = this;
return (0, _Result.toPromise)(function (callback) {
return _this.collection._fetchQuery(_this, callback);
});
};
_proto.then = function (onFulfill, onReject) {
// $FlowFixMe
return this.fetch().then(onFulfill, onReject);
}
/**
* Returns an `Rx.Observable` that tracks the list of records matching this query
*
* Tip: When using `withObservables`, you can simply pass the query without calling `.observe()`
*
* Warning: Changes to individual records in the array are NOT observed. Use `observeWithColumns`
*/;
_proto.observe = function () {
var _this2 = this;
return _rx.Observable.create(function (observer) {
return _this2._cachedSubscribable.subscribe(function (records) {
observer.next(records);
});
});
}
/**
* Same as {@link Query#observe}, but also emits when any of the records on the list
* has one of its `columnNames` changed.
*/;
_proto.observeWithColumns = function (columnNames) {
var _this3 = this;
return _rx.Observable.create(function (observer) {
return _this3.experimentalSubscribeWithColumns(columnNames, function (records) {
observer.next(records);
});
});
}
/**
* Fetches the number of records matching this query
*
* Tip: For convenience you can also use `await query.count`
*/;
_proto.fetchCount = function () {
var _this4 = this;
return (0, _Result.toPromise)(function (callback) {
return _this4.collection._fetchCount(_this4, callback);
});
};
/**
* Returns an `Rx.Observable` that tracks the number of matching records
*
* Note: By default, the count is throttled. Pass `false` to opt out of throttling.
*/
_proto.observeCount = function (isThrottled = true) {
var _this5 = this;
return _rx.Observable.create(function (observer) {
var subscribable = isThrottled ? _this5._cachedCountThrottledSubscribable : _this5._cachedCountSubscribable;
return subscribable.subscribe(function (count) {
observer.next(count);
});
});
}
/**
* Fetches the list of IDs of records matching this query
*
* Note: This is faster than using `fetch()` if you only need IDs
*/;
_proto.fetchIds = function () {
var _this6 = this;
return (0, _Result.toPromise)(function (callback) {
return _this6.collection._fetchIds(_this6, callback);
});
}
/**
* Fetches an array of raw results of this query from the database.
* These are plain JavaScript types and objects, not `Model` instances
*
* Warning: You MUST NOT mutate these objects, this can corrupt the database!
*
* This is useful as a performance optimization or for running non-standard raw queries
* (e.g. pragmas, statistics, groupped results, records with extra columns, etc...)
*/;
_proto.unsafeFetchRaw = function () {
var _this7 = this;
return (0, _Result.toPromise)(function (callback) {
return _this7.collection._unsafeFetchRaw(_this7, callback);
});
}
/**
* Rx-free equivalent of `.observe()`
*/;
_proto.experimentalSubscribe = function (subscriber) {
return this._cachedSubscribable.subscribe(subscriber);
}
/**
* Rx-free equivalent of `.observeWithColumns()`
*/;
_proto.experimentalSubscribeWithColumns = function (columnNames, subscriber) {
return (0, _subscribeToQueryWithColumns.default)(this, columnNames, subscriber);
}
/**
* Rx-free equivalent of `.observeCount()`
*/;
_proto.experimentalSubscribeToCount = function (subscriber) {
return this._cachedCountSubscribable.subscribe(subscriber);
}
/**
* Marks all records matching this query as deleted (they will be deleted permenantly after sync)
*
* Note: This method must be called within a Writer {@link Database#write}.
*
* @see {Model#markAsDeleted}
*/;
_proto.markAllAsDeleted = function () {
return new Promise(function ($return, $error) {
var records;
return Promise.resolve(this.fetch()).then(function ($await_1) {
try {
records = $await_1;
return Promise.resolve((0, _allPromises.default)(function (record) {
return record.markAsDeleted();
}, records)).then(function () {
try {
return $return();
} catch ($boundEx) {
return $error($boundEx);
}
}, $error);
} catch ($boundEx) {
return $error($boundEx);
}
}, $error);
}.bind(this));
}
/**
* Permanently deletes all records matching this query
*
* Note: Do not use this when using Sync, as deletion will not be synced.
*
* Note: This method must be called within a Writer {@link Database#write}.
*
* @see {Model#destroyPermanently}
*/;
_proto.destroyAllPermanently = function () {
return new Promise(function ($return, $error) {
var records;
return Promise.resolve(this.fetch()).then(function ($await_3) {
try {
records = $await_3;
return Promise.resolve((0, _allPromises.default)(function (record) {
return record.destroyPermanently();
}, records)).then(function () {
try {
return $return();
} catch ($boundEx) {
return $error($boundEx);
}
}, $error);
} catch ($boundEx) {
return $error($boundEx);
}
}, $error);
}.bind(this));
}
// MARK: - Internals
/**
* `Model` subclass associated with this query
*/;
// Serialized version of Query (e.g. for sending to web worker)
_proto.serialize = function () {
var {
table: table,
description: description,
associations: associations
} = this;
return {
table: table,
description: description,
associations: associations
};
};
return (0, _createClass2.default)(Query, [{
key: "count",
get: function get() {
var model = this;
return {
then: function then(onFulfill, onReject) {
// $FlowFixMe
return model.fetchCount().then(onFulfill, onReject);
}
};
}
}, {
key: "modelClass",
get: function get() {
return this.collection.modelClass;
}
/**
* Table name of the Collection associated with this query
*/
}, {
key: "table",
get: function get() {
// $FlowFixMe
return this.modelClass.table;
}
// TODO: Should any of the below be public API? Is this any useful outside of Watermelon
// internals? If so, should it even be here, not `_`-prefixed?
}, {
key: "secondaryTables",
get: function get() {
return this.description.joinTables.concat(this.description.nestedJoinTables.map(function ({
to: to
}) {
return to;
}));
}
}, {
key: "allTables",
get: function get() {
return [this.table].concat(this.secondaryTables);
}
}, {
key: "associations",
get: function get() {
return (0, _helpers.getAssociations)(this.description, this.modelClass, this.collection.db);
}
}]);
}(), _class2._wmelonTag = 'query', _class2), _descriptor = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_cachedSubscribable", [_lazy.default], {
configurable: true,
enumerable: true,
writable: true,
initializer: function initializer() {
var _this8 = this;
return new _subscriptions.SharedSubscribable(function (subscriber) {
return (0, _subscribeToQuery.default)(_this8, subscriber);
});
}
}), _descriptor2 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_cachedCountSubscribable", [_lazy.default], {
configurable: true,
enumerable: true,
writable: true,
initializer: function initializer() {
var _this9 = this;
return new _subscriptions.SharedSubscribable(function (subscriber) {
return (0, _subscribeToCount.default)(_this9, false, subscriber);
});
}
}), _descriptor3 = (0, _applyDecoratedDescriptor2.default)(_class.prototype, "_cachedCountThrottledSubscribable", [_lazy.default], {
configurable: true,
enumerable: true,
writable: true,
initializer: function initializer() {
var _this10 = this;
return new _subscriptions.SharedSubscribable(function (subscriber) {
return (0, _subscribeToCount.default)(_this10, true, subscriber);
});
}
}), _class);