简体   繁体   English

查询使用元组键的 erlang ETS 表

[英]Querying an erlang ETS table that uses a tuple key

I have an ETS table where the key consist of a record like {shell,TIME,NAME,ID} and I want to allow a user to search for entries using a combination of any of those.我有一个 ETS 表,其中的键由 {shell,TIME,NAME,ID} 之类的记录组成,我希望允许用户使用其中任何一个组合来搜索条目。 IE: where即:在哪里

  • TIME < LOW_VALUE and TIME > HIGH_VALUE or TIME < LOW_VALUE 和 TIME > HIGH_VALUE 或
  • TIME < 40 and NAME == "SAM" or TIME < 40 并且 NAME == "SAM" 或
  • ID == 123编号 == 123

I understand how to use fun2ms, but not well enough to know whether there's some clean one-liner way of doing this.我了解如何使用 fun2ms,但还不足以知道是否有一些干净的单线方法可以做到这一点。 My current solution is matching a search request to the 6 possible combinations of search types, and it just feels dirty compared to all my other erlang code that makes extensive use of pattern matching.我当前的解决方案是将搜索请求与 6 种可能的搜索类型组合进行匹配,与我所有其他广泛使用模式匹配的 erlang 代码相比,它感觉很脏。

Can you guys help me with making use of fun2ms or ETS tables in a more intelligent way?你们能帮助我以更智能的方式使用 fun2ms 或 ETS 表吗? I'm very sure this query can be done with one line.我很确定这个查询可以用一行来完成。 Here is an example of one of the 6 functions I use to show you what I have:这是我用来向您展示我拥有的 6 个函数之一的示例:

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

EDIT:编辑:

So This is what I'm trying to do so far:所以这就是我目前正在尝试做的事情:

I have a record that I want to default to: -record(s1shell_query,{tsched = {_ScLow,_ScHigh}, id = _ID, type = _Type}).我有一条记录,我想默认为:-record(s1shell_query,{tsched = {_ScLow,_ScHigh}, id = _ID, type = _Type})。

which means the user can change any of the record terms for the things they want to match.这意味着用户可以更改他们想要匹配的任何记录条款。 The problem is that you can't have unbound variables defaulted in a record.问题是您不能在记录中默认未绑定变量。

My match spec function looks like this:我的比赛规格 function 看起来像这样:

    {ScLow,ScHigh} = ShellQuery#s3shell_query.tsched,
  ets:select(Table, ets:fun2ms(
    fun(#stage3Activity{shell = #activity_shell{tsched = Tsched, id = ID, type = Type}})
      when Tsched < ScLow, Tsched>ScHigh,ID == ID, Type == Type ->
      #activity_shell{tsched = Tsched,id = ID,type = Type}
    end)).

So I'm stuck trying to figure out how I can ignore matching things the user didn't put into the shell query record.所以我一直在试图弄清楚如何忽略用户没有放入 shell 查询记录的匹配内容。

You can use custom match specs guards for searching instead of the ets:fun2ms/1 , for example:您可以使用自定义匹配规范守卫代替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, #row{key = {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}.

You can use it like:你可以像这样使用它:

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}]

You have more information about the match specs syntaxis in ets:select/2您在ets:select/2中有更多关于匹配规范语法的信息

I'd recommend using我建议使用

MatchHead = #row{key = {shell, '$1', '$2', '$3'}, value = '_'},

even if you need to tweak the record definition to allow the '$x' and '_' atoms.即使您需要调整记录定义以允许'$x''_'原子。

Also, please consider compiling the match spec and/or using continuations if you are using big tables.此外,如果您使用大表,请考虑编译匹配规范和/或使用延续。

EDIT By abusing the complete order between erlang terms and the useful '_' defaults in the search record, you're able to obtain what you need:编辑通过滥用 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, #row{key = {shell, I, integer_to_list(I), I * 1000}, value = I * 1000})
     || I <- lists:seq(1,1000)].

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

The id and name will be taken from the record if it's defined (and use '_' if it's not), and the guards will perform the filtering naturally if they are defined, and using the complete order for the higher limit if they are not (atoms are always higher than numbers, regardless of the atom and the number). idname如果已定义,将从记录中获取(如果未定义则使用'_' ),如果已定义,则守卫将自然执行过滤,如果未定义,则使用完整顺序作为上限(原子总是高于数字,无论原子和数字如何)。

An example of the usage follows:使用示例如下:

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_query{id = 14000}).
[#row{key = {shell,14,"14",14000},value = 14000}]
8> match_spec_guards:search(#s1shell_query{name = "14"}).
[#row{key = {shell,14,"14",14000},value = 14000}]
9> match_spec_guards:search(#s1shell_query{tsched_high = 20}).                 
[#row{key = {shell,1,"1",1000},value = 1000},
 #row{key = {shell,15,"15",15000},value = 15000},
 #row{key = {shell,6,"6",6000},value = 6000},
 #row{key = {shell,16,"16",16000},value = 16000},
 #row{key = {shell,8,"8",8000},value = 8000},
 #row{key = {shell,2,"2",2000},value = 2000},
 #row{key = {shell,9,"9",9000},value = 9000},
 #row{key = {shell,17,"17",17000},value = 17000},
 #row{key = {shell,12,"12",12000},value = 12000},
 #row{key = {shell,7,"7",7000},value = 7000},
 #row{key = {shell,13,"13",13000},value = 13000},
 #row{key = {shell,10,"10",10000},value = 10000},
 #row{key = {shell,3,"3",3000},value = 3000},
 #row{key = {shell,11,"11",11000},value = 11000},
 #row{key = {shell,19,"19",19000},value = 19000},
 #row{key = {shell,14,"14",14000},value = 14000},
 #row{key = {shell,5,"5",5000},value = 5000},
 #row{key = {shell,4,"4",4000},value = 4000},
 #row{key = {shell,18,"18",18000},value = 18000}]
10> match_spec_guards:search(#s1shell_query{tsched_low = 998}).
[#row{key = {shell,998,"998",998000},value = 998000},
 #row{key = {shell,999,"999",999000},value = 999000},
 #row{key = {shell,1000,"1000",1000000},value = 1000000}]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM