Set field value from "Extra" subfield

I imported a fairly large library from JabRef into Zotero a little while ago, and have since been working with the latter. I just noticed that, for some reason, the number of pages for my books and collections was not imported to the "# of pages" field, but was instead stored in the "Extra" field (among other pertinent information) as Pages:xxx (xxx=whatever the page count is for the item in question). As I have already been making changes and additions to my library in Zotero, and as my library is large enough that importing it takes about a day, changing the fields in BibTeX, wiping out all my entries, and importing them again is not an optimal solution. Is there a way I could selectively query my library for Books or Collections where the # of pages field is blank, and then have Zotero insert the value from the aforementioned "Pages:" line of the Extra field?

Thank you!
  • This looks like something that could be addressed using the JavaScript API (https://www.zotero.org/support/dev/client_coding/javascript_api). The only thing that I am not sure about is the proper syntax to extract the number of pages from the "Extra" field, which also contains other information. I am also not 100% sure on how to have the query limit itself to "Book" entries. I would be grateful if somebody could give me a bit of insight!
  • Assuming you have an item with extra:

    Pages: x-y
    Late Arrival: yes


    Then with this code

    let m
    const field = item.getField('extra').split('\n').reduce((acc, line) => {
    if (m = line.match(/^([A-Za-z ]+):(.*)/)) acc[m[1].trim().toLowerCase()] = m[2].trim()
    return acc
    }, {})


    you'll end up with an object field which looks like

    { pages: 'x-y', 'late arrival': yes }

    to limit to book entries you can test for item.itemTypeID === Zotero.ItemTypes.getID('book')
  • Thank you! That got me started down the correct path. Still, I seem to be doing something wrong, and I think the problem is somewhere in the regular expression. Do you think you can help? I am using the following, modified from your example and the one on the site:

    var fieldPageNum = "numPages";

    var fieldID = Zotero.ItemFields.getID(fieldPageNum);
    var s = new Zotero.Search();
    s.libraryID = Zotero.Libraries.userLibraryID;
    s.addCondition('itemTypeID', 'is', Zotero.ItemTypes.getID('book'));
    s.addCondition(fieldPageNum, 'doesNotContain', '%');
    var ids = await s.search();
    if (!ids.length) {
    return "No items found";
    } else {
    return ids.length + " item(s) need fixing.";
    };

    await Zotero.DB.executeTransaction(async function () {
    for (let id of ids) {
    let item = await Zotero.Items.getAsync(id);
    let curLine = item.getField('extra').split('\n');
    if (curLine = curLine.match(/^'Pages: '\d+/)) {
    var numPage = curline.match(/\d+/);
    let mappedFieldID = Zotero.ItemFields.getFieldIDFromTypeAndBase(item.itemTypeID, fieldPageNum);
    item.setField(mappedFieldID ? mappedFieldID : fieldID, numPage);
    await item.save();
    }
    }
    });
    return ids.length + " item(s) updated";
  • By the way, how do you indicate code in comments?
  • <code>stuff here</code>
  • Maybe something like

    var s = new Zotero.Search()
    s.libraryID = Zotero.Libraries.userLibraryID
    s.addCondition('itemTypeID', 'is', Zotero.ItemTypes.getID('book'))
    s.addCondition(fieldPageNum, 'doesNotContain', '%')
    var ids = await s.search()
    if (!ids.length) {
    return "No items found"
    } else {
    return `${ids.length} item(s) need fixing.`
    }

    await Zotero.DB.executeTransaction(async function() {
    for (let id of ids) {
    let item = await Zotero.Items.getAsync(id)
    for (var line of item.getField('extra').split('\n')) {
    const m = line.match(/^Pages: (\d+)$/)
    if (m) {
    item.setField('numPages', m[1])
    await item.save()
    }
    }
    }
    })
    return `${ids.length} item(s) updated`
  • That did it! Rather, that is doing it. According to the script, there are 533 entries to correct, but it seems to be working through them. Thank you so much for the help! For anyone else who has the same issue, I had to fix one call to the "numPages" field that I had set as a variable for some reason, so the working code is as follows (this also gives me a chance to use the code tag):


    var s = new Zotero.Search()
    s.libraryID = Zotero.Libraries.userLibraryID
    s.addCondition('itemTypeID', 'is', Zotero.ItemTypes.getID('book'))
    s.addCondition('numPages', 'doesNotContain', '%')
    var ids = await s.search()
    if (!ids.length) {
    return "No items found"
    } else {
    return `${ids.length} item(s) need fixing.`
    }

    await Zotero.DB.executeTransaction(async function() {
    for (let id of ids) {
    let item = await Zotero.Items.getAsync(id)
    for (var line of item.getField('extra').split('\n')) {
    const m = line.match(/^Pages: (\d+)$/)
    if (m) {
    item.setField('numPages', m[1])
    await item.save()
    }
    }
    }
    })
    return `${ids.length} item(s) updated`
Sign In or Register to comment.