Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,7 @@
## 2025-05-15 - [Optimization with Aggregation Pipelines]
**Learning:** This codebase frequently uses N+1 query patterns in service methods (e.g., looping through results and calling find_one). These can be significantly optimized using MongoDB aggregation pipelines with $lookup. However, mongomock (used in the test suite) has limited support for advanced $lookup features like 'let' and sub-pipelines.
**Action:** Use simple $lookup (localField/foreignField) when possible to maintain test compatibility, and handle any additional filtering or data processing in Python if necessary, which still provides a massive performance win by reducing database roundtrips to 1.

## 2026-02-27 - [Batching Polymorphic ID Counts]
**Learning:** In this codebase, `ticket_id` (and similar references) can be stored as either a string or an `ObjectId`. When batch-counting associated documents (like messages for tickets), the `$match` filter must include both representations in the `$in` list to ensure no data is missed, and results must be aggregated by the string representation in Python to merge counts from both formats.
**Action:** Use `query_ids = list(all_ids) + [ObjectId(tid) for tid in all_ids]` in `$match` and handle string-key mapping when processing aggregation results.
46 changes: 35 additions & 11 deletions app/services/ticket_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -269,25 +269,49 @@ def get_tickets_by_user(self, username: str, role: str, handlungsfelder: List[st
all_tickets = list(all_tickets)
logger.debug(f"Alle Tickets gefunden: {len(all_tickets)}")

# Nachrichtenanzahl und Auftragsdetails hinzufügen
# Nachrichtenanzahl und Auftragsdetails hinzufügen (Optimiert mit Batch-Abfrage Bolt ⚡)
logger.debug(f"Verarbeite {len(open_tickets)} offene, {len(assigned_tickets)} zugewiesene, {len(all_tickets)} alle Tickets")

# Alle Ticket-IDs sammeln für Batch-Abfrage
all_ticket_ids = set()
for ticket_list in [open_tickets, assigned_tickets, all_tickets]:
for ticket in ticket_list:
all_ticket_ids.add(str(ticket['_id']))

# Message-Counts in einem Rutsch laden
message_counts = {}
if all_ticket_ids:
try:
# Sammle sowohl String- als auch ObjectId-Repräsentationen
query_ids = list(all_ticket_ids)
try:
from bson import ObjectId
query_ids.extend([ObjectId(tid) for tid in all_ticket_ids if len(tid) == 24])
except Exception:
pass

pipeline = [
{'$match': {'ticket_id': {'$in': query_ids}}},
{'$group': {'_id': '$ticket_id', 'count': {'$sum': 1}}}
]

results = mongodb.aggregate('ticket_messages', pipeline)
for res in results:
tid = str(res['_id'])
message_counts[tid] = message_counts.get(tid, 0) + res['count']
except Exception as e:
logger.error(f"Fehler beim Batch-Laden der Nachrichtenanzahl: {e}")

for ticket_list in [open_tickets, assigned_tickets, all_tickets]:
for ticket in ticket_list:
logger.debug(f"Verarbeite Ticket: {ticket.get('title', 'Kein Titel')} (ID: {ticket.get('_id')})")

# ID-Feld für Template-Kompatibilität
ticket['id'] = str(ticket['_id'])
ticket_id_str = str(ticket['_id'])
ticket['id'] = ticket_id_str

# Nachrichtenanzahl laden (korrekte Collection)
# Unterstütze Messages, deren ticket_id als String oder ObjectId gespeichert ist
messages = mongodb.find('ticket_messages', {
'$or': [
{'ticket_id': str(ticket['_id'])},
{'ticket_id': ticket.get('_id')}
]
})
ticket['message_count'] = len(list(messages))
# Nachrichtenanzahl aus Batch-Ergebnis (Bolt ⚡)
ticket['message_count'] = message_counts.get(ticket_id_str, 0)

# Auftragsdetails laden (falls vorhanden)
if ticket.get('auftrag_details'):
Expand Down