EQL 语法参考
编辑EQL 语法参考
编辑基本语法
编辑EQL 查询需要一个事件类别和一个匹配条件。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"
匹配任何事件类别
编辑要匹配任何类别的事件,请使用 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 ...
示例
以下序列查询使用 15m
(15 分钟)的 maxspan
值。匹配序列中的事件必须在第一个事件的时间戳的 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
关键字在 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
语句
编辑使用 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
。file.extension
字段将仅包含file.path
字段中的文件扩展名。 - 使用包含
grok
处理器或其他预处理器工具的 ingest 管道,以便在索引之前从file.path
字段中提取文件扩展名。 - 将提取的文件扩展名索引到
file.extension
字段。
这些更改可能会降低索引速度,但可以加快搜索速度。用户可以使用 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] | // | +-----------+ +-----------+ +------------+ | // +------------------------------------------------------+