Enhance getGifts method in FalukantService with detailed performance metrics and optimized data retrieval
- Implemented timing metrics to track performance across various steps of the getGifts method, improving traceability. - Refactored data loading to optimize user, character, and relationship queries, ensuring only necessary fields are fetched. - Enhanced error handling and logging for better debugging and performance insights during execution.
This commit is contained in:
@@ -3566,115 +3566,140 @@ class FalukantService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getGifts(hashedUserId) {
|
async getGifts(hashedUserId) {
|
||||||
// 1) User & Character optimiert laden (nur benötigte Felder)
|
const startTime = Date.now();
|
||||||
const user = await FalukantUser.findOne({
|
const timings = {};
|
||||||
include: [
|
|
||||||
{ model: User, as: 'user', attributes: ['hashedId'], where: { hashedId: hashedUserId } },
|
try {
|
||||||
{
|
// 1) User & Character optimiert laden (nur benötigte Felder)
|
||||||
model: FalukantCharacter,
|
const step1Start = Date.now();
|
||||||
as: 'character',
|
const user = await FalukantUser.findOne({
|
||||||
attributes: ['id', 'titleOfNobility'],
|
include: [
|
||||||
required: true
|
{ model: User, as: 'user', attributes: ['hashedId'], where: { hashedId: hashedUserId } },
|
||||||
}
|
{
|
||||||
]
|
model: FalukantCharacter,
|
||||||
});
|
as: 'character',
|
||||||
if (!user) throw new Error('User not found');
|
attributes: ['id', 'titleOfNobility'],
|
||||||
const myChar = user.character;
|
required: true
|
||||||
if (!myChar) throw new Error('Character not found');
|
}
|
||||||
|
|
||||||
// 2) Beziehung finden und „anderen" Character bestimmen (optimiert: nur benötigte Felder)
|
|
||||||
const rel = await Relationship.findOne({
|
|
||||||
where: {
|
|
||||||
[Op.or]: [
|
|
||||||
{ character1Id: myChar.id },
|
|
||||||
{ character2Id: myChar.id }
|
|
||||||
]
|
]
|
||||||
},
|
});
|
||||||
include: [
|
if (!user) throw new Error('User not found');
|
||||||
{
|
const myChar = user.character;
|
||||||
model: FalukantCharacter,
|
if (!myChar) throw new Error('Character not found');
|
||||||
as: 'character1',
|
timings.step1_user_character = Date.now() - step1Start;
|
||||||
attributes: ['id', 'moodId'],
|
|
||||||
include: [{
|
// 2) Beziehung finden und „anderen" Character bestimmen (optimiert: nur benötigte Felder)
|
||||||
model: CharacterTrait,
|
const step2Start = Date.now();
|
||||||
as: 'traits',
|
const rel = await Relationship.findOne({
|
||||||
attributes: ['id'],
|
where: {
|
||||||
through: { attributes: [] },
|
[Op.or]: [
|
||||||
|
{ character1Id: myChar.id },
|
||||||
|
{ character2Id: myChar.id }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: FalukantCharacter,
|
||||||
|
as: 'character1',
|
||||||
|
attributes: ['id', 'moodId'],
|
||||||
|
include: [{
|
||||||
|
model: CharacterTrait,
|
||||||
|
as: 'traits',
|
||||||
|
attributes: ['id'],
|
||||||
|
through: { attributes: [] },
|
||||||
|
required: false
|
||||||
|
}],
|
||||||
required: false
|
required: false
|
||||||
}],
|
},
|
||||||
|
{
|
||||||
|
model: FalukantCharacter,
|
||||||
|
as: 'character2',
|
||||||
|
attributes: ['id', 'moodId'],
|
||||||
|
include: [{
|
||||||
|
model: CharacterTrait,
|
||||||
|
as: 'traits',
|
||||||
|
attributes: ['id'],
|
||||||
|
through: { attributes: [] },
|
||||||
|
required: false
|
||||||
|
}],
|
||||||
|
required: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
timings.step2_relationship = Date.now() - step2Start;
|
||||||
|
|
||||||
|
// 3) Wenn keine Beziehung gefunden, alle Gifts ohne Filter zurückgeben
|
||||||
|
const step3Start = Date.now();
|
||||||
|
let relatedTraitIds = [];
|
||||||
|
let relatedMoodId = null;
|
||||||
|
|
||||||
|
if (rel) {
|
||||||
|
const relatedChar = rel.character1.id === myChar.id ? rel.character2 : rel.character1;
|
||||||
|
// Trait-IDs und Mood des relatedChar
|
||||||
|
relatedTraitIds = relatedChar.traits ? relatedChar.traits.map(t => t.id) : [];
|
||||||
|
relatedMoodId = relatedChar.moodId;
|
||||||
|
}
|
||||||
|
timings.step3_process_relationship = Date.now() - step3Start;
|
||||||
|
|
||||||
|
// 4) Gifts laden – mit Mood/Trait-Filter nur wenn Beziehung existiert
|
||||||
|
const step4Start = Date.now();
|
||||||
|
const giftIncludes = [
|
||||||
|
{
|
||||||
|
model: PromotionalGiftMood,
|
||||||
|
as: 'promotionalgiftmoods',
|
||||||
|
attributes: ['mood_id', 'suitability'],
|
||||||
required: false
|
required: false
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
model: FalukantCharacter,
|
model: PromotionalGiftCharacterTrait,
|
||||||
as: 'character2',
|
as: 'characterTraits',
|
||||||
attributes: ['id', 'moodId'],
|
attributes: ['trait_id', 'suitability'],
|
||||||
include: [{
|
|
||||||
model: CharacterTrait,
|
|
||||||
as: 'traits',
|
|
||||||
attributes: ['id'],
|
|
||||||
through: { attributes: [] },
|
|
||||||
required: false
|
|
||||||
}],
|
|
||||||
required: false
|
required: false
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
});
|
|
||||||
|
|
||||||
// 3) Wenn keine Beziehung gefunden, alle Gifts ohne Filter zurückgeben
|
// Wenn Beziehung existiert, Filter anwenden
|
||||||
let relatedTraitIds = [];
|
if (rel && relatedMoodId) {
|
||||||
let relatedMoodId = null;
|
giftIncludes[0].where = { mood_id: relatedMoodId };
|
||||||
|
|
||||||
if (rel) {
|
|
||||||
const relatedChar = rel.character1.id === myChar.id ? rel.character2 : rel.character1;
|
|
||||||
// Trait-IDs und Mood des relatedChar
|
|
||||||
relatedTraitIds = relatedChar.traits ? relatedChar.traits.map(t => t.id) : [];
|
|
||||||
relatedMoodId = relatedChar.moodId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4) Gifts laden – mit Mood/Trait-Filter nur wenn Beziehung existiert
|
|
||||||
const giftIncludes = [
|
|
||||||
{
|
|
||||||
model: PromotionalGiftMood,
|
|
||||||
as: 'promotionalgiftmoods',
|
|
||||||
attributes: ['mood_id', 'suitability'],
|
|
||||||
required: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
model: PromotionalGiftCharacterTrait,
|
|
||||||
as: 'characterTraits',
|
|
||||||
attributes: ['trait_id', 'suitability'],
|
|
||||||
required: false
|
|
||||||
}
|
}
|
||||||
];
|
if (rel && relatedTraitIds.length > 0) {
|
||||||
|
giftIncludes[1].where = { trait_id: { [Op.in]: relatedTraitIds } };
|
||||||
|
}
|
||||||
|
timings.step4_prepare_gift_includes = Date.now() - step4Start;
|
||||||
|
|
||||||
// Wenn Beziehung existiert, Filter anwenden
|
// 5) Parallel: Gifts und lowestTitleOfNobility laden
|
||||||
if (rel && relatedMoodId) {
|
const step5Start = Date.now();
|
||||||
giftIncludes[0].where = { mood_id: relatedMoodId };
|
const [gifts, lowestTitleOfNobility] = await Promise.all([
|
||||||
|
PromotionalGift.findAll({
|
||||||
|
include: giftIncludes
|
||||||
|
}),
|
||||||
|
TitleOfNobility.findOne({ order: [['id', 'ASC']] })
|
||||||
|
]);
|
||||||
|
timings.step5_load_gifts_and_title = Date.now() - step5Start;
|
||||||
|
|
||||||
|
// 6) Kosten berechnen (getGiftCost ist synchron, kein await nötig)
|
||||||
|
const step6Start = Date.now();
|
||||||
|
const result = gifts.map(gift => ({
|
||||||
|
id: gift.id,
|
||||||
|
name: gift.name,
|
||||||
|
cost: this.getGiftCost(
|
||||||
|
gift.value,
|
||||||
|
myChar.titleOfNobility,
|
||||||
|
lowestTitleOfNobility.id
|
||||||
|
),
|
||||||
|
moodsAffects: gift.promotionalgiftmoods, // nur Einträge mit relatedMoodId
|
||||||
|
charactersAffects: gift.characterTraits // nur Einträge mit relatedTraitIds
|
||||||
|
}));
|
||||||
|
timings.step6_calculate_costs = Date.now() - step6Start;
|
||||||
|
|
||||||
|
const totalTime = Date.now() - startTime;
|
||||||
|
console.log(`[getGifts] Performance: ${totalTime}ms total`, timings);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('[getGifts] Error:', error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
if (rel && relatedTraitIds.length > 0) {
|
|
||||||
giftIncludes[1].where = { trait_id: { [Op.in]: relatedTraitIds } };
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5) Parallel: Gifts und lowestTitleOfNobility laden
|
|
||||||
const [gifts, lowestTitleOfNobility] = await Promise.all([
|
|
||||||
PromotionalGift.findAll({
|
|
||||||
include: giftIncludes
|
|
||||||
}),
|
|
||||||
TitleOfNobility.findOne({ order: [['id', 'ASC']] })
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 6) Kosten berechnen (getGiftCost ist synchron, kein await nötig)
|
|
||||||
return gifts.map(gift => ({
|
|
||||||
id: gift.id,
|
|
||||||
name: gift.name,
|
|
||||||
cost: this.getGiftCost(
|
|
||||||
gift.value,
|
|
||||||
myChar.titleOfNobility,
|
|
||||||
lowestTitleOfNobility.id
|
|
||||||
),
|
|
||||||
moodsAffects: gift.promotionalgiftmoods, // nur Einträge mit relatedMoodId
|
|
||||||
charactersAffects: gift.characterTraits // nur Einträge mit relatedTraitIds
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async getChildren(hashedUserId) {
|
async getChildren(hashedUserId) {
|
||||||
|
|||||||
Reference in New Issue
Block a user