Advanced Usage¶
This guide covers advanced features that help you get the most out of the ESO Logs API while minimizing API calls and maximizing performance.
Filter Expressions¶
Filter expressions are one of the most powerful features of the ESO Logs API. They allow you to write SQL-like queries to filter events, dramatically reducing the number of API calls needed and improving performance.
Why Use Filter Expressions?¶
Consider this common scenario: You want to track the uptime of multiple buffs during a fight. Without filter expressions, you'd need to make separate API calls for each buff:
# ❌ Inefficient: Multiple API calls
major_courage = await client.get_report_table(
code="xb7TKHXR8DJByp4Q",
fight_i_ds=[17],
ability_id=109966 # Major Courage
)
minor_courage = await client.get_report_table(
code="xb7TKHXR8DJByp4Q",
fight_i_ds=[17],
ability_id=109967 # Minor Courage
)
major_force = await client.get_report_table(
code="xb7TKHXR8DJByp4Q",
fight_i_ds=[17],
ability_id=40225 # Major Force
)
With filter expressions, you can get all this data in a single API call:
# ✅ Efficient: Single API call
events = await client.get_report_events(
code="xb7TKHXR8DJByp4Q",
fight_i_ds=[17],
filter_expression="type in ('applybuff', 'removebuff') and ability.id in (109966, 109967, 40225)"
)
Filter Expression Syntax¶
Filter expressions use a SQL-like syntax with the following operators:
Comparison Operators¶
=- Equals!=- Not equals<,>,<=,>=- Numeric comparisonsin- Value is in a listnot in- Value is not in a list
Logical Operators¶
and- Both conditions must be trueor- Either condition must be truenot- Negates a condition()- Group conditions
Available Fields¶
Common fields you can filter on:
Event Fields
type- Event type (damage, heal, applybuff, removebuff, death, etc.)timestamp- When the event occurred
Ability Fields
ability.id- Numeric ability IDability.name- Ability name (string)ability.type- Ability type
Actor Fields
source.id- Source actor IDsource.name- Source actor namesource.type- Actor type (Player, NPC, etc.)target.id- Target actor IDtarget.name- Target actor nametarget.type- Target actor type
Common Filter Expression Patterns¶
1. Track Multiple Buffs/Debuffs¶
# Get all buff/debuff events for specific abilities
filter_expr = """
type in ('applybuff', 'removebuff', 'applydebuff', 'removedebuff')
and ability.id in (109966, 109967, 40225, 61737)
"""
events = await client.get_report_events(
code=report_code,
filter_expression=filter_expr
)
2. Find Damage from Specific Source¶
# All damage done by a specific player
filter_expr = """
type = 'damage'
and source.name = 'PlayerName'
"""
# All damage done by players (not NPCs)
filter_expr = """
type = 'damage'
and source.type = 'Player'
"""
3. Track Specific Mechanics¶
# Find all applications of a specific debuff
filter_expr = """
type = 'applydebuff'
and ability.name = 'Baneful Mark'
"""
# Find deaths from specific ability
filter_expr = """
type = 'death'
and ability.name = 'Poison Injection'
"""
4. Complex Filtering¶
# Healing done by healers excluding self-healing
filter_expr = """
type = 'heal'
and source.type = 'Player'
and source.id != target.id
and source.name in ('Healer1', 'Healer2')
"""
# Major buffs on tanks only
filter_expr = """
type = 'applybuff'
and ability.name like 'Major %'
and target.name in ('Tank1', 'Tank2')
"""
Complete Example: Buff Uptime Tracker¶
Here's a complete example that tracks buff uptimes efficiently:
import asyncio
from typing import Dict, List
from esologs import Client
from esologs.auth import get_access_token
async def calculate_buff_uptimes(
events: List[Dict],
start_time: float,
end_time: float
) -> Dict[int, Dict[int, float]]:
"""Calculate buff uptimes from event data."""
# Track active buffs: source_id -> ability_id -> timestamp
active_buffs: Dict[int, Dict[int, float]] = {}
# Track total uptimes: source_id -> ability_id -> seconds
uptimes: Dict[int, Dict[int, float]] = {}
for event in events:
source_id = event.get('sourceID', 0)
ability_id = event.get('abilityGameID', 0)
timestamp = event.get('timestamp', 0)
event_type = event.get('type', '')
if source_id == 0 or ability_id == 0:
continue
# Initialize tracking dicts
if source_id not in active_buffs:
active_buffs[source_id] = {}
uptimes[source_id] = {}
if ability_id not in uptimes[source_id]:
uptimes[source_id][ability_id] = 0.0
if event_type == 'applybuff':
# Start tracking this buff
active_buffs[source_id][ability_id] = timestamp
elif event_type == 'removebuff':
# Calculate duration and add to total
if ability_id in active_buffs[source_id]:
duration = (timestamp - active_buffs[source_id][ability_id]) / 1000.0
uptimes[source_id][ability_id] += duration
del active_buffs[source_id][ability_id]
# Handle buffs still active at fight end
for source_id, buffs in active_buffs.items():
for ability_id, start in buffs.items():
duration = (end_time - start) / 1000.0
uptimes[source_id][ability_id] += duration
return uptimes
async def main():
# Setup
token = get_access_token()
client = Client(
url="https://www.esologs.com/api/v2/client",
headers={"Authorization": f"Bearer {token}"}
)
# Report parameters
report_code = "xb7TKHXR8DJByp4Q"
fight_id = 17
start_time = 2614035
end_time = 2777081
# Track multiple buffs efficiently
buff_ids = [
109966, # Major Courage
109967, # Minor Courage
40225, # Major Force
61737, # Minor Force
]
# Build filter expression
filter_expr = f"""
type in ('applybuff', 'removebuff')
and ability.id in ({','.join(map(str, buff_ids))})
"""
# Single API call for all buffs
response = await client.get_report_events(
code=report_code,
fight_i_ds=[fight_id],
start_time=start_time,
end_time=end_time,
filter_expression=filter_expr
)
# Calculate uptimes
events = response.report_data.report.events.data
uptimes = await calculate_buff_uptimes(events, start_time, end_time)
# Display results
fight_duration = (end_time - start_time) / 1000.0
print(f"Fight Duration: {fight_duration:.1f}s\n")
for source_id, buffs in uptimes.items():
print(f"Player {source_id}:")
for ability_id, uptime in buffs.items():
percentage = (uptime / fight_duration) * 100
print(f" Ability {ability_id}: {uptime:.1f}s ({percentage:.1f}%)")
if __name__ == "__main__":
asyncio.run(main())
Output:
Fight Duration: 163.0s
Player 66:
Ability 109966: 62.5s (38.3%)
Player 17:
Ability 61737: 146.8s (90.0%)
Player 22:
Ability 61737: 88.0s (54.0%)
Player 67:
Ability 40225: 17.5s (10.7%)
Performance Benefits¶
Using filter expressions provides several performance benefits:
- Reduced API Calls: Get data for multiple conditions in one request
- Lower API Point Usage: Each API call consumes points based on data processed
- Faster Response Times: Server-side filtering is more efficient
- Simplified Code: Less code to maintain and debug
Tips for Writing Filter Expressions¶
Test with Small Datasets First: Start with short time ranges to verify your filter works correctly
Use Specific Conditions: The more specific your filter, the less data returned and processed
Combine Related Queries: If you need multiple types of related data, try to get them in one call
Watch for Typos: Filter expressions are strings, so typos won't be caught until runtime
Check API Documentation: The ESO Logs API documentation has the complete list of filterable fields
When to Use Filter Expressions¶
Filter expressions are ideal for: - Tracking multiple buffs/debuffs - Analyzing specific mechanics - Finding events from specific sources - Complex event analysis - Reducing API call count
They may not be necessary for: - Simple single-ability queries - When you need all events anyway - Initial data exploration (start broad, then filter)
Other Advanced Features¶
Pagination for Large Datasets¶
When dealing with reports that have thousands of events:
# Use limit and startTime for pagination
all_events = []
start_time = fight_start
batch_size = 1000
while True:
response = await client.get_report_events(
code=report_code,
start_time=start_time,
limit=batch_size
)
events = response.report_data.report.events.data
if not events:
break
all_events.extend(events)
# Get the timestamp of the last event for next batch
start_time = events[-1]['timestamp'] + 1
# Avoid infinite loops
if len(events) < batch_size:
break
Using Multiple Data Types¶
Some methods support multiple data types in a single call:
from esologs import EventDataType
# Get both damage and healing in one call
response = await client.get_report_events(
code=report_code,
filter_expression="type in ('damage', 'heal')"
)
Time-Window Analysis¶
Analyze specific phases of a fight:
# Boss phase 2 only (timestamps in milliseconds)
phase2_start = start_time + (60 * 1000) # 1 minute in
phase2_end = start_time + (180 * 1000) # 3 minutes in
events = await client.get_report_events(
code=report_code,
start_time=phase2_start,
end_time=phase2_end,
filter_expression="type = 'damage' and target.name = 'Boss Name'"
)
Next Steps¶
- Explore the API Reference for detailed method documentation
- Check out the examples directory for more complex usage patterns
- Join the ESO Logs Discord for API discussions and help