Skip to content

Commit 44c4b2f

Browse files
Merge pull request #2644 from MicrosoftDocs/main638847397513213382sync_temp
For protected branch, push strategy should use PR and merge to target branch method to work around git push error
2 parents 2ee74cc + 2f581ad commit 44c4b2f

File tree

1 file changed

+50
-7
lines changed

1 file changed

+50
-7
lines changed

data-explorer/kusto/query/scan-operator.md

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: scan operator
33
description: Learn how to use the scan operator to scan data, match, and build sequences based on the predicates.
44
ms.reviewer: alexans
55
ms.topic: reference
6-
ms.date: 01/22/2025
6+
ms.date: 05/30/2025
77
---
88
# scan operator
99

@@ -71,7 +71,7 @@ Each input record is evaluated against all of the steps in reverse order, from t
7171
7272
* **Check 2:** If the state of *s_k* has an active sequence or *s_k* is the first step, and *r* meets the *Condition* of *s_k*, then a match occurs. The match leads to the following actions:
7373
1. The assignments of *s_k* are calculated and extend *r*.
74-
2. The values that represent *s_k* in the state of *s_k* are replaced with the values of the extended *r*.
74+
1. The values that represent *s_k* in the state of *s_k* are replaced with the values of the extended *r*.
7575
1. If *s_k* is defined as `output=all`, the extended *r* is added to the output.
7676
1. If *s_k* is the first step, a new sequence begins and the match ID increases by `1`. This only affects the output when `with_match_id` is used.
7777

@@ -82,7 +82,7 @@ For a detailed example of this logic, see the [scan logic walkthrough](#scan-log
8282
## Examples
8383

8484
The example in this section shows how to use the syntax to help you get started.
85-
85+
8686
[!INCLUDE [help-cluster](../includes/help-cluster-note.md)]
8787

8888
### Cumulative sum
@@ -230,6 +230,50 @@ Events
230230
|00:41:00|E|00:32:00|1|
231231
|01:15:00|A|01:15:00|2|
232232

233+
### Calculating Session Length per User
234+
235+
Calculate the session start time, end time, and duration for each user's session using the `scan` operator. A session is defined as a period between a user's login and the subsequent logout. By combining `partition` and `scan` with `output=none` and `output=all`, this pattern ensures that a **single row is returned per session** (i.e., per login/logout pair), rather than a row per event.
236+
237+
The logic works by:
238+
239+
* In step s1: Capturing the login timestamp using a scan step with `output=none`
240+
* In step s2: Emitting a row only when a matching logout is found using `output=all`
241+
242+
:::moniker range="azure-data-explorer"
243+
> [!div class="nextstepaction"]
244+
> <a href="https://dataexplorer.azure.com/clusters/help/databases/Samples?query=H4sIAAAAAAAAA41Sz2%2BCMBS%2Bk%2FA%2FvHiCBA0tkmkNO7nDkh29LTtUbLALFkIfLib74%2FdAoWYuTjhA%2B74fr19fqRDeqsK%2BHJVBCxnsJNK7LVWw0QdlUR5qQXsKaRVBa1XzuhbaYAQ9ZXOqlbDYaFOEvvfue0DPgA94zOfTOJ3yBcQLwVLBkzACFsGkrAptJtFdfBIThfD8IfxSsFjwJ8Inj%2BAZ7%2FTnqeunavEugRFaJNw19B8hETwVbOk66gi%2B97HyPRe5732DrRqE7emSbgRj8l2xlg1q1JWBPcU%2Bo6zJqzhlRqI%2BKkeD4NzKIDaKgLQ5nGtklUsDO5WXslEQUL1BAe5%2BldldL62ylpzXLXnSV0C3bWtpQvjSuB8se1tUNVgGdMa6xcxURgk3IpBlw51A9gy9Lw3b2OPqtxAfhGRZ%2FqHTJXktZNms%2F%2B1PcC18c4brIkxH4qUBmuGwD72pPlWO44048RvBH%2FdgF3BCAwAA" target="_blank">Run the query</a>
245+
::: moniker-end
246+
247+
```kusto
248+
let LogsEvents = datatable(Timestamp:datetime, userID:int, EventType:string)
249+
[
250+
datetime(2024-05-28 08:15:23), 1, "login",
251+
datetime(2024-05-28 08:30:15), 2, "login",
252+
datetime(2024-05-28 09:10:27), 3, "login",
253+
datetime(2024-05-28 12:30:45), 1, "logout",
254+
datetime(2024-05-28 11:45:32), 2, "logout",
255+
datetime(2024-05-28 13:25:19), 3, "logout"
256+
];
257+
LogsEvents
258+
| sort by userID, Timestamp
259+
| partition hint.strategy=native by userID (
260+
sort by Timestamp asc
261+
| scan declare (start: datetime, end: datetime, sessionDuration: timespan) with (
262+
step s1 output=none: EventType == "login" => start = Timestamp;
263+
step s2 output=all: EventType == "logout" => start = s1.start, end = Timestamp, sessionDuration = Timestamp - s1.start;
264+
)
265+
)
266+
| project start, end, userID, sessionDuration
267+
```
268+
269+
**Output**
270+
271+
| userID | start | end | sessionDuration |
272+
|--------|--------------------------|---------------------------|-----------------|
273+
| 1 | 2024-05-28 08:15:23.0000 | 2024-05-28 12:30:45.0000 | 04:15:22 |
274+
| 3 | 2024-05-28 09:10:27.0000 | 2024-05-28 13:25:19.0000 | 04:14:52 |
275+
| 2 | 2024-05-28 08:30:15.0000 | 2024-05-28 11:45:32.0000 | 03:15:17 |
276+
233277
### Events between Start and Stop
234278

235279
Find all sequences of events between the event `Start` and the event `Stop` that occur within 5 minutes. Assign a match ID for each sequence.
@@ -369,7 +413,7 @@ The "X" indicates that a specific field is irrelevant for that step.
369413
This section follows the [matching logic](#matching-logic) through each record of the `Events` table, explaining the transformation of the state and output at each step.
370414

371415
> [!NOTE]
372-
> An input record is evaluated against the steps in reverse order, from the last step (`s3`) to the first step (`s1`).
416+
> An input record is evaluated against the steps in reverse order, from the last step (`s3`) to the first step (`s1`).
373417
374418
#### Record 1
375419

@@ -431,7 +475,7 @@ This section follows the [matching logic](#matching-logic) through each record o
431475
|s2|0|00:01:00|"Start"|00:02:00|"B"|X|X|
432476
|s3||||||||
433477

434-
#### Record 4
478+
#### Record 4
435479

436480
|Ts|Event|
437481
|---|---|
@@ -463,7 +507,6 @@ This section follows the [matching logic](#matching-logic) through each record o
463507
* `s2`: **Check 1** isn't passed because the state of `s1` is empty, and **Check 2** isn't passed because `s2` lacks an active sequence.
464508
* `s1`: **Check 1** is irrelevant because there's no previous step. **Check 2** isn't passed because the record doesn't meet the condition of `Event == "Start"`.
465509

466-
467510
**State:**
468511

469512
|step|m_id|s1.Ts|s1.Event|s2.Ts|s2.Event|s3.Ts|s3.Event|
@@ -502,7 +545,7 @@ This section follows the [matching logic](#matching-logic) through each record o
502545

503546
* `s3`: **Check 1** isn't passed because the state of `s2` is empty, and **Check 2** isn't passed because it doesn't meet the condition of `Event == "Stop"`.
504547
* `s2`: **Check 1** isn't passed because the state of `s1` is empty, and **Check 2** isn't passed because `s2` lacks an active sequence.
505-
* `s1`: **Check 1** isn't passed because there's no previous step. it passes **Check 2** because it meets the condition of `Event == "Start"`. This match initiates a new sequence in `s1` with a new `m_id`. **Record 7** and its `m_id` (`1`) are added to the state and the output.
548+
* `s1`: **Check 1** isn't passed because there's no previous step. it passes **Check 2** because it meets the condition of `Event == "Start"`. This match initiates a new sequence in `s1` with a new `m_id`. **Record 7** and its `m_id` (`1`) are added to the state and the output.
506549

507550
**State:**
508551

0 commit comments

Comments
 (0)