EQL 语法参考
editEQL 语法参考
edit基本语法
editEQL 查询需要一个事件类别和一个匹配条件。where 关键字将它们连接起来。
event_category where condition
事件类别是事件类别字段的索引值。默认情况下,EQL搜索API使用来自Elastic Common Schema (ECS)的event.category字段。您可以使用API的event_category_field参数指定另一个事件类别字段。
例如,以下 EQL 查询匹配事件类别为
process 且 process.name 为 svchost.exe 的事件:
process where process.name == "svchost.exe"
匹配任意事件类别
edit要匹配任何类别的事件,请使用any关键字。您还可以使用any关键字来搜索没有事件类别字段的文档。
例如,以下 EQL 查询匹配任何具有 network.protocol 字段值为 http 的文档:
any where network.protocol == "http"
转义事件类别
edit使用封闭的双引号(")或三个封闭的双引号(""")来转义事件类别,这些类别:
-
包含特殊字符,例如连字符(
-)或点(.) - 包含空格
- 以数字开头
".my.event.category" "my-event-category" "my event category" "6eventcategory" """.my.event.category""" """my-event-category""" """my event category""" """6eventcategory"""
转义字段名称
edit使用反引号(`)来转义以下字段名称:
-
包含一个连字符 (
-) - 包含一个空格
- 以数字开头
`my-field` `my field` `6myfield`
使用双反引号 (``) 来转义字段名称中的任何反引号 (`)。
my`field -> `my``field`
条件
edit条件由一个或多个事件必须匹配的标准组成。 您可以使用以下运算符指定和组合这些标准。大多数 EQL 运算符默认情况下区分大小写。
比较运算符
edit< <= == : != >= >
-
<(less than) -
如果运算符左侧的值小于右侧的值,则返回
true。否则返回false。 -
<=(less than or equal) -
如果运算符左侧的值小于或等于右侧的值,则返回
true。否则返回false。 -
==(equal, case-sensitive) -
返回
true如果运算符左右两边的值相等。 否则返回false。不支持通配符。 -
:(equal, case-insensitive) -
如果运算符左右两侧的字符串相等,则返回
true。否则返回false。只能用于比较字符串。支持通配符和列表查找。 -
!=(not equal, case-sensitive) -
如果运算符左右两侧的值不相等,则返回
true。否则返回false。不支持通配符。 -
>=(greater than or equal) -
如果运算符左侧的值大于或等于右侧的值,则返回
true。否则返回false。在比较字符串时,运算符使用区分大小写的字典顺序。 -
>(greater than) -
返回
true如果运算符左侧的值大于右侧的值。否则返回false。当比较字符串时,运算符使用区分大小写的字典顺序。
= 不支持作为等号运算符。请使用 == 或 : 代替。
模式比较关键词
editmy_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
比较的局限性
edit你不能链接比较。相反,在比较之间使用逻辑运算符。例如,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"
逻辑运算符
editand or not
-
and -
仅当左侧和右侧的条件都返回
true时,才返回true。 否则返回false。 -
or -
返回
true,如果左侧或右侧的条件之一为true。 否则返回false。 -
not -
返回
true如果右侧的条件是false。
查找操作符
editmy_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(case-sensitive) -
如果值包含在提供的列表中,则返回
true。对于不区分大小写的匹配,请使用in~。 -
not in(case-sensitive) -
如果值不在提供的列表中,则返回
true。对于不区分大小写的匹配,请使用not in~。 -
:(case-insensitive) -
如果字符串包含在提供的列表中,则返回
true。只能用于比较字符串。 -
like(case-sensitive) -
如果字符串与提供的列表中的通配符模式匹配,则返回
true。只能用于比较字符串。对于不区分大小写的匹配,请使用like~。 -
regex(case-sensitive) -
如果字符串与提供的列表中的正则表达式模式匹配,则返回
true。有关支持的正则表达式语法,请参阅正则表达式语法。 只能用于比较字符串。对于不区分大小写的匹配,请使用regex~。
数学运算符
edit+ - * / %
-
+(add) - 将运算符左右两边的值相加。
-
-(subtract) - 从左边的值中减去右边的值。
-
*(multiply) - 将运算符左右两边的值相乘。
-
/(divide) -
将运算符左侧的值除以右侧的值。
如果被除数和除数都是整数,除法(
\)操作会将返回的任何浮点数向下取整到最接近的整数。为了避免取整,将被除数或除数转换为浮点数。示例
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
-
%(modulo) - 将运算符左侧的值除以右侧的值。仅返回余数。
匹配任意条件
edit要仅根据事件类别匹配事件,请使用 where true 条件。
例如,以下 EQL 查询匹配任何 文件 事件:
file where true
要匹配任何事件,您可以将 any 关键字与 where true 条件结合使用:
any where true
可选字段
edit默认情况下,EQL查询只能包含存在于您正在搜索的数据集中的字段。如果一个字段具有显式、动态或运行时映射,则该字段存在于数据集中。如果EQL查询包含一个不存在的字段,它将返回一个错误。
如果你不确定一个字段是否存在于数据集中,使用?操作符将该字段标记为可选。如果一个可选字段不存在,查询会将其替换为null而不是返回错误。
示例
在以下查询中,user.id 字段是可选的。
network where ?user.id != null
如果在您搜索的数据集中存在user.id字段,则查询匹配包含user.id值的任何network事件。如果数据集中不存在user.id字段,EQL会将查询解释为:
network where null != null
在这种情况下,查询没有匹配到任何事件。
检查字段是否存在
edit要匹配包含某个字段任何值的事件,请使用 != 运算符将该字段与 null 进行比较:
?my_field != null
要匹配不包含字段值的事件,请使用 == 运算符将字段与 null 进行比较:
?my_field == null
字符串
edit字符串用双引号(")括起来。
"hello world"
不支持用单引号 (') 括起来的字符串。
字符串中的转义字符
edit当在字符串中使用时,特殊字符,如回车或双引号("),必须用前置的反斜杠(\)进行转义。
"example \r of \" escaped \n characters"
| Escape sequence | Literal character |
|---|---|
|
换行符(换行) |
|
回车 |
|
标签 |
|
反斜杠 ( |
|
双引号 ( |
您可以使用十六进制 \u{XXXXXXXX} 转义序列来转义 Unicode 字符。十六进制值可以是 2-8 个字符,并且不区分大小写。
短于 8 个字符的值会用零填充。您可以使用这些转义序列在字符串中包含不可打印或从右到左 (RTL) 的字符。例如,您可以将
从右到左标记 (RLM) 转义为 \u{200f}、
\u{200F} 或 \u{0000200f}。
单引号 (') 字符保留供将来使用。您不能在字面字符串中使用转义的单引号 (\')。请改用转义的双引号 (\")。
原始字符串
edit原始字符串将特殊字符(如反斜杠 \)视为字面字符。原始字符串用三个双引号(""")括起来。
"""Raw string with a literal double quote " and blackslash \ included"""
原始字符串不能包含三个连续的双引号(""")。相反,请使用带有 \" 转义序列的常规字符串。
"String containing \"\"\" three double quotes"
通配符
edit对于使用 : 运算符或 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")
序列
edit您可以使用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 语句
edit您可以使用 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 ]
缺失事件
edit使用 ! 来匹配缺失的事件:在时间跨度约束的序列中,不符合给定条件的事件。
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 关键字
edit在序列查询中使用 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 字段
edit默认情况下,连接键必须是一个非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]
直到 关键字
edit您可以使用 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关键字在具有event.type为stop的process事件之前结束序列。这些事件表示进程已被终止。
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 语句
edit使用 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 ]
The runs value must be between 1 and 100 (inclusive).
你可以使用带有by关键字的with runs语句。
例如:
sequence [ process where event.type == "creation" ] by process.executable [ library where process.name == "regsvr32.exe" ] by dll.path with runs=3
示例
edit您可以使用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 关键字
不受支持。
函数
edit您可以使用EQL函数来转换数据类型、执行数学运算、操作字符串等。有关支持的函数列表,请参阅函数参考。
不区分大小写的函数
edit大多数EQL函数默认情况下是区分大小写的。要使函数不区分大小写,请在函数名后使用~运算符:
stringContains(process.name,".exe") // Matches ".exe" but not ".EXE" or ".Exe" stringContains~(process.name,".exe") // Matches ".exe", ".EXE", or ".Exe"
函数如何影响搜索性能
edit在EQL查询中使用函数可能会导致搜索速度变慢。如果你经常使用函数来转换索引数据,可以通过在索引期间进行这些更改来加快搜索速度。然而,这通常意味着索引速度会变慢。
示例
一个索引包含 file.path 字段。file.path 包含文件的完整路径,包括文件扩展名。
在运行EQL搜索时,用户通常会使用endsWith函数与file.path字段来匹配文件扩展名:
file where endsWith(file.path,".exe") or endsWith(file.path,".dll")
虽然这可以工作,但编写起来可能会很重复,并且可能会减慢搜索速度。为了加快搜索速度,你可以改为执行以下操作:
-
添加一个新字段,
file.extension, 到索引中。该file.extension字段将仅包含来自file.path字段的文件扩展名。 -
使用包含
grok处理器或其他预处理工具的 ingest pipeline 在索引之前从file.path字段中提取文件扩展名。 -
将提取的文件扩展名索引到
file.extension字段中。
这些更改可能会减慢索引速度,但可以加快搜索速度。用户可以使用file.extension字段,而不是多次调用endsWith函数:
file where file.extension in ("exe", "dll")
我们建议在生产环境中部署任何索引更改之前进行测试和基准测试。请参阅调整索引速度和调整搜索速度。
管道
editEQL管道过滤、聚合和后处理由EQL查询返回的事件。您可以使用管道来缩小EQL查询结果的范围或使其更加具体。
管道使用竖线(|)字符进行分隔。
event_category where condition | pipe
示例
以下EQL查询使用tail管道仅返回与查询匹配的10个最新事件。
authentication where agent.id == 4624 | tail 10
您可以将一个管道的输出传递给另一个管道。这使您能够使用单个查询进行多个管道操作。
有关支持的管道的列表,请参阅 管道参考。
限制
editEQL 有以下限制。
EQL 使用 fields 参数
editEQL 使用搜索 API 的 fields
参数 来检索字段值。fields 参数的任何限制也适用于 EQL
查询。例如,如果 _source 在任何返回字段或索引级别被禁用,则无法检索这些值。
比较字段
edit您不能使用EQL比较运算符将一个字段与另一个字段进行比较。即使字段通过函数进行了更改,这也适用。
不支持文本字段
editEQL 搜索不支持 text 字段。要搜索 text 字段,
请使用 EQL 搜索 API 的 查询 DSL filter
参数。
在嵌套字段上进行EQL搜索
edit您不能使用EQL来搜索nested字段的值或nested字段的子字段。然而,包含nested字段映射的数据流和索引在其他方面是受支持的。
与Endgame EQL语法的差异
editElasticsearch 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 不支持:
序列查询如何处理匹配
edit序列查询不会找到序列的所有潜在匹配项。对于大型事件数据集,这种方法会太慢且成本太高。相反,序列查询将待处理的序列匹配作为状态机来处理:
- 序列查询中的每个事件项都是机器中的一个状态。
- 每个状态下只能有一个待处理的序列。
- 如果两个待处理的序列同时处于同一状态,则最新的序列会覆盖较旧的序列。
-
如果查询包含
by字段,查询将为每个唯一的by字段值使用一个单独的状态机。
示例
数据集包含以下按时间顺序升序排列的过程事件:
{ "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] |
// | +-----------+ +-----------+ +------------+ |
// +------------------------------------------------------+