Skip to content

Commit 5a60f40

Browse files
committed
MOBILE-1666 db: Fallback where equal when generators not supported
1 parent 3d5a137 commit 5a60f40

File tree

3 files changed

+231
-75
lines changed

3 files changed

+231
-75
lines changed

upgrade.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ information provided here is intended especially for developers.
1515
or
1616
ionic state restore
1717
* mm-rich-text-editor now admits a new parameter adjust-height (default: true). It admits an HTML DOM Id string or boolean. Specifying it will adjust height of the editor to the selected element. If true it will adjust to the whole page and false will use height parameter instead.
18+
* Generators on stores are deprecated. Using generators will make Android < 4.4 fail. Use keyPath instead. They will be automatically generated if needed. DB Functions query, update and where are not allowed using compoung indexes see JSdoc DB functions on db.js for more info.
1819

1920
=== 3.2 ===
2021

www/core/lib/db.js

Lines changed: 154 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -72,23 +72,23 @@ angular.module('mm.core')
7272
* @return {Promise} Promise to be resolved when the operation finishes.
7373
*/
7474
function callDBFunction(db, func) {
75+
if (typeof db == 'undefined') {
76+
return $q.reject();
77+
}
78+
7579
var deferred = $q.defer();
7680

7781
try {
78-
if (typeof(db) != 'undefined') {
79-
db[func].apply(db, Array.prototype.slice.call(arguments, 2)).then(function(result) {
80-
if (typeof(result) == 'undefined') {
81-
deferred.reject();
82-
} else {
83-
deferred.resolve(result);
84-
}
85-
});
86-
} else {
87-
deferred.reject();
88-
}
82+
db[func].apply(db, Array.prototype.slice.call(arguments, 2)).then(function(result) {
83+
if (typeof result == 'undefined') {
84+
deferred.reject();
85+
} else {
86+
deferred.resolve(result);
87+
}
88+
}, deferred.reject);
8989
} catch(ex) {
90-
$log.error('Error executing function '+func+' to DB '+db.getName());
91-
$log.error(ex.name+': '+ex.message);
90+
$log.error('Error executing function ' + func + ' to DB ' + db.getName());
91+
$log.error(ex.name + ': ' + ex.message);
9292
deferred.reject();
9393
}
9494

@@ -103,24 +103,32 @@ angular.module('mm.core')
103103
* @return {Promise}
104104
*/
105105
function callCount(db, store, where) {
106-
var deferred = $q.defer(),
107-
query;
106+
if (typeof db == 'undefined') {
107+
return $q.reject();
108+
}
109+
110+
var deferred = $q.defer();
108111

109112
try {
110-
if (typeof(db) != 'undefined') {
111-
query = db.from(store);
112-
query = applyWhere(query, where);
113-
query.count().then(function(count) {
114-
deferred.resolve(count);
115-
}, function() {
116-
deferred.reject();
113+
var query = db.from(store);
114+
query = applyWhere(query, where);
115+
query.count().then(deferred.resolve, deferred.reject);
116+
} catch(ex) {
117+
var promise;
118+
119+
if (where[1] == '=') {
120+
// Fallback, not using all options.
121+
promise = callWhereEqualFallBack(db, store, where[0], where[2]).then(function(list) {
122+
deferred.resolve(list.length);
117123
});
118124
} else {
119-
deferred.reject();
125+
promise = $q.reject();
120126
}
121-
} catch(ex) {
122-
$log.error('Error querying db '+db.getName()+'. '+ex.name+': '+ex.message);
123-
deferred.reject();
127+
128+
promise.catch(function () {
129+
$log.error('Error counting on db ' + db.getName() + '. ' + ex.name + ': ' + ex.message);
130+
deferred.reject();
131+
});
124132
}
125133

126134
return deferred.promise;
@@ -138,21 +146,28 @@ angular.module('mm.core')
138146
* @return {Promise} Promise to be resolved when the list is retrieved.
139147
*/
140148
function callWhere(db, store, field_name, op, value, op2, value2) {
149+
if (typeof db == 'undefined') {
150+
return $q.reject();
151+
}
152+
141153
var deferred = $q.defer();
142154

143155
try {
144-
if (typeof(db) != 'undefined') {
145-
db.from(store).where(field_name, op, value, op2, value2).list().then(function(list) {
146-
deferred.resolve(list);
147-
}, function() {
148-
deferred.reject();
149-
});
156+
db.from(store).where(field_name, op, value, op2, value2).list().then(deferred.resolve, deferred.reject);
157+
} catch(ex) {
158+
var promise;
159+
160+
if (op == '=') {
161+
// Fallback, not using all options.
162+
promise = callWhereEqualFallBack(db, store, field_name, value).then(deferred.resolve).catch(deferred.reject);
150163
} else {
151-
deferred.reject();
164+
promise = $q.reject();
152165
}
153-
} catch(ex) {
154-
$log.error('Error querying db '+db.getName()+'. '+ex.name+': '+ex.message);
155-
deferred.reject();
166+
167+
promise.catch(function () {
168+
$log.error('Error getting where from db ' + db.getName() + '. ' + ex.name+': ' + ex.message);
169+
deferred.reject();
170+
});
156171
}
157172

158173
return deferred.promise;
@@ -168,26 +183,97 @@ angular.module('mm.core')
168183
* @return {Promise} Promise to be resolved when the list is retrieved.
169184
*/
170185
function callWhereEqual(db, store, field_name, value) {
186+
if (typeof db == 'undefined') {
187+
return $q.reject();
188+
}
189+
171190
var deferred = $q.defer();
172191

173192
try {
174-
if (typeof(db) != 'undefined') {
175-
db.from(store).where(field_name, '=', value).list().then(function(list) {
176-
deferred.resolve(list);
177-
}, function() {
178-
deferred.reject();
179-
});
180-
} else {
193+
db.from(store).where(field_name, '=', value).list().then(deferred.resolve, deferred.reject);
194+
} catch(ex) {
195+
callWhereEqualFallBack(db, store, field_name, value).then(deferred.resolve).catch(function () {
196+
$log.error('Error getting where equal from db ' + db.getName() + '. ' + ex.name + ': ' + ex.message);
181197
deferred.reject();
182-
}
198+
});
199+
}
200+
201+
return deferred.promise;
202+
}
203+
204+
// FallBack Method when where equal is not supported by the DB. See callWhereEqual for more info.
205+
// It will only be used if the field_name is a compound index and the database does not support where equals on IndexedDB.
206+
function callWhereEqualFallBack(db, store, field_name, values) {
207+
var fields = getCompoundIndex(db, store, field_name);
208+
if (!fields) {
209+
return $q.reject();
210+
}
211+
212+
if (typeof fields == "string") {
213+
// This should not happen.
214+
fields = [fields];
215+
}
216+
217+
var deferred = $q.defer();
218+
219+
try {
220+
db.from(store).where(fields[0], '=', values[0]).list().then(function(list) {
221+
var results = filterWhereList(list, fields, values, 1);
222+
deferred.resolve(results);
223+
}, deferred.reject);
183224
} catch(ex) {
184-
$log.error('Error getting where equal from db '+db.getName()+'. '+ex.name+': '+ex.message);
185225
deferred.reject();
186226
}
187227

188228
return deferred.promise;
189229
}
190230

231+
/**
232+
* Convenience function that returns the Compound Index for a given store and db.
233+
* @param {Object} db Database to get the schema.
234+
* @param {String} storeName The store where to find the index.
235+
* @param {String} index Index to get.
236+
* @return {Mixed} KeyPath of the index found or false.
237+
*/
238+
function getCompoundIndex(db, storeName, index) {
239+
var stores = db.getSchema().stores;
240+
for (var x in stores) {
241+
if (stores[x].name == storeName) {
242+
var indexes = stores[x].indexes;
243+
for (var y in indexes) {
244+
if (indexes[y].name == index) {
245+
return indexes[y].keyPath;
246+
}
247+
}
248+
return false;
249+
}
250+
}
251+
return false;
252+
}
253+
254+
/**
255+
* Convenience function to filter results by a list of fields and values. Recursive.
256+
*
257+
* @param {Array] list List of results to be filtered.
258+
* @param {Array} fields List of field names to filter.
259+
* @param {Array} values List of field values to filter, in the same order as fields.
260+
* @param {Number} indexNum What field - value have to be filtered.
261+
* @return {Array} Filtered list.
262+
*/
263+
function filterWhereList(list, fields, values, indexNum) {
264+
if (list.length == 0 || fields.length < indexNum || values.length < indexNum) {
265+
return list;
266+
}
267+
268+
var field = fields[indexNum],
269+
value = values[indexNum];
270+
271+
list = list.filter(function (item) {
272+
return item[field] == value;
273+
});
274+
return filterWhereList(list, fields, values, indexNum + 1);
275+
}
276+
191277
/**
192278
* Performs an operation with every entry in a certain store.
193279
* @param {Object} db DB to use.
@@ -203,9 +289,7 @@ angular.module('mm.core')
203289
callback(entries[i]);
204290
}
205291
deferred.resolve();
206-
}, function() {
207-
deferred.reject();
208-
});
292+
}, deferred.reject);
209293

210294
return deferred.promise;
211295
}
@@ -222,22 +306,18 @@ angular.module('mm.core')
222306
* @return {Promise}
223307
*/
224308
function doQuery(db, store, where, order, reverse, limit) {
309+
if (typeof db == 'undefined') {
310+
return $q.reject();
311+
}
312+
225313
var deferred = $q.defer(),
226314
query;
227315

228316
try {
229-
if (typeof(db) != 'undefined') {
230-
query = db.from(store);
231-
query = applyWhere(query, where);
232-
query = applyOrder(query, order, reverse);
233-
query.list(limit).then(function(list) {
234-
deferred.resolve(list);
235-
}, function() {
236-
deferred.reject();
237-
});
238-
} else {
239-
deferred.reject();
240-
}
317+
query = db.from(store);
318+
query = applyWhere(query, where);
319+
query = applyOrder(query, order, reverse);
320+
query.list(limit).then(deferred.resolve, deferred.reject);
241321
} catch(ex) {
242322
$log.error('Error querying ' + store + ' on ' + db.getName() + '. ' + ex.name + ': ' + ex.message);
243323
deferred.reject();
@@ -256,23 +336,19 @@ angular.module('mm.core')
256336
* @return {Promise}
257337
*/
258338
function doUpdate(db, store, values, where) {
339+
if (typeof db == 'undefined') {
340+
return $q.reject();
341+
}
342+
259343
var deferred = $q.defer(),
260344
query;
261345

262346
try {
263-
if (typeof(db) != 'undefined') {
264-
query = db.from(store);
265-
query = applyWhere(query, where);
266-
query.patch(values).then(function(count) {
267-
deferred.resolve(count);
268-
}, function() {
269-
deferred.reject();
270-
});
271-
} else {
272-
deferred.reject();
273-
}
347+
query = db.from(store);
348+
query = applyWhere(query, where);
349+
query.patch(values).then(deferred.resolve, deferred.reject);
274350
} catch(ex) {
275-
$log.error('Error querying ' + store + ' on ' + db.getName() + '. ' + ex.name + ': ' + ex.message);
351+
$log.error('Error updating ' + store + ' on ' + db.getName() + '. ' + ex.name + ': ' + ex.message);
276352
deferred.reject();
277353
}
278354

@@ -296,8 +372,8 @@ angular.module('mm.core')
296372
self.getDB = function(name, schema, options, forceNew) {
297373
if (typeof dbInstances[name] === 'undefined' || forceNew) {
298374

299-
var isSafari = !ionic.Platform.isIOS() && !ionic.Platform.isAndroid() && navigator.userAgent.indexOf('Safari') != -1
300-
&& navigator.userAgent.indexOf('Chrome') == -1 && navigator.userAgent.indexOf('Firefox') == -1;
375+
var isSafari = !ionic.Platform.isIOS() && !ionic.Platform.isAndroid() && navigator.userAgent.indexOf('Safari') != -1 &&
376+
navigator.userAgent.indexOf('Chrome') == -1 && navigator.userAgent.indexOf('Firefox') == -1;
301377
if (typeof IDBObjectStore == 'undefined' || typeof IDBObjectStore.prototype.count == 'undefined' || isSafari) {
302378
// IndexedDB not implemented or not fully implemented (Galaxy S4 Mini). Use WebSQL.
303379
if (typeof options.mechanisms == 'undefined') {
@@ -387,6 +463,7 @@ angular.module('mm.core')
387463
},
388464
/**
389465
* Query the database.
466+
* WARNING: Do not use it with compound indexes, on Android versions lower than 4.4 will fail.
390467
*
391468
* @param {String} store Name of the store.
392469
* @param {Array} [where] Array of where conditions, see applyWhere.
@@ -419,6 +496,7 @@ angular.module('mm.core')
419496
},
420497
/**
421498
* Update records matching.
499+
* WARNING: Do not use it with compound indexes, on Android versions lower than 4.4 will fail.
422500
*
423501
* @param {String} store Name of the store.
424502
* @param {Object} values The values to update.
@@ -430,6 +508,8 @@ angular.module('mm.core')
430508
},
431509
/**
432510
* Get the entries where a field match certain conditions.
511+
* WARNING: Do not use it with compound indexes, on Android versions lower than 4.4 will fail.
512+
* Try to use whereEqual instead.
433513
*
434514
* @param {String} store Name of the store.
435515
* @param {String} field_name Name of the field to match.

0 commit comments

Comments
 (0)