Speed up saving of tags with many items

Fixes #289, RIS import gets progressively slower
This commit is contained in:
Dan Stillman 2013-05-01 19:14:45 -04:00
parent 9e8559b4be
commit efda43f6e3
2 changed files with 77 additions and 82 deletions

View file

@ -47,7 +47,9 @@ Zotero.Tag.prototype._init = function () {
this._previousData = false;
this._linkedItemsLoaded = false;
this._linkedItems = [];
this._linkedItems = {};
this._linkedItemIDsToAdd = [];
this._linkedItemIDsToRemove = [];
}
@ -212,66 +214,63 @@ Zotero.Tag.prototype.getLinkedItems = function (asIDs) {
this._loadLinkedItems();
}
if (this._linkedItems.length == 0) {
return false;
if (Zotero.Utilities.isEmpty(this._linkedItems)) {
return [];
}
// Return itemIDs
if (asIDs) {
var ids = [];
for each(var item in this._linkedItems) {
ids.push(item.id);
}
return ids;
return Object.keys(this._linkedItems);
}
// Return Zotero.Item objects
var objs = [];
for each(var item in this._linkedItems) {
objs.push(item);
}
return objs;
return [item for each(item in this._linkedItems)];
}
Zotero.Tag.prototype.countLinkedItems = function () {
return this.getLinkedItems().length;
return Object.keys(this._linkedItems).length;
}
Zotero.Tag.prototype.addItem = function (itemID) {
var current = this.getLinkedItems(true);
if (current && current.indexOf(itemID) != -1) {
if (!this._linkedItemsLoaded) {
this._loadLinkedItems();
}
if (this._linkedItems[itemID]) {
Zotero.debug("Item " + itemID + " already has tag "
+ this.id + " in Zotero.Tag.addItem()");
return false;
}
this._prepFieldChange('linkedItems');
var item = Zotero.Items.get(itemID);
if (!item) {
throw ("Can't link invalid item " + itemID + " to tag " + this.id
+ " in Zotero.Tag.addItem()");
throw new Error("Can't link invalid item " + itemID + " to tag " + this.id);
}
this._linkedItems.push(item);
this._prepFieldChange('linkedItems');
this._linkedItems[itemID] = item;
this._linkedItemIDsToAdd.push(itemID);
this._linkedItemIDsToRemove = this._linkedItemIDsToRemove.filter(function (x) x != itemID);
return true;
}
Zotero.Tag.prototype.removeItem = function (itemID) {
var current = this.getLinkedItems(true);
if (current) {
var index = current.indexOf(itemID);
if (!this._linkedItemsLoaded) {
this._loadLinkedItems();
}
if (!current || index == -1) {
if (!this._linkedItems[itemID]) {
Zotero.debug("Item " + itemID + " doesn't have tag "
+ this.id + " in Zotero.Tag.removeItem()");
return false;
}
this._prepFieldChange('linkedItems');
this._linkedItems.splice(index, 1);
delete this._linkedItems[itemID];
this._linkedItemIDsToAdd = this._linkedItemIDsToAdd.filter(function (x) x != itemID);
this._linkedItemIDsToRemove.push(itemID);
return true;
}
@ -406,7 +405,6 @@ Zotero.Tag.prototype.save = function (full) {
}
}
// Linked items
if (full || this._changed.linkedItems) {
var removed = [];
@ -430,17 +428,8 @@ Zotero.Tag.prototype.save = function (full) {
}
}
else {
if (this._previousData.linkedItems) {
removed = Zotero.Utilities.arrayDiff(
this._previousData.linkedItems, currentIDs
);
newids = Zotero.Utilities.arrayDiff(
currentIDs, this._previousData.linkedItems
);
}
else {
newids = currentIDs;
}
removed = this._linkedItemIDsToRemove;
newids = this._linkedItemIDsToAdd;
}
if (removed.length) {
@ -675,14 +664,13 @@ Zotero.Tag.prototype._loadLinkedItems = function() {
}
var sql = "SELECT itemID FROM itemTags WHERE tagID=?";
var ids = Zotero.DB.columnQuery(sql, this.id);
var ids = Zotero.DB.columnQuery(sql, this.id) || [];
this._linkedItems = [];
this._linkedItems = {};
if (ids) {
for each(var id in ids) {
this._linkedItems.push(Zotero.Items.get(id));
}
for (let i=0; i<ids.length; i++) {
let id = ids[i];
this._linkedItems[id] = Zotero.Items.get(id);
}
this._linkedItemsLoaded = true;
@ -694,57 +682,63 @@ Zotero.Tag.prototype._setLinkedItems = function (itemIDs) {
this._loadLinkedItems();
}
if (itemIDs.constructor.name != 'Array') {
throw ('ids must be an array in Zotero.Tag._setLinkedItems()');
if (!Array.isArray(itemIDs)) {
throw new Error('itemIDs must be an array');
}
var currentIDs = this.getLinkedItems(true);
if (!currentIDs) {
currentIDs = [];
}
var oldIDs = []; // children being kept
var newIDs = []; // new children
var numCurrent = this.countLinkedItems();
if (itemIDs.length == 0) {
if (currentIDs.length == 0) {
if (numCurrent == 0) {
Zotero.debug('No linked items added', 4);
return false;
}
}
else {
for (var i in itemIDs) {
var id = parseInt(itemIDs[i]);
if (isNaN(id)) {
throw ("Invalid itemID '" + itemIDs[i]
+ "' in Zotero.Tag._setLinkedItems()");
}
if (currentIDs.indexOf(id) != -1) {
Zotero.debug("Item " + itemIDs[i]
+ " is already linked to tag " + this.id);
if (oldIDs.indexOf(id) == -1) {
oldIDs.push(id);
}
continue;
}
newIDs.push(id);
}
}
// Mark as changed if new or removed ids
if (newIDs.length > 0 || oldIDs.length != currentIDs.length) {
// No items passed, so remove all currently linked items
this._prepFieldChange('linkedItems');
this._linkedItems = {};
this._linkedItemIDsToAdd = [];
this._linkedItemIDsToRemove = this.getLinkedItems(true);
return true;
}
// Get all new items
for (let i=0; i<itemIDs.length; i++) {
let id = itemIDs[i];
if (isNaN(parseInt(id))) {
throw new Error("Invalid itemID '" + itemIDs[i] + "'");
}
if (this._linkedItemIDs[id]) {
Zotero.debug("Item " + id + " is already linked to tag " + this.id);
}
else {
this._linkedItemIDsToAdd.push(id);
}
}
// If no new items and the old and new lengths match, none are being removed either
if (!this._linkedItemIDsToAdd.length && numCurrent == itemIDs.length) {
Zotero.debug('Linked items not changed in Zotero.Tag._setLinkedItems()', 4);
return false;
}
newIDs = oldIDs.concat(newIDs);
this._prepFieldChange('linkedItems');
// Rebuild linked items with items that exist
var items = Zotero.Items.get(itemIDs) || [];
this._linkedItems = {};
for (let i=0; i<items; i++) {
this._linkedItems[items[i].id] = items[i];
}
// Mark items not found for removal
for (let i=0; i<itemIDs.length; i++) {
if (!this._linkedItemIDs[id]) {
this._linkedItemIDsToRemove.push(id);
}
}
var items = Zotero.Items.get(itemIDs);
this._linkedItems = items ? items : [];
return true;
}

View file

@ -557,11 +557,11 @@ Zotero.Utilities = {
* otherwise return the values
*/
"arrayDiff":function(array1, array2, useIndex) {
if (array1.constructor.name != 'Array') {
throw ("array1 is not an array in Zotero.Utilities.arrayDiff() (" + array1 + ")");
if (!Array.isArray(array1)) {
throw ("array1 is not an array (" + array1 + ")");
}
if (array2.constructor.name != 'Array') {
throw ("array2 is not an array in Zotero.Utilities.arrayDiff() (" + array2 + ")");
if (!Array.isArray(array2)) {
throw ("array2 is not an array (" + array2 + ")");
}
var val, pos, vals = [];
@ -575,6 +575,7 @@ Zotero.Utilities = {
return vals;
},
/**
* Return new array with duplicate values removed
*