Fix updating of cached child collections/items, and make more efficient

This commit is contained in:
Dan Stillman 2015-05-30 18:35:43 -04:00
parent 807c40859f
commit ed1c0a4637
4 changed files with 183 additions and 80 deletions

View File

@ -173,14 +173,14 @@ Zotero.Collection.prototype.getChildCollections = function (asIDs) {
*
* @param {Boolean} asIDs Return as itemIDs
* @param {Boolean} includeDeleted Include items in Trash
* @return {Zotero.Item[]|Integer[]|FALSE} Array of Zotero.Item instances or itemIDs,
* or FALSE if none
* @return {Zotero.Item[]|Integer[]} - Array of Zotero.Item instances or itemIDs,
* or FALSE if none
*/
Zotero.Collection.prototype.getChildItems = function (asIDs, includeDeleted) {
this._requireData('childItems');
if (this._childItems.length == 0) {
return false;
return [];
}
// Remove deleted items if necessary
@ -280,21 +280,30 @@ Zotero.Collection.prototype._saveData = Zotero.Promise.coroutine(function* (env)
}
if (this._changed.parentKey) {
var parentIDs = [];
if (this.id && this._previousData.parentKey) {
parentIDs.push(this.ObjectsClass.getIDFromLibraryAndKey(
this.libraryID, this._previousData.parentKey
));
}
// Add this item to the parent's cached item lists after commit,
// if the parent was loaded
if (this.parentKey) {
parentIDs.push(this.ObjectsClass.getIDFromLibraryAndKey(
let parentCollectionID = this.ObjectsClass.getIDFromLibraryAndKey(
this.libraryID, this.parentKey
));
);
Zotero.DB.addCurrentCallback("commit", function () {
this.ObjectsClass.registerChildCollection(parentCollectionID, this.id);
}.bind(this));
}
// Remove this from the previous parent's cached collection lists after commit,
// if the parent was loaded
else if (!isNew && this._previousData.parentKey) {
let parentCollectionID = this.ObjectsClass.getIDFromLibraryAndKey(
this.libraryID, this._previousData.parentKey
);
Zotero.DB.addCurrentCallback("commit", function () {
this.ObjectsClass.unregisterChildCollection(parentCollectionID, this.id);
}.bind(this));
}
if (!isNew) {
Zotero.Notifier.queue('move', 'collection', this.id);
}
env.parentIDs = parentIDs;
}
});
@ -314,11 +323,6 @@ Zotero.Collection.prototype._finalizeSave = Zotero.Promise.coroutine(function* (
}
}
// Invalidate cached child collections
if (env.parentIDs) {
this.ObjectsClass.refreshChildCollections(env.parentIDs);
}
if (!env.skipCache) {
yield this.reload();
// If new, there's no other data we don't have, so we can mark everything as loaded
@ -866,7 +870,7 @@ Zotero.Collection.prototype.loadChildItems = Zotero.Promise.coroutine(function*
this._childItems = [];
if (ids) {
var items = yield this.ChildObjects.getAsync(ids)
var items = yield this.ChildObjects.getAsync(ids);
if (items) {
this._childItems = items;
}
@ -878,31 +882,60 @@ Zotero.Collection.prototype.loadChildItems = Zotero.Promise.coroutine(function*
/**
* Invalidate child collection cache, if collections are loaded
*
* Note: This is called by Zotero.Collections.refreshChildCollections()
*
* @private
* @return {Promise}
* Add a collection to the cached child collections list if loaded
*/
Zotero.Collection.prototype._refreshChildCollections = Zotero.Promise.coroutine(function* () {
yield this.reloadHasChildCollections();
Zotero.Collection.prototype._registerChildCollection = function (collectionID) {
if (this._loaded.childCollections) {
return this.loadChildCollections(true);
let collection = this.ObjectsClass.get(collectionID);
if (collection) {
this._hasChildCollections = true;
this._childCollections.push(collection);
}
}
});;
}
/**
* Invalidate child item cache, if items are loaded
*
* Note: This is called by Zotero.Collections.refreshChildItems()
*
* @private
* Remove a collection from the cached child collections list if loaded
*/
Zotero.Collection.prototype._refreshChildItems = Zotero.Promise.coroutine(function* () {
yield this.reloadHasChildItems();
if (this._loaded.childItems) {
return this.loadChildItems(true);
Zotero.Collection.prototype._unregisterChildCollection = function (collectionID) {
if (this._loaded.childCollections) {
for (let i = 0; i < this._childCollections.length; i++) {
if (this._childCollections[i].id == collectionID) {
this._childCollections.splice(i, 1);
break;
}
}
this._hasChildCollections = this._childCollections.length > 0;
}
});
}
/**
* Add an item to the cached child items list if loaded
*/
Zotero.Collection.prototype._registerChildItem = function (itemID) {
if (this._loaded.childItems) {
let item = this.ChildObjects.get(itemID);
if (item) {
this._hasChildItems = true;
this._childItems.push(item);
}
}
}
/**
* Remove an item from the cached child items list if loaded
*/
Zotero.Collection.prototype._unregisterChildItem = function (itemID) {
if (this._loaded.childItems) {
for (let i = 0; i < this._childItems.length; i++) {
if (this._childItems[i].id == itemID) {
this._childItems.splice(i, 1);
break;
}
}
this._hasChildItems = this._childItems.length > 0;
}
}

View File

@ -155,38 +155,32 @@ Zotero.Collections = function() {
}
/**
* Invalidate child collection cache in specified collections, skipping any that aren't loaded
*
* @param {Integer|Integer[]} ids One or more collectionIDs
*/
this.refreshChildCollections = Zotero.Promise.coroutine(function* (ids) {
ids = Zotero.flattenArguments(ids);
for (let i=0; i<ids.length; i++) {
let id = ids[i];
if (this._objectCache[id]) {
yield this._objectCache[id]._refreshChildCollections();
}
this.registerChildCollection = function (collectionID, childCollectionID) {
if (this._objectCache[collectionID]) {
this._objectCache[collectionID]._registerChildCollection(childCollectionID);
}
});
}
/**
* Invalidate child item cache in specified collections, skipping any that aren't loaded
*
* @param {Integer|Integer[]} ids One or more itemIDs
*/
this.refreshChildItems = Zotero.Promise.coroutine(function* (ids) {
ids = Zotero.flattenArguments(ids);
for (let i=0; i<ids.length; i++) {
let id = ids[i];
if (this._objectCache[id]) {
yield this._objectCache[id]._refreshChildItems();
}
this.unregisterChildCollection = function (collectionID, childCollectionID) {
if (this._objectCache[collectionID]) {
this._objectCache[collectionID]._unregisterChildCollection(childCollectionID);
}
});
}
this.registerChildItem = function (collectionID, itemID) {
if (this._objectCache[collectionID]) {
this._objectCache[collectionID]._registerChildItem(itemID);
}
}
this.unregisterChildItem = function (collectionID, itemID) {
if (this._objectCache[collectionID]) {
this._objectCache[collectionID]._unregisterChildItem(itemID);
}
}
this.erase = function(ids) {

View File

@ -1399,7 +1399,7 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
if (changedCollections.length) {
let parentItem = yield this.ObjectsClass.getByLibraryAndKeyAsync(
this.libraryID, parentItemKey
)
);
for (let i=0; i<changedCollections.length; i++) {
yield parentItem.loadCollections();
parentItem.addToCollection(changedCollections[i]);
@ -1622,19 +1622,27 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
let toAdd = Zotero.Utilities.arrayDiff(newCollections, oldCollections);
let toRemove = Zotero.Utilities.arrayDiff(oldCollections, newCollections);
for (let i=0; i<toAdd.length; i++) {
let collectionID = toAdd[i];
if (toAdd.length) {
for (let i=0; i<toAdd.length; i++) {
let collectionID = toAdd[i];
let sql = "SELECT IFNULL(MAX(orderIndex)+1, 0) FROM collectionItems "
+ "WHERE collectionID=?";
let orderIndex = yield Zotero.DB.valueQueryAsync(sql, collectionID);
sql = "INSERT OR IGNORE INTO collectionItems "
+ "(collectionID, itemID, orderIndex) VALUES (?, ?, ?)";
yield Zotero.DB.queryAsync(sql, [collectionID, this.id, orderIndex]);
Zotero.Notifier.queue('add', 'collection-item', collectionID + '-' + this.id);
}
let sql = "SELECT IFNULL(MAX(orderIndex)+1, 0) FROM collectionItems "
+ "WHERE collectionID=?";
let orderIndex = yield Zotero.DB.valueQueryAsync(sql, collectionID);
sql = "INSERT OR IGNORE INTO collectionItems "
+ "(collectionID, itemID, orderIndex) VALUES (?, ?, ?)";
yield Zotero.DB.queryAsync(sql, [collectionID, this.id, orderIndex]);
yield this.ContainerObjectsClass.refreshChildItems(collectionID);
Zotero.Notifier.queue('add', 'collection-item', collectionID + '-' + this.id);
// Add this item to any loaded collections' cached item lists after commit
Zotero.DB.addCurrentCallback("commit", function () {
for (let i = 0; i < toAdd.length; i++) {
this.ContainerObjectsClass.registerChildItem(toAdd[i], this.id);
}
}.bind(this));
}
if (toRemove.length) {
@ -1645,9 +1653,16 @@ Zotero.Item.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
for (let i=0; i<toRemove.length; i++) {
let collectionID = toRemove[i];
yield this.ContainerObjectsClass.refreshChildItems(collectionID);
Zotero.Notifier.queue('remove', 'collection-item', collectionID + '-' + this.id);
}
// Remove this item from any loaded collections' cached item lists after commit
Zotero.DB.addCurrentCallback("commit", function () {
for (let i = 0; i < toRemove.length; i++) {
this.ContainerObjectsClass.unregisterChildItem(toRemove[i], this.id);
}
}.bind(this));
}
}

View File

@ -97,4 +97,65 @@ describe("Zotero.Collection", function() {
assert.isFalse(ret);
});
})
describe("#getChildCollections()", function () {
it("should include child collections", function* () {
var collection1 = yield createDataObject('collection');
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
yield collection1.saveTx();
yield collection1.loadChildCollections();
var childCollections = collection1.getChildCollections();
assert.lengthOf(childCollections, 1);
assert.equal(childCollections[0].id, collection2.id);
})
it("should not include collections that have been removed", function* () {
var collection1 = yield createDataObject('collection');
var collection2 = yield createDataObject('collection', { parentID: collection1.id });
yield collection1.saveTx();
yield collection1.loadChildCollections();
collection2.parentID = false;
yield collection2.save()
var childCollections = collection1.getChildCollections();
assert.lengthOf(childCollections, 0);
})
})
describe("#getChildItems()", function () {
it("should include child items", function* () {
var collection = yield createDataObject('collection');
var item = createUnsavedDataObject('item');
item.addToCollection(collection.key);
yield item.saveTx();
yield collection.loadChildItems();
assert.lengthOf(collection.getChildItems(), 1);
})
it("should not include items in trash by default", function* () {
var collection = yield createDataObject('collection');
var item = createUnsavedDataObject('item');
item.deleted = true;
item.addToCollection(collection.key);
yield item.saveTx();
yield collection.loadChildItems();
assert.lengthOf(collection.getChildItems(), 0);
})
it("should include items in trash if includeTrashed=true", function* () {
var collection = yield createDataObject('collection');
var item = createUnsavedDataObject('item');
item.deleted = true;
item.addToCollection(collection.key);
yield item.saveTx();
yield collection.loadChildItems();
assert.lengthOf(collection.getChildItems(false, true), 1);
})
})
})