refactor(topic_index): no forced ceiling entry in index table
This commit is contained in:
parent
f4c8c6be55
commit
a1e6635614
|
@ -104,12 +104,16 @@ gbt_update(Name, Tree) ->
|
||||||
true.
|
true.
|
||||||
|
|
||||||
gbt_next(nil, _Input) ->
|
gbt_next(nil, _Input) ->
|
||||||
emqx_trie_search:ceiling();
|
'$end_of_table';
|
||||||
gbt_next({P, _V, _Smaller, Bigger}, K) when K >= P ->
|
gbt_next({P, _V, _Smaller, Bigger}, K) when K >= P ->
|
||||||
gbt_next(Bigger, K);
|
gbt_next(Bigger, K);
|
||||||
gbt_next({P, _V, Smaller, _Bigger}, K) ->
|
gbt_next({P, _V, Smaller, _Bigger}, K) ->
|
||||||
NextKey = gbt_next(Smaller, K),
|
case gbt_next(Smaller, K) of
|
||||||
min(P, NextKey).
|
'$end_of_table' ->
|
||||||
|
P;
|
||||||
|
NextKey ->
|
||||||
|
NextKey
|
||||||
|
end.
|
||||||
|
|
||||||
make_nextf(Name) ->
|
make_nextf(Name) ->
|
||||||
{_SizeWeDontCare, TheTree} = gbt(Name),
|
{_SizeWeDontCare, TheTree} = gbt(Name),
|
||||||
|
|
|
@ -35,9 +35,7 @@
|
||||||
%% Usable mostly for testing purposes.
|
%% Usable mostly for testing purposes.
|
||||||
-spec new() -> ets:table().
|
-spec new() -> ets:table().
|
||||||
new() ->
|
new() ->
|
||||||
T = ets:new(?MODULE, [public, ordered_set, {read_concurrency, true}]),
|
ets:new(?MODULE, [public, ordered_set, {read_concurrency, true}]).
|
||||||
ets:insert(T, {emqx_trie_search:ceiling(), []}),
|
|
||||||
T.
|
|
||||||
|
|
||||||
%% @doc Insert a new entry into the index that associates given topic filter to given
|
%% @doc Insert a new entry into the index that associates given topic filter to given
|
||||||
%% record ID, and attaches arbitrary record to the entry. This allows users to choose
|
%% record ID, and attaches arbitrary record to the entry. This allows users to choose
|
||||||
|
|
|
@ -98,14 +98,14 @@
|
||||||
|
|
||||||
-module(emqx_trie_search).
|
-module(emqx_trie_search).
|
||||||
|
|
||||||
-export([ceiling/0, make_key/2]).
|
-export([make_key/2]).
|
||||||
-export([match/2, matches/3, get_id/1, get_topic/1]).
|
-export([match/2, matches/3, get_id/1, get_topic/1]).
|
||||||
-export_type([key/1, word/0, nextf/0, opts/0]).
|
-export_type([key/1, word/0, nextf/0, opts/0]).
|
||||||
|
|
||||||
-type word() :: binary() | '+' | '#'.
|
-type word() :: binary() | '+' | '#'.
|
||||||
-type base_key() :: {binary() | [word()], {}}.
|
-type base_key() :: {binary() | [word()], {}}.
|
||||||
-type key(ID) :: {binary() | [word()], {ID}}.
|
-type key(ID) :: {binary() | [word()], {ID}}.
|
||||||
-type nextf() :: fun((key(_) | base_key()) -> key(_)).
|
-type nextf() :: fun((key(_) | base_key()) -> '$end_of_table' | key(_)).
|
||||||
-type opts() :: [unique | return_first].
|
-type opts() :: [unique | return_first].
|
||||||
|
|
||||||
%% Holds the constant values of each search.
|
%% Holds the constant values of each search.
|
||||||
|
@ -134,31 +134,20 @@
|
||||||
matches = []
|
matches = []
|
||||||
}).
|
}).
|
||||||
|
|
||||||
%% All valid utf8 bytes are less than 255.
|
|
||||||
-define(CEILING_TOPIC, <<255>>).
|
|
||||||
-define(CEILING, {?CEILING_TOPIC, {1}}).
|
|
||||||
|
|
||||||
%% @doc Return a key which is greater than all other valid keys.
|
|
||||||
ceiling() ->
|
|
||||||
?CEILING.
|
|
||||||
|
|
||||||
%% @doc Make a search-key for the given topic.
|
%% @doc Make a search-key for the given topic.
|
||||||
-spec make_key(emqx_types:topic(), ID) -> key(ID).
|
-spec make_key(emqx_types:topic(), ID) -> key(ID).
|
||||||
make_key(Topic, ID) when is_binary(Topic) ->
|
make_key(Topic, ID) when is_binary(Topic) ->
|
||||||
Words = words(Topic),
|
Words = words(Topic),
|
||||||
Key =
|
case lists:any(fun erlang:is_atom/1, Words) of
|
||||||
case lists:any(fun erlang:is_atom/1, Words) of
|
true ->
|
||||||
true ->
|
%% it's a wildcard
|
||||||
%% it's a wildcard
|
{Words, {ID}};
|
||||||
{Words, {ID}};
|
false ->
|
||||||
false ->
|
%% Not a wildcard. We do not split the topic
|
||||||
%% Not a wildcard. We do not split the topic
|
%% because they can be found with direct lookups.
|
||||||
%% because they can be found with direct lookups.
|
%% it is also more compact in memory.
|
||||||
%% it is also more compact in memory.
|
{Topic, {ID}}
|
||||||
{Topic, {ID}}
|
end.
|
||||||
end,
|
|
||||||
Key > ceiling() andalso throw({invalid_topic, Topic}),
|
|
||||||
Key.
|
|
||||||
|
|
||||||
%% @doc Extract record ID from the match.
|
%% @doc Extract record ID from the match.
|
||||||
-spec get_id(key(ID)) -> ID.
|
-spec get_id(key(ID)) -> ID.
|
||||||
|
@ -236,12 +225,14 @@ search(Topic, NextF, Opts) ->
|
||||||
%% The recursive entrypoint of the trie-search algorithm.
|
%% The recursive entrypoint of the trie-search algorithm.
|
||||||
%% Always start from the initial prefix and words.
|
%% Always start from the initial prefix and words.
|
||||||
search_new(#ctx{prefix0 = Prefix, words0 = Words0} = C, NewBase, Acc0) ->
|
search_new(#ctx{prefix0 = Prefix, words0 = Words0} = C, NewBase, Acc0) ->
|
||||||
#acc{target = {Filter, _}} = Acc = move_up(C, Acc0, NewBase),
|
case move_up(C, Acc0, NewBase) of
|
||||||
case Prefix of
|
#acc{target = '$end_of_table'} = Acc ->
|
||||||
[] ->
|
Acc;
|
||||||
|
#acc{target = {Filter, _}} = Acc when Prefix =:= [] ->
|
||||||
%% This is not a '$' topic, start from '+'
|
%% This is not a '$' topic, start from '+'
|
||||||
search_plus(C, Words0, Filter, [], Acc);
|
search_plus(C, Words0, Filter, [], Acc);
|
||||||
[DollarWord] ->
|
#acc{target = {Filter, _}} = Acc ->
|
||||||
|
[DollarWord] = Prefix,
|
||||||
%% Start from the '$' word
|
%% Start from the '$' word
|
||||||
search_up(C, DollarWord, Words0, Filter, [], Acc)
|
search_up(C, DollarWord, Words0, Filter, [], Acc)
|
||||||
end.
|
end.
|
||||||
|
|
Loading…
Reference in New Issue