compendium shennanegins

TIL about CompendiumContent.getDocuments taking nedb queries.
game.packs.get('dnd5e.heroes').getDocuments({ 'data.abilities.con.value': {$gt: 14} });
game.packs.get('dnd5e.heroes').getDocuments({ 'data.abilities.con.value': {$gt: 14} });
More info about nedb queries: https://github.com/louischatriot/nedb#finding-documents
13 Replies
Calego
Calego3y ago
No description
Calego
Calego3y ago
The above returns all actors in the compendium with a constitution value greater than 14.
Ethaks
Ethaks3y ago
I vaguely remember Atro mentioning this on the mothership at some point, too. IIRC documents fetched by this are not cached though, which can make repeated calls a bit costly. (Not sure if the caching situation changed in the meantime, so I cannot say whether the warning still applies)
Calego
Calego3y ago
Yeah good call, the docs only mention caching for the singular getDocument
ccjmk
ccjmk3y ago
hmm even with no caching this might be useful for my module 🤔 i mean, its mostly working already, but half of the code is held together with ducttape and best intentions; it would probably be helpful if I use queries to say, get only Spells when I want spells, and only Classes when I want classes, and don't rely on the user doing things the proper way spoiler alert: they never do
Calego
Calego3y ago
@ccjmk do you use the compendium indexes for your thing?
Ethaks
Ethaks3y ago
The query option is why inquired about 5e's data usage a while back, but all the custom stuff you had to do made it a bit difficult for me to recommend this
ccjmk
ccjmk3y ago
ehhh to be honest, I .. think ? but I never understood that part deeply. Ghost helped me set up a function I could call with a compendium name, and it would get all the items on the compendium, ready to be added into an actor and I just parsed it after the fact as I needed this' Le Function
export async function getItemListFromPackListByNames(packNames: string[]) {
const allItems = [];
for (const compendiumName of packNames) {
const pack = game.packs.get(compendiumName);
const worldItems = game.items;
if (!worldItems) throw new Error('game.items not initialized yet');
if (!pack) ui.notifications?.warn(`No pack for name [${compendiumName}]!`);
if (pack?.documentName !== 'Item') throw new Error(`${compendiumName} is not an Item pack`);
const itemPack = pack as CompendiumCollection<CompendiumCollection.Metadata & { entity: 'Item' }>;
const itemsPromises: Promise<Item | null | undefined>[] = [];
for (const itemIndex of pack.index.keys()) {
const item = itemPack.getDocument(itemIndex);
itemsPromises.push(item);
}
const items = await Promise.all(itemsPromises);
allItems.push(
...items
.filter((item): item is Item => !!item)
.map((item) => {
const itemFromCompendium = worldItems.fromCompendium(item);
// intentionally adding the flag without using the API as I don't want to persist this flag
// this should be enough and more lightweight
itemFromCompendium.flags.hct = {
link: {
id: item.id,
pack: item.pack,
},
};
return itemFromCompendium;
}),
);
}
return allItems;
}
export async function getItemListFromPackListByNames(packNames: string[]) {
const allItems = [];
for (const compendiumName of packNames) {
const pack = game.packs.get(compendiumName);
const worldItems = game.items;
if (!worldItems) throw new Error('game.items not initialized yet');
if (!pack) ui.notifications?.warn(`No pack for name [${compendiumName}]!`);
if (pack?.documentName !== 'Item') throw new Error(`${compendiumName} is not an Item pack`);
const itemPack = pack as CompendiumCollection<CompendiumCollection.Metadata & { entity: 'Item' }>;
const itemsPromises: Promise<Item | null | undefined>[] = [];
for (const itemIndex of pack.index.keys()) {
const item = itemPack.getDocument(itemIndex);
itemsPromises.push(item);
}
const items = await Promise.all(itemsPromises);
allItems.push(
...items
.filter((item): item is Item => !!item)
.map((item) => {
const itemFromCompendium = worldItems.fromCompendium(item);
// intentionally adding the flag without using the API as I don't want to persist this flag
// this should be enough and more lightweight
itemFromCompendium.flags.hct = {
link: {
id: item.id,
pack: item.pack,
},
};
return itemFromCompendium;
}),
);
}
return allItems;
}
given that its iterating over const itemIndex of pack.index.keys() I think the answer is "technically yes"
Calego
Calego3y ago
Looks like yes but also no Lol
ccjmk
ccjmk3y ago
Schrodinger's Indexes
Calego
Calego3y ago
Have a look at the getIndex method on compendium collection It lets you build and cache a slimmed down version of a compendium's documents locally So for instance if you need to display all the spells and their levels, you can add spell level to the index Instead of parsing all spells from the compendium itself
ccjmk
ccjmk3y ago
hmm but I will need the full item anyway later, though if getting trimmed versions of documents is performant enough, I guess I could get the minimum I need for displaying/sorting data, and search it on-demand when the user actually submits the actor to be created (which is usually just name, img, requirement, and for spells, level + I imagine index to re-find the appropriate documents)
Calego
Calego3y ago
right, later you get the full items, but only the ones you actually need