Replaced expensive getActiveProperties with cheaper filter by inactive field

This commit is contained in:
Stefan Zermatten
2021-01-28 14:29:10 +02:00
parent fc03097ed8
commit 83f2047dbe
14 changed files with 147 additions and 197 deletions

View File

@@ -3,7 +3,7 @@ import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import SimpleSchema from 'simpl-schema'; import SimpleSchema from 'simpl-schema';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import Creatures from '/imports/api/creature/Creatures.js'; import Creatures from '/imports/api/creature/Creatures.js';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
export const recomputeDamageMultipliers = new ValidatedMethod({ export const recomputeDamageMultipliers = new ValidatedMethod({
@@ -31,9 +31,13 @@ export const recomputeDamageMultipliers = new ValidatedMethod({
export function recomputeDamageMultipliersById(creatureId){ export function recomputeDamageMultipliersById(creatureId){
if (!creatureId) throw 'Creature ID is required'; if (!creatureId) throw 'Creature ID is required';
let props = getActiveProperties({ let props = CreatureProperties.find({
ancestorId: creatureId, 'ancestors.id': creatureId,
filter: {type: 'damageMultiplier'}, type: 'damageMultiplier',
removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
// Count of how many weakness, resistances and immunities each damage type has // Count of how many weakness, resistances and immunities each damage type has

View File

@@ -1,86 +0,0 @@
import Creatures from '/imports/api/creature/Creatures.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
export default function getActiveProperties({
ancestorId,
filter = {},
options = {sort: {order: 1}},
includeUntoggled = false,
includeUnprepared = false,
includeUnequipped = false,
excludeAncestors,
}){
filter = getActivePropertyFilter({
ancestorId,
filter,
includeUntoggled,
includeUnprepared,
includeUnequipped,
excludeAncestors,
});
return CreatureProperties.find(filter, options).fetch();
}
export function getActivePropertyFilter({
ancestorId,
filter = {},
includeUntoggled = false,
includeUnprepared = false,
includeUnequipped = false,
excludeAncestors = [],
}){
if (!ancestorId){
throw 'Ancestor Id is required to get active properties'
}
// First get ids of disabled properties, unequiped items, unapplied buffs
let disabledAncestorsFilter = {
'ancestors.id': ancestorId,
$or: [
{disabled: true}, // Everything can be disabled
{applied: false}, // Buffs can be applied
],
};
if (!includeUnequipped){
disabledAncestorsFilter.$or.push({type: 'item', equipped: {$ne: true}});
}
if (!includeUntoggled){
disabledAncestorsFilter.$or.push({toggleResult: false});
}
if (!includeUnprepared){
disabledAncestorsFilter.$or.push({
type: 'spell',
prepared: {$ne: true},
alwaysPrepared: {$ne: true}
});
}
let disabledAncestorIds = CreatureProperties.find(disabledAncestorsFilter, {
fields: {_id: 1},
}).map(prop => prop._id);
// Then get the ids of creatures that are children of this creature
// to isolate their decendent properties
Creatures.find({
'ancestors.id': ancestorId,
}, {
fields: {_id: 1},
}).forEach(subCreature => {
disabledAncestorIds.push(subCreature._id);
});
// Get all the properties that are decendents of the ancestor of interest but
// aren't from the excluded decendents
if (filter['ancestors.id'] && Meteor.isClient){
console.warn('Filtering on ancestor id is ignored')
}
filter['ancestors.id'] = {
$eq: ancestorId,
$nin: disabledAncestorIds.concat(excludeAncestors),
};
// Get properties that aren't removed
filter.removed = {$ne: true};
// Don't include the disabled ancestors themselves either
filter._id = {
$nin: disabledAncestorIds,
}
return filter;
}

View File

@@ -3,7 +3,6 @@ import { ValidatedMethod } from 'meteor/mdg:validated-method';
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin'; import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
import Creatures from '/imports/api/creature/Creatures.js'; import Creatures from '/imports/api/creature/Creatures.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import getActiveProperties, { getActivePropertyFilter } from '/imports/api/creature/getActiveProperties.js';
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js'; import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js'; import { recomputeCreatureById } from '/imports/api/creature/computation/recomputeCreature.js';
@@ -43,11 +42,12 @@ const restCreature = new ValidatedMethod({
resetFilter = {$in: ['shortRest', 'longRest']} resetFilter = {$in: ['shortRest', 'longRest']}
} }
// Only apply to active properties // Only apply to active properties
let filter = getActivePropertyFilter({ let filter = {
filter: {reset: resetFilter}, 'ancestors.id': creatureId,
ancestorId: creatureId, reset: resetFilter,
includeUntoggled: true, removed: {$ne: true},
}); inactive: {$ne: true},
};
// update all attribute's damage // update all attribute's damage
filter.type = 'attribute'; filter.type = 'attribute';
CreatureProperties.update(filter, { CreatureProperties.update(filter, {
@@ -70,14 +70,18 @@ const restCreature = new ValidatedMethod({
}); });
// Reset half hit dice on a long rest, starting with the highest dice // Reset half hit dice on a long rest, starting with the highest dice
if (restType === 'longRest'){ if (restType === 'longRest'){
let hitDice = getActiveProperties({ let hitDice = CreatureProperties.find({
ancestorId: creatureId, 'ancestors.id': creatureId,
filter: {type: 'attribute', attributeType: 'hitDice'}, type: 'attribute',
options: {fields: { attributeType: 'hitDice',
removed: {$ne: true},
inactive: {$ne: true},
}, {
fields: {
hitDiceSize: 1, hitDiceSize: 1,
damage: 1, damage: 1,
value: 1, value: 1,
}}, }
}); });
// Use a collator to do sorting in natural order // Use a collator to do sorting in natural order
let collator = new Intl.Collator('en', { let collator = new Intl.Collator('en', {

View File

@@ -55,7 +55,7 @@ export default {
}, },
meteor: { meteor: {
name(){ name(){
let creature = Creatures.findOne(this.id); let creature = Creatures.findOne(this.id, {fields: {name: 1}});
return creature && creature.name; return creature && creature.name;
}, },
}, },

View File

@@ -114,7 +114,6 @@ import Creatures from '/imports/api/creature/Creatures.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue'; import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
import NoteCard from '/imports/ui/properties/components/persona/NoteCard.vue'; import NoteCard from '/imports/ui/properties/components/persona/NoteCard.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import Slots from '/imports/ui/creature/slots/Slots.vue'; import Slots from '/imports/ui/creature/slots/Slots.vue';
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue'; import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
@@ -171,9 +170,13 @@ export default {
return Creatures.findOne(this.creatureId); return Creatures.findOne(this.creatureId);
}, },
classLevels(){ classLevels(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': this.creatureId,
filter: {type: 'classLevel'}, type: 'classLevel',
removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
}, },
}, },

View File

@@ -16,7 +16,7 @@
</template> </template>
<script> <script>
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue'; import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.vue'; import FeatureCard from '/imports/ui/properties/components/features/FeatureCard.vue';
@@ -33,11 +33,13 @@ import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
}, },
meteor: { meteor: {
features(){ features(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': this.creatureId,
filter: { type: 'feature',
type: 'feature', removed: {$ne: true},
}, inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
}, },
}, },

View File

@@ -24,7 +24,7 @@
<script> <script>
import ColumnLayout from '/imports/ui/components/ColumnLayout.vue'; import ColumnLayout from '/imports/ui/components/ColumnLayout.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import SpellListCard from '/imports/ui/properties/components/spells/SpellListCard.vue'; import SpellListCard from '/imports/ui/properties/components/spells/SpellListCard.vue';
import SpellList from '/imports/ui/properties/components/spells/SpellList.vue'; import SpellList from '/imports/ui/properties/components/spells/SpellList.vue';
@@ -48,35 +48,42 @@ export default {
}}, }},
meteor: { meteor: {
spellLists(){ spellLists(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': this.creatureId,
filter: { type: 'spellList',
type: 'spellList', removed: {$ne: true},
}, inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
}, },
spellsWithoutList(){ spellsWithoutList(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': {
excludeAncestors: this.spellListIds, $eq: this.creatureId,
filter: { $nin: this.spellListIds,
type: 'spell',
},
options: {
sort: {
level: 1,
order: 1,
},
}, },
type: 'spell',
removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {
level: 1,
order: 1,
}
}); });
}, },
spellListsWithoutAncestorSpellLists(){ spellListsWithoutAncestorSpellLists(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': {
excludeAncestors: this.spellListIds, $eq: this.creatureId,
filter: { $nin: this.spellListIds,
type: 'spellList',
}, },
type: 'spellList',
removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
}, },
}, },

View File

@@ -322,7 +322,7 @@
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue'; import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue'; import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
import RestButton from '/imports/ui/creature/RestButton.vue'; import RestButton from '/imports/ui/creature/RestButton.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import castSpellWithSlot from '/imports/api/creature/actions/castSpellWithSlot.js'; import castSpellWithSlot from '/imports/api/creature/actions/castSpellWithSlot.js';
const getProperties = function(creature, filter,){ const getProperties = function(creature, filter,){
@@ -330,10 +330,11 @@
if (creature.settings.hideUnusedStats){ if (creature.settings.hideUnusedStats){
filter.hide = {$ne: true}; filter.hide = {$ne: true};
} }
return getActiveProperties({ filter['ancestors.id'] = creature._id;
ancestorId: creature._id, filter.removed = {$ne: true};
filter, filter.inactive = {$ne: true};
options: {sort: {order: 1}}, return CreatureProperties.find(filter, {
sort: {order: 1}
}); });
}; };
@@ -422,9 +423,12 @@
}, },
attacks(){ attacks(){
let props = getProperties(this.creature, {type: 'attack'}).map(attack => { let props = getProperties(this.creature, {type: 'attack'}).map(attack => {
attack.children = getActiveProperties({ attack.children = CreatureProperties.find({
ancestorId: attack._id, 'ancestors.id': attack._id,
options: {sort: {order: 1}}, removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
return attack; return attack;
}); });

View File

@@ -59,7 +59,6 @@ import {
softRemoveProperty, softRemoveProperty,
restoreProperty restoreProperty
} from '/imports/api/creature/CreatureProperties.js'; } from '/imports/api/creature/CreatureProperties.js';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js'; import getPropertyTitle from '/imports/ui/properties/shared/getPropertyTitle.js';
export default { export default {
@@ -122,15 +121,17 @@ export default {
}, },
meteor: { meteor: {
slots(){ slots(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.creatureId, 'ancestors.id': this.creatureId,
filter: { type: 'propertySlot',
type: 'propertySlot', $or: [
$or: [ {slotConditionResult: true},
{slotConditionResult: true}, {slotConditionResult: {$exists: false}},
{slotConditionResult: {$exists: false}}, ],
], removed: {$ne: true},
} inactive: {$ne: true},
}, {
sort: {order: 1}
}).map(slot => { }).map(slot => {
if ( if (
!this.showHiddenSlots && !this.showHiddenSlots &&

View File

@@ -90,8 +90,8 @@
<script> <script>
import { getPropertyName } from '/imports/constants/PROPERTIES.js'; import { getPropertyName } from '/imports/constants/PROPERTIES.js';
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js'; import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import doAction from '/imports/api/creature/actions/doAction.js'; import doAction from '/imports/api/creature/actions/doAction.js';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue'; import TreeNodeView from '/imports/ui/properties/treeNodeViews/TreeNodeView.vue';
import AttributeConsumedView from '/imports/ui/properties/components/actions/AttributeConsumedView.vue'; import AttributeConsumedView from '/imports/ui/properties/components/actions/AttributeConsumedView.vue';
import ItemConsumedView from '/imports/ui/properties/components/actions/ItemConsumedView.vue'; import ItemConsumedView from '/imports/ui/properties/components/actions/ItemConsumedView.vue';
@@ -161,10 +161,12 @@ export default {
}, },
meteor: { meteor: {
children(){ children(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.model._id, 'parent.id': this.model._id,
filter: {'parent.id': this.model._id}, removed: {$ne: true},
options: {sort: {order: 1}}, inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
}, },
}, },

View File

@@ -20,7 +20,7 @@
<script> <script>
import ItemTreeNode from '/imports/ui/properties/treeNodeViews/ItemTreeNode.vue'; import ItemTreeNode from '/imports/ui/properties/treeNodeViews/ItemTreeNode.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import { selectAmmoItem } from '/imports/api/creature/CreatureProperties.js'; import { selectAmmoItem } from '/imports/api/creature/CreatureProperties.js';
import { findIndex } from 'lodash'; import { findIndex } from 'lodash';
export default { export default {
@@ -39,15 +39,16 @@ export default {
}, },
meteor: { meteor: {
items(){ items(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.action.ancestors[0].id, 'ancestors.id': this.action.ancestors[0].id,
filter: { type: 'item',
tags: this.itemConsumed.tag, equipped: true,
equipped: true, tags: this.itemConsumed.tag,
}, removed: {$ne: true},
options: { inactive: {$ne: true},
fields: {equipped: false}, }, {
} sort: {order: 1},
fields: {equipped: false},
}); });
} }
}, },

View File

@@ -10,7 +10,7 @@
import Creatures from '/imports/api/creature/Creatures.js'; import Creatures from '/imports/api/creature/Creatures.js';
import { damageProperty } from '/imports/api/creature/CreatureProperties.js'; import { damageProperty } from '/imports/api/creature/CreatureProperties.js';
import HealthBarCard from '/imports/ui/properties/components/attributes/HealthBarCard.vue'; import HealthBarCard from '/imports/ui/properties/components/attributes/HealthBarCard.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
export default { export default {
components: { components: {
@@ -30,16 +30,17 @@
let creature = this.creature; let creature = this.creature;
if (!creature) return; if (!creature) return;
let filter = { let filter = {
'ancestors.id': creature._id,
type: 'attribute', type: 'attribute',
attributeType: 'healthBar', attributeType: 'healthBar',
removed: {$ne: true},
inactive: {$ne: true},
}; };
if (creature.settings.hideUnusedStats){ if (creature.settings.hideUnusedStats){
filter.hide = {$ne: true}; filter.hide = {$ne: true};
} }
return getActiveProperties({ return CreatureProperties.find(filter, {
ancestorId: creature._id, sort: {order: 1}
filter,
options: {sort: {order: 1}},
}); });
}, },
}, },

View File

@@ -60,7 +60,7 @@
<script> <script>
import ToolbarCard from '/imports/ui/components/ToolbarCard.vue'; import ToolbarCard from '/imports/ui/components/ToolbarCard.vue';
import SpellList from '/imports/ui/properties/components/spells/SpellList.vue'; import SpellList from '/imports/ui/properties/components/spells/SpellList.vue';
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
export default { export default {
components: { components: {
@@ -79,29 +79,32 @@ export default {
}}, }},
meteor: { meteor: {
spells(){ spells(){
return getActiveProperties({ let filter = {
ancestorId: this.model._id, 'ancestors.id': this.model._id,
filter: { type: 'spell',
type: 'spell', removed: {$ne: true},
}, };
options: { if (this.preparingSpells){
sort: { filter.deactivatedByAncestor = {$ne: true};
level: 1, } else {
order: 1, filter.inactive = {$ne: true};
}, }
}, return CreatureProperties.find(filter, {
includeUnprepared: this.preparingSpells, sort: {
level: 1,
order: 1,
}
}); });
}, },
numPrepared(){ numPrepared(){
return getActiveProperties({ return CreatureProperties.find({
ancestorId: this.model._id, 'ancestors.id': this.model._id,
filter: { type: 'spell',
type: 'spell', removed: {$ne: true},
prepared: true, prepared: true,
alwaysPrepared: {$ne: true}, alwaysPrepared: {$ne: true},
}, deactivatedByAncestor: {$ne: true},
}).length; }).count();
}, },
preparedError(){ preparedError(){
if (!this.model.maxPrepared) return; if (!this.model.maxPrepared) return;

View File

@@ -9,14 +9,18 @@
</template> </template>
<script> <script>
import getActiveProperties from '/imports/api/creature/getActiveProperties.js'; import CreatureProperties from '/imports/api/creature/CreatureProperties.js';
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue'; import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
function getProperties(ancestorId, type){ function getProperties(ancestorId, type){
if (!ancestorId) return []; if (!ancestorId) return [];
return getActiveProperties({ return CreatureProperties.find({
ancestorId, 'ancestors.id': ancestorId,
filter: {type}, type,
removed: {$ne: true},
inactive: {$ne: true},
}, {
sort: {order: 1}
}); });
} }