JXA / get specific properties of all database records

Hi,

I’m trying to export some specific properties (e.g. UUID together with name) for all (group) entries within a database. I’ve accomplished to do so for all root entries by

const dT = Application('DEVONthink 3');
dT.includeStandardAdditions = true;	

const dbs = dT.databases();
for (const db of dbs) {
    var names = db.records.name();
    var uuid = db.records.uuid();
}

I also did manage to extend this approach recursively by repeating for all child entries:

...
for (var i = 0; i < db.records.length; i++)
    someFunction(db.records[i].children);
...

where in someFunction above steps are repeated and the function recursively calls itself. This exhibits however very poor performance.

My next idea was to get an array of all entries using the search function by

var list = dT.search("kind:group", {in: db.root()});

for which I could iterate through by

for (var i = 0; i < db.records.length; i++)
    console.log(list[I].name());

but was unable to apply .name() at once, i.e.

list.name()

It appears that list is of type object, while db.records is of type function. I’m note sure what I’m doing wrong here - help would be greatly appreciated.

Also, I was unable to find any structured documentation for DevonThink and JXA (available functions and objects…) - does something like that exist?

Thanks!

const list = dT.search(...) /* gives you a list of groups */
list.forEach(l => {
  console.log(l.name());
}

As to your code: there’s a typo here
console.log(list[I].name()); since I should be lowercase (as per the for above).

list is probably what it says it is, namely a list, aka an array. So list.name() makes no sense since name is not a method of the Array class.
Basically, you use parenthesis to get at the value of any of the properties of an object. To set it, you leave out the properties. E.g:

const t = record.plainText(); /* that gives you the text of a record (if there is one) */
record.plainText = "no text any more"; /* that sets the text of this record */

Use Script Editor, set the Language to “JavaScript” on the top left and choose “DEVONthink” from the list of “Application dictionaries” under the “File” menu (fourth entry, I’m not sure about the wording in the english locale). This is as structured as it gets, unfortunately.
You might find some hints here in the forum if you search for

Application("DEVONthink 3")

Oh, there’s one caveat with JXA in DT: You can’t use JavaScript in smart rules. Something in DT’s approach to scripting does not agree with JavaScript, so it does not work. Normal scripts though are (mostly) ok.

Welcome @winkeltreu

Also, I was unable to find any structured documentation for DevonThink and JXA (available functions and objects…) - does something like that exist?

No, there is no JXA-specific documentation outside what is found in the application dictionary in Script Editor. Just open DEVONthink’s dictionary and switch the language dropdown to JavaScript.

image

Great, thanks!

Fast batch fetching of the same property for all records in a collection depends on preserving the reference and delaying the function call.

i.e. the problem is this line

const dbs = dT.databases();

and in particular the () at the end, which you just need to drop.

This is what fast fetches of all names, or all uuids etc look like:

const dt3 = Application("DEVONthink 3");

const
    dbs = dt3.databases,
    names = dbs.name(),
    uuids = dbs.uuid();

The basics, of course, are here:

OS X 10.10 JavaScript for Automation Release Notes

Fleshing out the sample code a bit, you could combine your lists of names and UUIDS, and construct UUID ⇄ name dictionaries, in patterns like these:

(() => {
    "use strict";

    const main = () => {

        const dt3 = Application("DEVONthink 3");

        const
            dbs = dt3.databases,
            names = dbs.name(),
            uuids = dbs.uuid();

        console.log(names);
        console.log(uuids);

        const nameUUIDList = zipWith(
            name => uuid => ({
                name,
                uuid
            })
        )(names)(uuids);

        const
            nameByUUID = nameUUIDList
            .reduce((a, dict) =>
                Object.assign(
                    a, {
                        [dict.uuid]: dict.name
                    }
                ), {}
            );

        const uuidByName = nameUUIDList
            .reduce((a, dict) =>
                Object.assign(
                    a, {
                        [dict.name]: dict.uuid
                    }
                ), {}
            );

        // Two dictionaries.
        return [
            nameByUUID,
            uuidByName
        ];
    };

    // --------------------- GENERIC ---------------------

    // zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
    const zipWith = f =>
        // A list constructed by zipping with a
        // custom function, rather than with the
        // default tuple constructor.
        xs => ys => xs.map(
            (x, i) => f(x)(ys[i])
        ).slice(
            0, Math.min(xs.length, ys.length)
        );


    return JSON.stringify(
        main(),
        null, 2
    );
})();
3 Likes

Regarding documentation for JXA: you may already have found these resources, but for whatever it’s worth, here are a few of the best things I’ve found:

I also find it particularly helpful to search certain forums for JXA, to see what other people have discovered. The best places I’ve found so far are the Keyboard Maestro forum and Stack Overflow. Here are direct links to search for JXA in those two places:

If you use TypeScript, the following also looks useful:

I’ve looked for better JXA documentation many times, but there doesn’t seem to be – and it’s been that way for years. I personally would prefer to work in JavaScript than AppleScript, but based on many signs, I get the impression that JXA is never going to be well supported by Apple, and may be a dead end. The developers of Script Debugger described the situation in 2014 when JXA was introduced, and they haven’t changed changed their opinion since then. Also, if you look through Hacker News, you will find postings by another developer who implemented a very useful package (one that allows access to Apple events from Python, something else I’ve used) and bitterly gave up, and blamed AppleScript’s and JXA’s problems on certain people at Apple.

2 Likes

Whereas AppleScript didn’t receive any updates since ages, is not available on i*OS and badly supported/implemented/documented in current Apple software (eg Notes). Automation is a shambles. At least JavaScript is available in some apps across Apple’s platform, like Drafts.

Errr, I don’t disagree that AppleScript is bad too, and I didn’t mean my comments to suggest that AppleScript wasn’t all those things you wrote :slight_smile:

Thanks a lot for your answer, that was very helpful to me. I now do understand what went wrong before in terms of batch collection and your example was very useful!

Sorry, I didn’t mean to say that you were advertising AppleScript. I just wanted to point out that automation is in a bad state right now. With no hope for it to get better, unfortunately.

1 Like