查询使用元组键的 erlang ETS 表

Posted

技术标签:

【中文标题】查询使用元组键的 erlang ETS 表【英文标题】:Querying an erlang ETS table that uses a tuple key 【发布时间】:2020-08-11 03:35:05 【问题描述】:

我有一个 ETS 表,其中的键由 shell,TIME,NAME,ID 之类的记录组成,我希望允许用户使用其中任何一个组合来搜索条目。 即:在哪里

TIME HIGH_VALUE 或 时间 ID == 123

我了解如何使用 fun2ms,但还不足以知道是否有一些干净的单线方法可以做到这一点。我当前的解决方案是将搜索请求与 6 种可能的搜索类型组合进行匹配,与我所有其他广泛使用模式匹配的 erlang 代码相比,它只是感觉很脏。

你们能帮我以更智能的方式使用 fun2ms 或 ETS 表吗?我很确定这个查询可以用一行来完成。这是我用来向您展示我所拥有的 6 个函数之一的示例:

  getShells_by_Time(Tstart, Tend) ->
  Results = ets:select(schedule_row1,ets:fun2ms(
    fun(A = #activityshell = ActivityShell,activity_data = S1Data)
      when (ActivityShell#activity_shell.tsched < Tend)
      andalso (ActivityShell#activity_shell.tsched > Tstart)  ->
      ActivityShell
    end)),

编辑:

所以这就是我目前正在尝试做的事情:

我有一条记录,我想默认为: -record(s1shell_query,tsched = _ScLow,_ScHigh, id = _ID, type = _Type)。

这意味着用户可以更改他们想要匹配的任何记录词。问题是您不能在记录中默认未绑定变量。

我的匹配规范函数如下所示:

    ScLow,ScHigh = ShellQuery#s3shell_query.tsched,
  ets:select(Table, ets:fun2ms(
    fun(#stage3Activityshell = #activity_shelltsched = Tsched, id = ID, type = Type)
      when Tsched < ScLow, Tsched>ScHigh,ID == ID, Type == Type ->
      #activity_shelltsched = Tsched,id = ID,type = Type
    end)).

所以我一直在试图弄清楚如何忽略用户没有放入 shell 查询记录的匹配内容。

【问题讨论】:

你不能在状态定义记录(s1shell_query,tsched = _ScLow,_ScHigh, id = undef, type = undef) 中放一个默认值并检查它是否在比它不是从用户输入发送的。 记录定义不允许我放入 _ 原子,但我会再试一次。虽然你给了我一个我还没有想到的格式的想法!不幸的是,我只是推送了这部分代码,而我目前正在做其他事情。我会尽快尝试的。目前我正在检查用户搜索参数的“未定义”,然后生成不同的搜索功能,但就像我说的那样,感觉很难看。 我一直在使用它。示例记录定义 -record(state, message = [] :: term(),tablename :: binary() | 'undefined')。 【参考方案1】:

您可以使用自定义匹配规范守卫代替ets:fun2ms/1 进行搜索,例如:

-module(match_spec_guards).
-export([new/0, populate/0, search/1]).

-record(row, 
          key :: shell, Time :: integer(), Name :: string(), ID :: term(),
          value :: term()
         ).

new() ->
    ets:new(?MODULE, [set, named_table, keypos, #row.key]).

populate() ->
    [ets:insert(?MODULE, #rowkey = shell, I, integer_to_list(I), I * 1000, value = I * 1000)
     || I <- lists:seq(1,1000)].

search(Filters) ->
    Guards = [filter_to_guard(Filter) || Filter <- Filters],
    MatchHead = row, shell, '$1', '$2', '$3', '_',
    Result = ['$_'],
    ets:select(?MODULE, [MatchHead, Guards, Result]).

filter_to_guard(time_higher_than, X) -> '<', X, '$1';
filter_to_guard(time_lower_than, X) -> '>', X, '$1';
filter_to_guard(name_is, X) -> '==', '$2', X;
filter_to_guard(id_is, X) -> '==', '$3', X.

你可以像这样使用它:

Erlang/OTP 22 [erts-10.7.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1]

Eshell V10.7.1  (abort with ^G)
1> c(match_spec_guards).
ok,match_spec_guards
2>  match_spec_guards:new().
match_spec_guards
3> match_spec_guards:populate().
[true,true,true,true,true,true,true,true,true,true,true,
 true,true,true,true,true,true,true,true,true,true,true,true,
 true,true,true,true,true,true|...]
4> match_spec_guards:search([time_higher_than, 10, time_lower_than, 20]).
[row,shell,15,"15",15000,15000,
 row,shell,16,"16",16000,16000,
 row,shell,17,"17",17000,17000,
 row,shell,12,"12",12000,12000,
 row,shell,13,"13",13000,13000,
 row,shell,11,"11",11000,11000,
 row,shell,19,"19",19000,19000,
 row,shell,14,"14",14000,14000,
 row,shell,18,"18",18000,18000]
5> match_spec_guards:search([id_is, 15000]).
[row,shell,15,"15",15000,15000]
6> match_spec_guards:search([name_is, "15000"]).
[]
7> match_spec_guards:search([name_is, "15"]).   
[row,shell,15,"15",15000,15000]

您在ets:select/2 中有更多关于匹配规范语法的信息

我建议使用

MatchHead = #rowkey = shell, '$1', '$2', '$3', value = '_',

即使您需要调整记录定义以允许 '$x''_' 原子。

另外,如果您使用大表,请考虑编译匹配规范和/或使用延续。

编辑 通过滥用 erlang 术语之间的完整顺序和搜索记录中有用的 '_' 默认值,您可以获得所需的内容:


-module(match_spec_guards).
-export([new/0, populate/0, search/1]).

-record(row, 
          key :: shell, Time :: integer(), Name :: string(), ID :: term(),
          value :: term()
         ).

-record(s1shell_query, 
          tsched_low = 0,
          tsched_high = undefined,
          id = '_',
          name = '_'
         ).

new() ->
    ets:new(?MODULE, [set, named_table, keypos, #row.key]).

populate() ->
    [ets:insert(?MODULE, #rowkey = shell, I, integer_to_list(I), I * 1000, value = I * 1000)
     || I <- lists:seq(1,1000)].

search(#s1shell_querytsched_low = Low, tsched_high = High = Query) ->
    MatchHead = #rowkey = shell, '$1', Query#s1shell_query.name, Query#s1shell_query.id, value = '_',
    Guards = ['>', High, '$1', '=<', Low, '$1'],
    ets:select(?MODULE, [MatchHead, Guards, ['$_']]).

idname 如果已定义,将从记录中取出(如果未定义,则使用 '_'),如果已定义,守卫将自然执行过滤,并使用 complete order如果不是,则为更高的限制(原子总是高于数字,无论原子和数字如何)。

使用示例如下:

Erlang/OTP 22 [erts-10.7.1] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1]

Eshell V10.7.1  (abort with ^G)
1>  c(match_spec_guards).
ok,match_spec_guards
2> match_spec_guards:new().
match_spec_guards
3> match_spec_guards:populate().
[true,true,true,true,true,true,true,true,true,true,true,
 true,true,true,true,true,true,true,true,true,true,true,true,
 true,true,true,true,true,true|...]
4>  rr(match_spec_guards).
[row,s1shell_query]
7> match_spec_guards:search(#s1shell_queryid = 14000).
[#rowkey = shell,14,"14",14000,value = 14000]
8> match_spec_guards:search(#s1shell_queryname = "14").
[#rowkey = shell,14,"14",14000,value = 14000]
9> match_spec_guards:search(#s1shell_querytsched_high = 20).                 
[#rowkey = shell,1,"1",1000,value = 1000,
 #rowkey = shell,15,"15",15000,value = 15000,
 #rowkey = shell,6,"6",6000,value = 6000,
 #rowkey = shell,16,"16",16000,value = 16000,
 #rowkey = shell,8,"8",8000,value = 8000,
 #rowkey = shell,2,"2",2000,value = 2000,
 #rowkey = shell,9,"9",9000,value = 9000,
 #rowkey = shell,17,"17",17000,value = 17000,
 #rowkey = shell,12,"12",12000,value = 12000,
 #rowkey = shell,7,"7",7000,value = 7000,
 #rowkey = shell,13,"13",13000,value = 13000,
 #rowkey = shell,10,"10",10000,value = 10000,
 #rowkey = shell,3,"3",3000,value = 3000,
 #rowkey = shell,11,"11",11000,value = 11000,
 #rowkey = shell,19,"19",19000,value = 19000,
 #rowkey = shell,14,"14",14000,value = 14000,
 #rowkey = shell,5,"5",5000,value = 5000,
 #rowkey = shell,4,"4",4000,value = 4000,
 #rowkey = shell,18,"18",18000,value = 18000]
10> match_spec_guards:search(#s1shell_querytsched_low = 998).
[#rowkey = shell,998,"998",998000,value = 998000,
 #rowkey = shell,999,"999",999000,value = 999000,
 #rowkey = shell,1000,"1000",1000000,value = 1000000]

【讨论】:

以上是关于查询使用元组键的 erlang ETS 表的主要内容,如果未能解决你的问题,请参考以下文章

如何将带有元组键的 python 字典转换为 pandas 多索引数据框?

使用元组键从Dictionary [key1,key2]获取第一个键的列表

Erlang - ETS 表之间的比较

在不使用保护子句的情况下过滤 erlang ets 表

Erlang ets插入多个表

Erlang ets 移除/过滤元素