KDA: Echoes of Deception - Onboarding

Part of the "Kusto Detective Agency" series:

  1. Kusto Detective Agency - Intro
  2. KDA: Echoes of Deception - Onboarding
Page content

I’ll walk through the KDA UI and tackle the first challenge before diving into the real cases.

The UI layout ๐Ÿ”—︎

This is what your layout might look like:

Kusto Detective Agency layout

  1. LHS menu where you can switch challenges.
  2. All currently available cases. You’ll start with one and unlock new ones. Ordered like a mailbox, with the oldest at the bottom.
  3. Flavor text introducing the challenge.
  4. Ingestion script to load the data into your personal cluster.
  5. The main question that needs to be answered.
  6. Answer field โ€” usually includes hints about the expected answer format.
  7. Three hints โ€” IIRC, not all are available from the start; you might need to wait before requesting a hint.
  8. Training section โ€” introduces concepts and simpler challenges that help you with the main one.

Solving the case ๐Ÿ”—︎

We need to answer this question:

Who is the detective that earned the most money in 2022?

We can see that only one table (DetectiveCases) was added in the ingestion section. Let’s take a look at its data.

DetectiveCases
| take 50

First 50 rows of DetectiveCases table

It looks like the Bounty is a dynamic property and is only assigned when the status is CaseOpened.

We can test that hypothesis:

DetectiveCases
| where isnotempty(Properties) 
| take 50
| extend toreal(Properties.Bounty)

Let’s also review all the rows for a single case.
I’ll use CASE_0521475 from the first result set.

DetectiveCases
| where CaseId == "CASE_0521475"

First 50 rows of DetectiveCases table

Only the CaseOpened status has a bounty.
I’m also assuming that only the first case solved receives the bounty.

The question specifies the year 2022.
I’ve verified that all values fall within this range.

DetectiveCases
| summarize min(Timestamp), max(Timestamp)

The correct approach is to add the time filter.
Always add the time filter first in Kusto queries to optimize performance due to partitioning.

Final query ๐Ÿ”—︎

Putting it all together:

Summary:

  • Find all solved cases in 2022 and get the first detective who solved each case.
    • Cases may have started in previous years, but only the solve date matters.
  • Self-join the data on CaseId and parse the bounty from the Properties column.
  • Summarize and sort bounties by detective.
DetectiveCases
| where Timestamp >= datetime(2022,1,1) and Timestamp < datetime(2023,1,1)
| where EventType == "CaseSolved"
| summarize arg_min(Timestamp, DetectiveId) by CaseId
| project-rename FirstSolver = DetectiveId
| lookup kind=inner
(
    DetectiveCases
    | where EventType == 'CaseOpened'
    | extend Bounty = toreal(Properties.Bounty)
    | where Bounty > 0
    | project CaseId, Bounty
) on CaseId
| summarize sum(Bounty) by FirstSolver
| top 3 by sum_Bounty desc

With the onboarding case solved, let’s move on to the first real case.

As always, thank you for reading.