# Useful JXA snippets

**URL:** <https://discourse.devontechnologies.com/t/useful-jxa-snippets/81635>\
**Category:** Tips\
**Tags:** automation\
**Created:** [December 21, 2024, 12:48pm UTC](https://discourse.devontechnologies.com/t/useful-jxa-snippets/81635 "2024-12-21T12:48:33Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![chrillek](https://discourse.devontechnologies.com/user_avatar/discourse.devontechnologies.com/chrillek/32/29340_2.png) [@chrillek](https://discourse.devontechnologies.com/u/chrillek)\
**Post date:** [December 21, 2024, 12:48pm UTC](https://discourse.devontechnologies.com/t/useful-jxa-snippets/81635/1 "2024-12-21T12:48:33Z")

</div>

Just in time for Christmas… Some JXA snippets that might be useful when writing JavaScript code for DT.

All snippets assume that `app` is defined as  
`app = Application('DEVONthink 3');` and `r` is a DT record.

## Access database by name

```javascript
const db = app.databases["MyDatabase"];

```

How it works: `databases` is a top-level collection of `Elements` of the DT app. Every database has a name and can be accessed as `app.databases.byName("MyDatabase")`. The array notation `["MyDatabase"]` is a shorthand for `byName("MyDatabase")`.

## Consider only a certain type from the selected records

```javascript
const MDRecords = app.selectedRecords().filter(r => 
  r.type() === 'markdown');

```

How it works: `selectedRecords()` is an array containing the currently selected records. Since it provides all array methods, you can use `filter` with a condition. Only those records meeting the condition are returned by `filter`.

## Consider several types from the selected records

```javascript
const HTMLRecords = app.selectedRecords().filter(r => 
  ["HMTL", "formatted note"].includes(r.type());

```

How it works: See above for `filter`. The condition here uses a literal array `["HTML", "formatted note"]` and its `includes` method. `includes` returns `true` if its parameter (`r.type()`) is contained in the array. Therefore, `filter` will return only records of type “HTML” and “formatted note”.

## Get the names of all selected records as an array

```javascript
const names = app.selectedRecords.name();

```

How it works: `selectedRecords` is an Object Specifier, **not** and array, since it is used without appended parenthesis. Therefore, you can dereference its properties, in this case `name`. The parenthesis appended to this property convert it to an array. This method works with all record properties.

## Find all headings in a Markdown record

```javascript
const headings = MDRecord.plainText().split("\n").filter(para =>
 para.startsWith('#'));

```

How it works: The `plainText` property of a markdown record contains the record’s text, which is converted to a string by using the parenthesis. It is then `split` at every newline, which gives us an array of the text’s paragraphs. The `filter` condition is true only for paragraphs beginning with a pound sign, thus a markdown headline. Finally, `headings` is an array containing all the headlines.

## Add tag to record

```javascript
record.tags = record.tags().concat("newTag");

```

How it works: `tags()` returns an array of the record’s tags, and `concat` appends “newTag” to it. Assigning this array to `record.tags` updates the records tags.

## How _not_ to add tag to record

**This code does not do what you might think it should do**

```javascript
record.tags = record.tags().push("newTag");

```

Why it **does not work** : The `push()` method appends its parameter to the array. But it does **not return** the array, only its length. Therefore, the code would try to set the `records.tags` property to an integer, which is not possible, since `tags` expects an array.

## Delete tag from record

```javascript
record.tags = record.tags().filter(t => 
  t !== 'removeTag');

```

How it works: As before, `filter` returns an array whose elements meet a certain condition. Here, the condition is that a tag is not equal to “removeTag”. Consequently, the array returned by `filter` contains all tags but “removeTag”.

## Get keywords from record as comma-separated string

```javascript
const metadata = r.metaData();
const keywords = ("kMDItemKeywords" in metadata) ? 
   metadata.kMDItemKeywords : "";

```

How it works: The `metaData()` property is an object possibly containing values from the Spotlight collection of metadata. `kMDItemKeywords` is an array in Spotlight, but DT stores it as a list of comma separated strings. The code returns this string if keywords are set for the record `r`, otherwise an empty string.

## Get keywords from record as an array

```javascript
const metadata = r.metaData();
const keywords = ("kMDItemKeywords" in metadata) ? 
   metadata.kMDItemKeywords.split(",") : [];

```

How it works: The code works mostly as described above. The only difference is that it returns an array with the keywords (or an empty one). This array is created by applying `split(",")` to the comma-separated string of keywords ;

## Set custom metadata

```javascript
app.addCustomMetaData("value", {for: "key", to: record});

```

How it works: That’s DT’s utility method to set custom metadata. It has the advantage to create the metadata “key” if it doesn’t exist already. The alternative approach

```javascript
const md = r.customMetaData();
md.key = "value";
r.customMetaData = md;

```

doesn’t work at all if there are no custom metadata values defined: `md` will be `undefined` in that case.

## Call shell commands

```javascript
const curApp = application.currentApplication();
curApp.includeStandardAdditions = true;
const file = "/Users/me/My Folder";
const result = curApp.doShellScript(`command '${file}'`)

```

How it works: `doShellScript` can only be called at the object returned by `application.currentApplication()`, and this object must have its property `includeStandardAdditions` to `true`. The simplest way to build the command for `doShellScript` is to use a template string included in `. If you want to pass one or more files to the shell command, include them in single quotes like above. That escapes special characters in filenames, like spaces. Unescaped, they would break the shell command.

---

<div class="post-metadata">

**Author:** ![korm](https://discourse.devontechnologies.com/letter_avatar_proxy/v4/letter/k/e0b2c6/32.png) [@korm](https://discourse.devontechnologies.com/u/korm)\
**Post date:** [December 21, 2024, 1:32pm UTC](https://discourse.devontechnologies.com/t/useful-jxa-snippets/81635/2 "2024-12-21T13:32:52Z")

</div>

What a gift! Thank you @chrillek 🙌

---

<div class="post-metadata">

**Author:** ![BLUEFROG](https://discourse.devontechnologies.com/user_avatar/discourse.devontechnologies.com/bluefrog/32/135_2.png) [@BLUEFROG](https://discourse.devontechnologies.com/u/BLUEFROG)\
**Post date:** [December 22, 2024, 1:30am UTC](https://discourse.devontechnologies.com/t/useful-jxa-snippets/81635/3 "2024-12-22T01:30:20Z")

</div>

Thanks for sharing some of these tidbits for the JXA-inclined.
