mirror of
https://github.com/zotero/zotero.git
synced 2026-10-05 02:43:38 +00:00
Cache field lookups when loading item data
Zotero.Item#setField() in loadIn mode repeats the same lookups for every value (field ID, type-specific field, validity for the type, multiline), several of which allocate. Cache them per item type and field in Zotero.ItemFields._getLoadInfo(). The cache is rebuilt by ItemFields.init() and dropped if any method it depends on is replaced, so an overridden method is still consulted. Also look up each item type's fields once per load rather than once per item. setField() is still called for every value with the same arguments, so code that wraps it sees the same calls, and the loaded data is identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8c3b967bf9
commit
25621d3a21
3 changed files with 86 additions and 4 deletions
|
|
@ -794,7 +794,30 @@ Zotero.Item.prototype.setField = function (field, value, loadIn) {
|
|||
if (!itemTypeID) {
|
||||
throw new Error('Item type must be set before setting field data');
|
||||
}
|
||||
|
||||
|
||||
// Loading calls this for every stored value, so use cached field lookups.
|
||||
// Same result as the general path below.
|
||||
if (loadIn && typeof field == 'number') {
|
||||
let info = Zotero.ItemFields._getLoadInfo(itemTypeID, field);
|
||||
if (info && !info.isISBN) {
|
||||
if (field == info.titleID && this.isNote()) {
|
||||
this._noteTitle = value ? value : "";
|
||||
return true;
|
||||
}
|
||||
if (value !== false && !info.valid) {
|
||||
Zotero.debug("'" + field + "' is not a valid field for type '"
|
||||
+ Zotero.ItemTypes.getName(itemTypeID) + "'" + " -- ignoring value '" + value + "'", 2);
|
||||
return false;
|
||||
}
|
||||
if (typeof value == 'string' && !info.multiline
|
||||
&& (value.includes('\n') || value.includes('\r'))) {
|
||||
value = value.replace(/[\r\n]+/g, " ");
|
||||
}
|
||||
this._itemData[info.fieldID] = value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
var fieldID = Zotero.ItemFields.getID(field);
|
||||
if (!fieldID) {
|
||||
throw new Error('"' + field + '" is not a valid itemData field');
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@ Zotero.ItemFields = new function () {
|
|||
var _typeFieldNamesByBase = {};
|
||||
var _baseFieldIDsByTypeAndField = {};
|
||||
var _autocompleteFields = null;
|
||||
|
||||
var _loadInfo = [];
|
||||
var _loadInfoMethods = {};
|
||||
|
||||
// Privileged methods
|
||||
this.getName = getName;
|
||||
this.getID = getID;
|
||||
|
|
@ -59,6 +61,7 @@ Zotero.ItemFields = new function () {
|
|||
this.init = async function () {
|
||||
_fields = {};
|
||||
_fieldsFormats = [];
|
||||
_loadInfo = [];
|
||||
|
||||
var result = await Zotero.DB.queryAsync('SELECT * FROM fieldFormats');
|
||||
|
||||
|
|
@ -123,6 +126,57 @@ Zotero.ItemFields = new function () {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* The lookups Zotero.Item#setField() does for every value loaded from the
|
||||
* database, cached per item type and field
|
||||
*
|
||||
* The cache is dropped if any method it depends on is replaced, so an
|
||||
* overridden method is still consulted.
|
||||
*
|
||||
* @param {Integer} itemTypeID
|
||||
* @param {Integer} fieldID
|
||||
* @return {Object|false} - false for an unknown field
|
||||
*/
|
||||
this._getLoadInfo = function (itemTypeID, fieldID) {
|
||||
if (_loadInfoMethods.getID !== this.getID
|
||||
|| _loadInfoMethods.getFieldIDFromTypeAndBase !== this.getFieldIDFromTypeAndBase
|
||||
|| _loadInfoMethods.isValidForType !== this.isValidForType
|
||||
|| _loadInfoMethods.isMultiline !== this.isMultiline
|
||||
|| _loadInfoMethods.getItemTypeID !== Zotero.ItemTypes.getID) {
|
||||
_loadInfo = [];
|
||||
_loadInfoMethods = {
|
||||
getID: this.getID,
|
||||
getFieldIDFromTypeAndBase: this.getFieldIDFromTypeAndBase,
|
||||
isValidForType: this.isValidForType,
|
||||
isMultiline: this.isMultiline,
|
||||
getItemTypeID: Zotero.ItemTypes.getID
|
||||
};
|
||||
}
|
||||
var byField = _loadInfo[itemTypeID];
|
||||
if (!byField) {
|
||||
byField = _loadInfo[itemTypeID] = new Map();
|
||||
}
|
||||
var info = byField.get(fieldID);
|
||||
if (info === undefined) {
|
||||
if (!this.getID(fieldID)) {
|
||||
info = false;
|
||||
}
|
||||
else {
|
||||
let resolvedID = this.getFieldIDFromTypeAndBase(itemTypeID, fieldID) || fieldID;
|
||||
info = {
|
||||
fieldID: resolvedID,
|
||||
titleID: this.getID('title'),
|
||||
valid: this.isValidForType(resolvedID, itemTypeID),
|
||||
multiline: this.isMultiline(resolvedID),
|
||||
isISBN: resolvedID == this.getID('ISBN')
|
||||
};
|
||||
}
|
||||
byField.set(fieldID, info);
|
||||
}
|
||||
return info;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Return the fieldName for a passed fieldID or fieldName
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -298,6 +298,7 @@ Zotero.Items = function () {
|
|||
var sql = "SELECT itemID FROM items WHERE libraryID=?" + idSQL;
|
||||
var params = [libraryID];
|
||||
var allItemIDs = [];
|
||||
var fieldIDsByType = new Map();
|
||||
await Zotero.DB.queryAsync(
|
||||
sql,
|
||||
params,
|
||||
|
|
@ -306,9 +307,13 @@ Zotero.Items = function () {
|
|||
onRow: function (row) {
|
||||
let itemID = row.getResultByIndex(0);
|
||||
let item = this._objectCache[itemID];
|
||||
|
||||
|
||||
// Set nonexistent fields in the cache list to false (instead of null)
|
||||
let fieldIDs = Zotero.ItemFields.getItemTypeFields(item.itemTypeID);
|
||||
let fieldIDs = fieldIDsByType.get(item.itemTypeID);
|
||||
if (!fieldIDs) {
|
||||
fieldIDs = Zotero.ItemFields.getItemTypeFields(item.itemTypeID);
|
||||
fieldIDsByType.set(item.itemTypeID, fieldIDs);
|
||||
}
|
||||
for (let j=0; j<fieldIDs.length; j++) {
|
||||
let fieldID = fieldIDs[j];
|
||||
if (!itemFieldsCached[itemID] || !itemFieldsCached[itemID][fieldID]) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue