EQL 语法参考
编辑EQL 语法参考编辑
基本语法编辑
EQL 查询需要一个事件类别和一个匹配条件。 where
关键字将它们连接起来。
event_category where condition
事件类别是 事件类别字段 的索引值。默认情况下,EQL 搜索 API 使用 Elastic 通用架构 (ECS) 中的 event.category
字段。您可以使用 API 的 event_category_field
参数指定另一个事件类别字段。
例如,以下 EQL 查询匹配事件类别为 process
且 process.name
为 svchost.exe
的事件
process where process.name == "svchost.exe"
匹配任何事件类别编辑
要匹配任何类别的事件,请使用 any
关键字。您还可以使用 any
关键字搜索没有事件类别字段的文档。
例如,以下 EQL 查询匹配 network.protocol
字段值为 http
的任何文档
any where network.protocol == "http"
转义事件类别编辑
使用封闭双引号 ("
) 或三个封闭双引号 ("""
) 来转义以下事件类别:
- 包含特殊字符,例如连字符 (
-
) 或点 (.
) - 包含空格
- 以数字开头
".my.event.category" "my-event-category" "my event category" "6eventcategory" """.my.event.category""" """my-event-category""" """my event category""" """6eventcategory"""
转义字段名编辑
使用封闭反引号 (`) 来转义以下字段名:
- 包含连字符 (
-
) - 包含空格
- 以数字开头
`my-field` `my field` `6myfield`
使用双反引号 (``) 来转义字段名中的任何反引号 (`)。
my`field -> `my``field`
条件编辑
条件由事件必须匹配的一个或多个条件组成。您可以使用以下运算符指定和组合这些条件。默认情况下,大多数 EQL 运算符区分大小写。
比较运算符编辑
< <= == : != >= >
-
<
(小于) - 如果运算符左侧的值小于右侧的值,则返回
true
。否则返回false
。 -
<=
(小于或等于) - 如果运算符左侧的值小于或等于右侧的值,则返回
true
。否则返回false
。 -
==
(等于,区分大小写) - 如果运算符左侧和右侧的值相等,则返回
true
。否则返回false
。不支持通配符。 -
:
(等于,不区分大小写) - 如果运算符左侧和右侧的字符串相等,则返回
true
。否则返回false
。只能用于比较字符串。支持 通配符 和 列表查找。 -
!=
(不等于,区分大小写) - 如果运算符左侧和右侧的值不相等,则返回
true
。否则返回false
。不支持通配符。 -
>=
(大于或等于) - 如果运算符左侧的值大于或等于右侧的值,则返回
true
。否则返回false
。比较字符串时,运算符使用区分大小写的字典顺序。 -
>
(大于) - 如果运算符左侧的值大于右侧的值,则返回
true
。否则返回false
。比较字符串时,运算符使用区分大小写的字典顺序。
不支持 =
作为相等运算符。请改用 ==
或 :
。
模式比较关键字编辑
my_field like "VALUE*" // case-sensitive wildcard matching my_field like~ "value*" // case-insensitive wildcard matching my_field regex "VALUE[^Z].?" // case-sensitive regex matching my_field regex~ "value[^z].?" // case-insensitive regex matching
比较的限制编辑
您不能链接比较。请在比较之间使用 逻辑运算符。例如,不支持 foo < bar <= baz
。但是,您可以将表达式重写为 foo < bar and bar <= baz
,这是支持的。
您也不能将一个字段与另一个字段进行比较,即使使用 函数 更改了这些字段。
示例
以下 EQL 查询将 process.parent_name
字段值与静态值 foo
进行比较。支持此比较。
但是,该查询还将 process.parent.name
字段值与 process.name
字段进行了比较。不支持此比较,并且将为整个查询返回错误。
process where process.parent.name == "foo" and process.parent.name == process.name
相反,您可以重写查询以将 process.parent.name
和 process.name
字段与静态值进行比较。
process where process.parent.name == "foo" and process.name == "foo"
逻辑运算符编辑
and or not
-
and
- 仅当左侧和右侧的条件*都*返回
true
时,才返回true
。否则返回false
。 -
or
- 如果左侧或右侧的条件之一为
true
,则返回true
。否则返回false
。 -
not
- 如果右侧的条件为
false
,则返回true
。
查找运算符编辑
my_field in ("Value-1", "VALUE2", "VAL3") // case-sensitive my_field in~ ("value-1", "value2", "val3") // case-insensitive my_field not in ("Value-1", "VALUE2", "VAL3") // case-sensitive my_field not in~ ("value-1", "value2", "val3") // case-insensitive my_field : ("value-1", "value2", "val3") // case-insensitive my_field like ("Value-*", "VALUE2", "VAL?") // case-sensitive my_field like~ ("value-*", "value2", "val?") // case-insensitive my_field regex ("[vV]alue-[0-9]", "VALUE[^2].?", "VAL3") // case-sensitive my_field regex~ ("value-[0-9]", "value[^2].?", "val3") // case-insensitive
-
in
(区分大小写) - 如果该值包含在提供的列表中,则返回
true
。对于不区分大小写的匹配,请使用in~
。 -
not in
(区分大小写) - 如果该值未包含在提供的列表中,则返回
true
。对于不区分大小写的匹配,请使用not in~
。 -
:
(不区分大小写) - 如果该字符串包含在提供的列表中,则返回
true
。只能用于比较字符串。 -
like
(区分大小写) - 如果该字符串与提供的列表中的 通配符模式 匹配,则返回
true
。只能用于比较字符串。对于不区分大小写的匹配,请使用like~
。 -
regex
(区分大小写) - 如果该字符串与提供的列表中的正则表达式模式匹配,则返回
true
。有关支持的正则表达式语法,请参阅 正则表达式语法。只能用于比较字符串。对于不区分大小写的匹配,请使用regex~
。
数学运算符编辑
+ - * / %
-
+
(加) - 将运算符左侧和右侧的值相加。
-
-
(减) - 从左侧的值中减去右侧的值。
-
*
(乘) - 将运算符左侧和右侧的值相乘。
-
/
(除) -
将运算符左侧的值除以右侧的值。
如果被除数和除数都是整数,则除法 (
\
) 运算将*向下舍入*任何返回的浮点数到最接近的整数。为避免舍入,请将被除数或除数转换为浮点数。示例
process.args_count
字段是一个包含进程参数计数的long
整数字段。用户可能希望以下 EQL 查询仅匹配
process.args_count
值为4
的事件。process where ( 4 / process.args_count ) == 1
但是,EQL 查询匹配
process.args_count
值为3
或4
的事件。对于
process.args_count
值为3
的事件,除法运算返回浮点数1.333...
,该浮点数向下舍入为1
。要仅匹配
process.args_count
值为4
的事件,请将被除数或除数转换为浮点数。以下 EQL 查询将整数
4
更改为等效的浮点数4.0
。process where ( 4.0 / process.args_count ) == 1
-
%
(模) - 将运算符左侧的值除以右侧的值。仅返回余数。
匹配任何条件编辑
要仅根据事件类别匹配事件,请使用 where true
条件。
例如,以下 EQL 查询匹配任何 file
事件
file where true
要匹配任何事件,您可以将 any
关键字与 where true
条件组合使用
any where true
可选字段编辑
默认情况下,EQL 查询只能包含您正在搜索的数据集中存在的字段。如果一个字段具有 显式、动态 或 运行时 映射,则该字段存在于数据集中。如果 EQL 查询包含一个不存在的字段,则会返回错误。
如果您不确定数据集中是否存在某个字段,请使用 ?
运算符将该字段标记为可选字段。如果可选字段不存在,则查询会将其替换为 null
,而不是返回错误。
示例
在以下查询中,user.id
字段是可选的。
network where ?user.id != null
如果 user.id
字段存在于您要搜索的数据集中,则查询将匹配包含 user.id
值的任何 network
事件。如果数据集中不存在 user.id
字段,则 EQL 会将查询解释为
network where null != null
在这种情况下,查询不匹配任何事件。
检查字段是否存在编辑
要匹配包含某个字段的任何值的事件,请使用 !=
运算符将该字段与 null
进行比较
?my_field != null
要匹配不包含字段值的事件,请使用 ==
运算符将该字段与 null
进行比较
?my_field == null
字符串编辑
字符串用双引号 ("
) 括起来。
"hello world"
不支持用单引号 ('
) 括起来的字符串。
字符串中的转义字符编辑
当在字符串中使用时,特殊字符(例如回车符或双引号 ("
))必须在其前面加上反斜杠 (\
) 进行转义。
"example \r of \" escaped \n characters"
转义序列 | 字面字符 |
---|---|
|
换行符 |
|
回车符 |
|
制表符 |
|
反斜杠 ( |
|
双引号 ( |
您可以使用十六进制 \u{XXXXXXXX}
转义序列来转义 Unicode 字符。十六进制值可以是 2-8 个字符,并且不区分大小写。短于 8 个字符的值将用零填充。您可以使用这些转义序列在字符串中包含不可打印字符或从右到左 (RTL) 字符。例如,您可以将 从右到左标记 (RLM) 转义为 \u{200f}
、\u{200F}
或 \u{0000200f}
。
单引号 ('
) 字符保留供将来使用。您不能对字面字符串使用转义的单引号 (\'
)。请改用转义的双引号 (\"
)。
原始字符串编辑
原始字符串将特殊字符(例如反斜杠 (\
))视为字面字符。原始字符串用三个双引号 ("""
) 括起来。
"""Raw string with a literal double quote " and blackslash \ included"""
原始字符串不能包含三个连续的双引号 ("""
)。请改用带有 \"
转义序列的常规字符串。
"String containing \"\"\" three double quotes"
通配符编辑
对于使用 :
运算符或 like
关键字进行的字符串比较,您可以使用 *
和 ?
通配符来匹配特定模式。*
通配符匹配零个或多个字符
my_field : "doc*" // Matches "doc", "docs", or "document" but not "DOS" my_field : "*doc" // Matches "adoc" or "asciidoc" my_field : "d*c" // Matches "doc" or "disc" my_field like "DOC*" // Matches "DOC", "DOCS", "DOCs", or "DOCUMENT" but not "DOS" my_field like "D*C" // Matches "DOC", "DISC", or "DisC"
?
通配符匹配一个字符
my_field : "doc?" // Matches "docs" but not "doc", "document", or "DOS" my_field : "?doc" // Matches "adoc" but not "asciidoc" my_field : "d?c" // Matches "doc" but not "disc" my_field like "DOC?" // Matches "DOCS" or "DOCs" but not "DOC", "DOCUMENT", or "DOS" my_field like "D?c" // Matches "DOC" but not "DISC"
:
运算符和 like
关键字还支持在 列表查找 中使用通配符
my_field : ("doc*", "f*o", "ba?", "qux") my_field like ("Doc*", "F*O", "BA?", "QUX")
序列编辑
您可以使用 EQL 序列来描述和匹配一系列有序事件。序列中的每个项目都是一个事件类别和事件条件,用方括号 ([ ]
) 括起来。事件按时间顺序排列,最近的事件排在最后。
sequence [ event_category_1 where condition_1 ] [ event_category_2 where condition_2 ] ...
示例
以下 EQL 序列查询匹配此系列有序事件
-
从具有以下内容的事件开始
- 事件类别为
file
file.extension
为exe
- 事件类别为
- 后跟事件类别为
process
的事件
sequence [ file where file.extension == "exe" ] [ process where true ]
with maxspan
语句编辑
您可以使用 with maxspan
将序列限制为指定的时间跨度。匹配序列中的所有事件都必须在此持续时间内发生,从第一个事件的时间戳开始。
maxspan
接受 时间值 参数。
sequence with maxspan=30s [ event_category_1 where condition_1 ] by field_baz [ event_category_2 where condition_2 ] by field_bar ...
示例
以下序列查询使用 maxspan
值 15m
(15 分钟)。匹配序列中的事件必须在第一个事件时间戳的 15 分钟内发生。
sequence with maxspan=15m [ file where file.extension == "exe" ] [ process where true ]
缺失事件编辑
使用 !
匹配缺失事件:时间跨度受限序列中不符合给定条件的事件。
sequence with maxspan=1h [ event_category_1 where condition_1 ] ![ event_category_2 where condition_2 ] [ event_category_3 where condition_3 ] ...
缺失事件子句可以在序列的开头、结尾和/或中间使用,并与正事件子句组合使用。一个序列可以有多个缺失事件子句,但至少需要有一个正子句。当存在缺失事件子句时,with maxspan
是必需的。
示例
以下序列查询查找在 5 秒内未跟随注销事件的登录事件。
sequence by host.name, user.name with maxspan=5s [ authentication where event.code : "4624" ] ![ authentication where event.code : "4647" ]
by
关键字编辑
在序列查询中使用 by
关键字,即使这些值位于不同的字段中,也只匹配共享相同值的事件。这些共享值称为连接键。如果连接键在所有事件中都应位于同一字段中,请使用 sequence by
。
sequence by field_foo [ event_category_1 where condition_1 ] by field_baz [ event_category_2 where condition_2 ] by field_bar ...
示例
以下序列查询使用 by
关键字将匹配事件限制为
- 具有相同
user.name
值的事件 -
file
事件,其file.path
值等于以下process
事件的process.executable
值。
sequence [ file where file.extension == "exe" ] by user.name, file.path [ process where true ] by user.name, process.executable
因为 user.name
字段在序列中的所有事件之间共享,所以可以使用 sequence by
包含它。以下序列等效于前一个序列。
sequence by user.name [ file where file.extension == "exe" ] by file.path [ process where true ] by process.executable
您可以组合使用 sequence by
和 with maxspan
来按字段值和时间跨度限制序列。
sequence by field_foo with maxspan=30s [ event_category_1 where condition_1 ] [ event_category_2 where condition_2 ] ...
示例
以下序列查询使用 sequence by
和 with maxspan
来仅匹配以下事件序列
- 共享相同的
user.name
字段值 - 在第一个匹配事件的
15m
(15 分钟)内发生
sequence by user.name with maxspan=15m [ file where file.extension == "exe" ] [ process where true ]
可选的 by
字段编辑
默认情况下,连接键必须是非 null
字段值。要允许 null
连接键,请使用 ?
运算符将 by
字段标记为 可选。如果您不确定要搜索的数据集是否包含 by
字段,这也很有用。
示例
以下序列查询使用 sequence by
将匹配事件限制为
- 具有相同
process.pid
值的事件,不包括null
值。如果process.pid
字段在您要搜索的数据集中不存在,则查询将返回错误。 - 具有相同
process.entity_id
值的事件,包括null
值。如果事件不包含process.entity_id
字段,则其process.entity_id
值将被视为null
。即使process.pid
字段在您要搜索的数据集中不存在,也是如此。
sequence by process.pid, ?process.entity_id [process where process.name == "regsvr32.exe"] [network where true]
until
关键字编辑
您可以使用 until
关键字为序列指定过期事件。如果此过期事件发生在序列中匹配事件之间,则该序列将过期,并且不被视为匹配。如果过期事件发生在序列中匹配事件之后,则该序列仍被视为匹配。结果中不包括过期事件。
sequence [ event_category_1 where condition_1 ] [ event_category_2 where condition_2 ] ... until [ event_category_3 where condition_3 ]
示例
数据集包含以下事件序列,按共享 ID 分组
A, B A, B, C A, C, B
以下 EQL 查询在数据集中搜索包含事件 A
后跟事件 B
的序列。事件 C
用作过期事件。
sequence by ID A B until C
查询匹配序列 A, B
和 A, B, C
,但不匹配 A, C, B
。
在 Windows 事件日志中搜索进程序列时,until
关键字非常有用。
在 Windows 中,进程 ID (PID) 仅在进程运行时才唯一。进程终止后,可以重复使用其 PID。
您可以使用 by
和 sequence by
关键字搜索具有一系列相同 PID 值的事件。
示例
以下 EQL 查询使用 sequence by
关键字来匹配一系列共享相同 process.pid
值的事件。
sequence by process.pid [ process where event.type == "start" and process.name == "cmd.exe" ] [ process where file.extension == "exe" ]
但是,由于 PID 重复使用,这可能会导致匹配序列包含跨无关进程的事件。为了防止误报,您可以使用 until
关键字在进程终止事件之前结束匹配序列。
以下 EQL 查询使用 until
关键字在 process
事件(其 event.type
为 stop
)之前结束序列。这些事件表示进程已终止。
sequence by process.pid [ process where event.type == "start" and process.name == "cmd.exe" ] [ process where file.extension == "exe" ] until [ process where event.type == "stop" ]
with runs
语句编辑
使用 with runs
语句在序列查询中连续运行相同的事件条件。例如
sequence [ process where event.type == "creation" ] [ library where process.name == "regsvr32.exe" ] with runs=3 [ registry where true ]
等效于
sequence [ process where event.type == "creation" ] [ library where process.name == "regsvr32.exe" ] [ library where process.name == "regsvr32.exe" ] [ library where process.name == "regsvr32.exe" ] [ registry where true ]
runs
值必须介于 1
和 100
之间(含)。
您可以将 with runs
语句与 by
关键字 一起使用。例如
sequence [ process where event.type == "creation" ] by process.executable [ library where process.name == "regsvr32.exe" ] by dll.path with runs=3
示例编辑
您可以使用 EQL 样本描述和匹配按时间顺序排列的一系列事件。样本中的所有事件在一个或多个字段中共享相同的值,这些字段使用 by
关键字(连接键)指定。样本中的每个项目都是一个事件类别和事件条件,用方括号 ([ ]
) 括起来。事件按其匹配的过滤器的顺序排列。
sample by join_key [ event_category_1 where condition_1 ] [ event_category_2 where condition_2 ] ...
示例
以下 EQL 样本查询最多返回 10 个具有唯一 host
值的样本。每个样本包含两个事件
-
从具有以下内容的事件开始
- 事件类别为
file
file.extension
为exe
- 事件类别为
- 后跟事件类别为
process
的事件
sample by host [ file where file.extension == "exe" ] [ process where true ]
样本查询不考虑事件的时间顺序。with maxspan
和 with runs
语句以及 until
关键字不受支持。
函数编辑
您可以使用 EQL 函数转换数据类型、执行数学运算、操作字符串等。有关受支持函数的列表,请参阅函数参考。
不区分大小写的函数编辑
默认情况下,大多数 EQL 函数区分大小写。要使函数不区分大小写,请在函数名称后使用 ~
运算符
stringContains(process.name,".exe") // Matches ".exe" but not ".EXE" or ".Exe" stringContains~(process.name,".exe") // Matches ".exe", ".EXE", or ".Exe"
函数如何影响搜索性能编辑
在 EQL 查询中使用函数可能会导致搜索速度变慢。如果您经常使用函数转换索引数据,则可以在索引期间进行这些更改以加快搜索速度。但是,这通常意味着索引速度变慢。
示例
索引包含 file.path
字段。file.path
包含文件的完整路径,包括文件扩展名。
运行 EQL 搜索时,用户经常将 endsWith
函数与 file.path
字段一起使用来匹配文件扩展名
file where endsWith(file.path,".exe") or endsWith(file.path,".dll")
虽然这可行,但编写起来可能很重复,并且可能会降低搜索速度。要加快搜索速度,您可以改为执行以下操作
这些更改可能会降低索引速度,但可以加快搜索速度。用户可以使用 file.extension
字段代替多个 endsWith
函数调用
file where file.extension in ("exe", "dll")
我们建议在生产环境中部署任何索引更改之前对其进行测试和基准测试。请参阅调整索引速度 和 调整搜索速度。
管道编辑
EQL 管道过滤、聚合和后处理 EQL 查询返回的事件。您可以使用管道来缩小 EQL 查询结果的范围或使其更具体。
管道使用管道 (|
) 字符分隔。
event_category where condition | pipe
示例
以下 EQL 查询使用 tail
管道仅返回与查询匹配的 10 个最新事件。
authentication where agent.id == 4624 | tail 10
您可以将一个管道的输出传递给另一个管道。这使您可以对单个查询使用多个管道。
有关受支持管道的列表,请参阅管道参考。
限制编辑
EQL 具有以下限制。
EQL 使用 fields
参数编辑
EQL 使用搜索 API 的 fields
参数 检索字段值。对 fields
参数的任何限制也适用于 EQL 查询。例如,如果对任何返回的字段或在索引级别禁用了 _source
,则无法检索这些值。
比较字段编辑
您不能使用 EQL 比较运算符将一个字段与另一个字段进行比较。即使使用 函数 更改了字段,也是如此。
不支持文本字段编辑
EQL 搜索不支持 text
字段。要搜索 text
字段,请使用 EQL 搜索 API 的 查询 DSL filter
参数。
对嵌套字段进行 EQL 搜索编辑
您不能使用 EQL 搜索 nested
字段的值或 nested
字段的子字段。但是,否则支持包含 nested
字段映射的数据流和索引。
与 Endgame EQL 语法的区别编辑
Elasticsearch EQL 与 Elastic Endgame EQL 语法 的区别如下
- 在 Elasticsearch EQL 中,大多数运算符区分大小写。例如,
process_name == "cmd.exe"
不等于process_name == "Cmd.exe"
。 - 在 Elasticsearch EQL 中,函数区分大小写。要使函数不区分大小写,请使用
~
,例如endsWith~(process_name, ".exe")
。 - 对于不区分大小写的相等比较,请使用
:
运算符。*
和?
都是可识别的通配符。 ==
和!=
运算符不会扩展通配符。例如,process_name == "cmd*.exe"
将*
解释为文字星号,而不是通配符。- 对于通配符匹配,区分大小写时使用
like
关键字,不区分大小写时使用like~
。:
运算符等效于like~
。 - 对于正则表达式匹配,请使用
regex
或regex~
。 -
=
不能代替==
运算符。 - 不支持用单引号 (
'
) 括起来的字符串。请改用双引号 ("
) 括起字符串。 -
?"
和?'
不表示原始字符串。请改用三个双引号 ("""
) 括起原始字符串。 -
Elasticsearch EQL 不支持
序列查询如何处理匹配项编辑
序列查询 不会找到序列的所有潜在匹配项。对于大型事件数据集,这种方法太慢且成本太高。相反,序列查询将待处理的序列匹配项作为 状态机 处理
- 序列查询中的每个事件项都是机器中的一个状态。
- 一次只能有一个待处理序列处于每个状态。
- 如果两个待处理序列同时处于同一状态,则最新的序列将覆盖旧序列。
- 如果查询包含
by
字段,则查询对每个唯一的by
字段值使用单独的状态机。
示例
数据集包含以下按时间顺序排列的 process
事件
{ "index" : { "_id": "1" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} { "index" : { "_id": "2" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} { "index" : { "_id": "3" } } { "user": { "name": "elkbee" }, "process": { "name": "bash" }, ...} { "index" : { "_id": "4" } } { "user": { "name": "root" }, "process": { "name": "bash" }, ...} { "index" : { "_id": "5" } } { "user": { "name": "root" }, "process": { "name": "bash" }, ...} { "index" : { "_id": "6" } } { "user": { "name": "elkbee" }, "process": { "name": "attrib" }, ...} { "index" : { "_id": "7" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} { "index" : { "_id": "8" } } { "user": { "name": "elkbee" }, "process": { "name": "bash" }, ...} { "index" : { "_id": "9" } } { "user": { "name": "root" }, "process": { "name": "cat" }, ...} { "index" : { "_id": "10" } } { "user": { "name": "elkbee" }, "process": { "name": "cat" }, ...} { "index" : { "_id": "11" } } { "user": { "name": "root" }, "process": { "name": "cat" }, ...}
EQL 序列查询搜索数据集
sequence by user.name [process where process.name == "attrib"] [process where process.name == "bash"] [process where process.name == "cat"]
查询的事件项对应于以下状态
- 状态 A:
[process where process.name == "attrib"]
- 状态 B:
[process where process.name == "bash"]
- 完成:
[process where process.name == "cat"]
为了找到匹配的序列,查询对每个唯一的 user.name
值使用单独的状态机。根据数据集,您可以预期有两个状态机:一个用于 root
用户,一个用于 elkbee
。
待处理的序列匹配项按如下方式在每台机器的状态之间移动
{ "index" : { "_id": "1" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} // Creates sequence [1] in state A for the "root" user. // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | [1] | | | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "2" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} // Creates sequence [2] in state A for "root", overwriting sequence [1]. // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | [2] | | | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "3" } } { "user": { "name": "elkbee" }, "process": { "name": "bash" }, ...} // Nothing happens. The "elkbee" user has no pending sequence to move // from state A to state B. // // +-----------------------"elkbee"-----------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | | | | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "4" } } { "user": { "name": "root" }, "process": { "name": "bash" }, ...} // Sequence [2] moves out of state A for "root". // State B for "root" now contains [2, 4]. // State A for "root" is empty. // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ --> +-----------+ +------------+ | // | | | | [2, 4] | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "5" } } { "user": { "name": "root" }, "process": { "name": "bash" }, ...} // Nothing happens. State A is empty for "root". // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | | | [2, 4] | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "6" } } { "user": { "name": "elkbee" }, "process": { "name": "attrib" }, ...} // Creates sequence [6] in state A for "elkbee". // // +-----------------------"elkbee"-----------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | [6] | | | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "7" } } { "user": { "name": "root" }, "process": { "name": "attrib" }, ...} // Creates sequence [7] in state A for "root". // Sequence [2, 4] remains in state B for "root". // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | [7] | | [2, 4] | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "8" } } { "user": { "name": "elkbee" }, "process": { "name": "bash" }, ...} // Sequence [6, 8] moves to state B for "elkbee". // State A for "elkbee" is now empty. // // +-----------------------"elkbee"-----------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ --> +-----------+ +------------+ | // | | | | [6, 8] | | | | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "9" } } { "user": { "name": "root" }, "process": { "name": "cat" }, ...} // Sequence [2, 4, 9] is complete for "root". // State B for "root" is now empty. // Sequence [7] remains in state A. // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ --> +------------+ | // | | [7] | | | | [2, 4, 9] | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "10" } } { "user": { "name": "elkbee" }, "process": { "name": "cat" }, ...} // Sequence [6, 8, 10] is complete for "elkbee". // State A and B for "elkbee" are now empty. // // +-----------------------"elkbee"-----------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ --> +------------+ | // | | | | | | [6, 8, 10] | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ { "index" : { "_id": "11" } } { "user": { "name": "root" }, "process": { "name": "cat" }, ...} // Nothing happens. // The machines for "root" and "elkbee" remain the same. // // +------------------------"root"------------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | [7] | | | | [2, 4, 9] | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+ // // +-----------------------"elkbee"-----------------------+ // | +-----------+ +-----------+ +------------+ | // | | State A | | State B | | Complete | | // | +-----------+ +-----------+ +------------+ | // | | | | | | [6, 8, 10] | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+