mem_db 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +18 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/.rubocop.yml +43 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +105 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/mem_db.rb +102 -0
- data/lib/mem_db/bucket.rb +17 -0
- data/lib/mem_db/field.rb +34 -0
- data/lib/mem_db/field/enum.rb +56 -0
- data/lib/mem_db/field/matching.rb +11 -0
- data/lib/mem_db/field/may_missing.rb +44 -0
- data/lib/mem_db/field/negative.rb +40 -0
- data/lib/mem_db/field/pattern.rb +125 -0
- data/lib/mem_db/field/regexp.rb +68 -0
- data/lib/mem_db/fields.rb +25 -0
- data/lib/mem_db/idx.rb +41 -0
- data/lib/mem_db/idx/bytes.rb +25 -0
- data/lib/mem_db/idx/chars.rb +85 -0
- data/lib/mem_db/idx/default.rb +33 -0
- data/lib/mem_db/idx/itself.rb +25 -0
- data/lib/mem_db/idx/pattern.rb +71 -0
- data/lib/mem_db/idx/reverse.rb +27 -0
- data/lib/mem_db/idx/uniq.rb +36 -0
- data/lib/mem_db/index.rb +32 -0
- data/lib/mem_db/index/any.rb +60 -0
- data/lib/mem_db/index/bucket.rb +20 -0
- data/lib/mem_db/index/enum.rb +54 -0
- data/lib/mem_db/index/pattern_match.rb +104 -0
- data/lib/mem_db/index/prefix_tree.rb +110 -0
- data/lib/mem_db/index/sequence_match.rb +146 -0
- data/lib/mem_db/indexation.rb +17 -0
- data/lib/mem_db/indexing_object.rb +58 -0
- data/lib/mem_db/out.rb +25 -0
- data/lib/mem_db/query.rb +49 -0
- data/lib/mem_db/version.rb +5 -0
- data/mem_db.gemspec +25 -0
- metadata +86 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/idx"
|
4
|
+
|
5
|
+
class MemDB
|
6
|
+
module Idx
|
7
|
+
class Default
|
8
|
+
include MemDB::Idx
|
9
|
+
|
10
|
+
def initialize(original, default)
|
11
|
+
@original = original
|
12
|
+
@default = default
|
13
|
+
end
|
14
|
+
|
15
|
+
def field
|
16
|
+
@original.field
|
17
|
+
end
|
18
|
+
|
19
|
+
def value(obj)
|
20
|
+
v = obj[field]
|
21
|
+
if v.nil?
|
22
|
+
@default
|
23
|
+
else
|
24
|
+
@original.value(obj)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare_query(query)
|
29
|
+
@original.prepare_query(query)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/idx"
|
4
|
+
|
5
|
+
class MemDB
|
6
|
+
module Idx
|
7
|
+
class Itself
|
8
|
+
include MemDB::Idx
|
9
|
+
|
10
|
+
attr_reader :field
|
11
|
+
|
12
|
+
def initialize(field)
|
13
|
+
@field = field
|
14
|
+
end
|
15
|
+
|
16
|
+
def map_value(val)
|
17
|
+
val
|
18
|
+
end
|
19
|
+
|
20
|
+
def map_query(val)
|
21
|
+
val
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MemDB
|
4
|
+
module Idx
|
5
|
+
class Pattern
|
6
|
+
include MemDB::Idx
|
7
|
+
|
8
|
+
class Pattern
|
9
|
+
def initialize(source, min:)
|
10
|
+
@parts = source.split("*", -1)
|
11
|
+
@min = min
|
12
|
+
end
|
13
|
+
|
14
|
+
def value # rubocop:disable Metrics/PerceivedComplexity, Metrics/AbcSize, Metrics/CyclomaticComplexity
|
15
|
+
if @parts.length == 1
|
16
|
+
[:enum, @parts[0]]
|
17
|
+
else
|
18
|
+
prefix = @parts[0]
|
19
|
+
suffix = @parts[-1]
|
20
|
+
|
21
|
+
if prefix != "" || suffix != ""
|
22
|
+
if prefix.length >= @min && prefix.length >= suffix.length
|
23
|
+
[:prefix, prefix]
|
24
|
+
elsif suffix.length >= @min
|
25
|
+
[:suffix, suffix]
|
26
|
+
else
|
27
|
+
candidates = [
|
28
|
+
[:prefix, prefix],
|
29
|
+
[:suffix, suffix]
|
30
|
+
]
|
31
|
+
|
32
|
+
candidates.push([:substring, substring]) if substring # rubocop:disable Metrics/BlockNesting
|
33
|
+
|
34
|
+
candidates.max_by { |v| v[1].length }
|
35
|
+
end
|
36
|
+
else
|
37
|
+
[:substring, substring]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def substring
|
45
|
+
return @substring if defined?(@substring)
|
46
|
+
|
47
|
+
@substring =
|
48
|
+
if @parts.length > 2
|
49
|
+
substrings = @parts[1..-2]
|
50
|
+
substrings.find { |p| p.length > @min } || substrings.find { |p| p.length.positive? }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
attr_reader :field
|
56
|
+
|
57
|
+
def initialize(field, min: 3)
|
58
|
+
@field = field
|
59
|
+
@min = min
|
60
|
+
end
|
61
|
+
|
62
|
+
def map_value(pattern)
|
63
|
+
Pattern.new(pattern, min: @min).value
|
64
|
+
end
|
65
|
+
|
66
|
+
def map_query(text)
|
67
|
+
text
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/idx"
|
4
|
+
|
5
|
+
class MemDB
|
6
|
+
module Idx
|
7
|
+
class Reverse
|
8
|
+
include MemDB::Idx
|
9
|
+
|
10
|
+
def initialize(original)
|
11
|
+
@original = original
|
12
|
+
end
|
13
|
+
|
14
|
+
def field
|
15
|
+
@original.field
|
16
|
+
end
|
17
|
+
|
18
|
+
def map_value(val)
|
19
|
+
@original.map_value(val).reverse
|
20
|
+
end
|
21
|
+
|
22
|
+
def map_query(val)
|
23
|
+
@original.map_query(val).reverse
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MemDB
|
4
|
+
module Idx
|
5
|
+
class Uniq
|
6
|
+
include MemDB::Idx
|
7
|
+
|
8
|
+
def initialize(original)
|
9
|
+
@original = original
|
10
|
+
end
|
11
|
+
|
12
|
+
def field
|
13
|
+
@original.field
|
14
|
+
end
|
15
|
+
|
16
|
+
def value(obj)
|
17
|
+
val = @original.value(obj)
|
18
|
+
return val if val == MemDB::Idx::ANY
|
19
|
+
|
20
|
+
val.uniq
|
21
|
+
end
|
22
|
+
|
23
|
+
def prepare_query(query)
|
24
|
+
@original.prepare_query(query).uniq
|
25
|
+
end
|
26
|
+
|
27
|
+
def map_value(raw)
|
28
|
+
@original.map_value(raw)
|
29
|
+
end
|
30
|
+
|
31
|
+
def map_query(raw)
|
32
|
+
@original.map_query(raw)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/mem_db/index.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/out"
|
4
|
+
|
5
|
+
class MemDB
|
6
|
+
module Index
|
7
|
+
def self.compose(chain)
|
8
|
+
(0..chain.length - 2).each do |parent_i|
|
9
|
+
chain[parent_i].bucket = chain[parent_i + 1]
|
10
|
+
end
|
11
|
+
chain[0].new
|
12
|
+
end
|
13
|
+
|
14
|
+
def idx
|
15
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
16
|
+
end
|
17
|
+
|
18
|
+
def bucket
|
19
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
def add(_obj, _value)
|
23
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable Lint/UnusedMethodArgument
|
27
|
+
def query(_query, out: MemDB::Out.new)
|
28
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
29
|
+
end
|
30
|
+
# rubocop:enable Lint/UnusedMethodArgument
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/index"
|
4
|
+
require "mem_db/index/bucket"
|
5
|
+
require "mem_db/out"
|
6
|
+
require "mem_db/idx"
|
7
|
+
|
8
|
+
class MemDB
|
9
|
+
module Index
|
10
|
+
class Any
|
11
|
+
include MemDB::Index
|
12
|
+
|
13
|
+
class Bucket
|
14
|
+
include MemDB::Index::Bucket
|
15
|
+
|
16
|
+
def initialize(original)
|
17
|
+
@original = original
|
18
|
+
end
|
19
|
+
|
20
|
+
def new
|
21
|
+
MemDB::Index::Any.new(@original.new)
|
22
|
+
end
|
23
|
+
|
24
|
+
def bucket=(bucket)
|
25
|
+
@original.bucket = bucket
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize(original)
|
30
|
+
raise ArgumentError, "original must be MemDB::Index, got: #{original.class}" unless original.is_a?(MemDB::Index)
|
31
|
+
|
32
|
+
@original = original
|
33
|
+
end
|
34
|
+
|
35
|
+
def idx
|
36
|
+
@original.idx
|
37
|
+
end
|
38
|
+
|
39
|
+
def bucket
|
40
|
+
@original.bucket
|
41
|
+
end
|
42
|
+
|
43
|
+
def add(obj, value)
|
44
|
+
addr = obj.idx_value(idx)
|
45
|
+
if addr == MemDB::Idx::ANY
|
46
|
+
@any ||= bucket.new
|
47
|
+
@any.add(obj, value)
|
48
|
+
else
|
49
|
+
@original.add(obj, value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def query(query, out: MemDB::Out.new)
|
54
|
+
@original.query(query, out: out)
|
55
|
+
@any&.query(query, out: out)
|
56
|
+
out
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: move to mem_db/index.rb
|
4
|
+
class MemDB
|
5
|
+
module Index
|
6
|
+
module Bucket
|
7
|
+
def new
|
8
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
9
|
+
end
|
10
|
+
|
11
|
+
def bucket=(bucket)
|
12
|
+
@bucket = bucket
|
13
|
+
end
|
14
|
+
|
15
|
+
def accept_any
|
16
|
+
MemDB::Index::Any::Bucket.new(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/out"
|
4
|
+
require "mem_db/index"
|
5
|
+
require "mem_db/index/bucket"
|
6
|
+
require "mem_db/bucket"
|
7
|
+
|
8
|
+
class MemDB
|
9
|
+
module Index
|
10
|
+
class Enum
|
11
|
+
include MemDB::Index
|
12
|
+
|
13
|
+
class Bucket
|
14
|
+
include MemDB::Index::Bucket
|
15
|
+
|
16
|
+
def initialize(idx:, bucket: MemDB::Bucket)
|
17
|
+
@idx = idx
|
18
|
+
@bucket = bucket
|
19
|
+
end
|
20
|
+
|
21
|
+
def new
|
22
|
+
MemDB::Index::Enum.new(idx: @idx, bucket: @bucket)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_reader :idx, :bucket
|
27
|
+
|
28
|
+
def initialize(idx:, bucket: MemDB::Bucket)
|
29
|
+
@idx = idx
|
30
|
+
@bucket = bucket
|
31
|
+
@hash = {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def add(obj, value)
|
35
|
+
enums = obj.idx_value(@idx)
|
36
|
+
enums.each do |enum|
|
37
|
+
b = @hash[enum] ||= @bucket.new
|
38
|
+
b.add(obj, value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def query(query, out: MemDB::Out.new)
|
43
|
+
enums = query.idx_value(@idx)
|
44
|
+
enums.each do |enum|
|
45
|
+
if (b = @hash[enum])
|
46
|
+
b.query(query, out: out)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
out
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/index/enum"
|
4
|
+
require "mem_db/index/prefix_tree"
|
5
|
+
require "mem_db/index/sequence_match"
|
6
|
+
require "mem_db/bucket"
|
7
|
+
require "mem_db/index/bucket"
|
8
|
+
require "mem_db/out"
|
9
|
+
|
10
|
+
class MemDB
|
11
|
+
module Index
|
12
|
+
class PatternMatch
|
13
|
+
include MemDB::Index
|
14
|
+
|
15
|
+
class Bucket
|
16
|
+
include MemDB::Index::Bucket
|
17
|
+
|
18
|
+
def initialize(idx:, bucket: MemDB::Bucket)
|
19
|
+
@idx = idx
|
20
|
+
@bucket = bucket
|
21
|
+
end
|
22
|
+
|
23
|
+
def new
|
24
|
+
MemDB::Index::PatternMatch.new(idx: @idx, bucket: @bucket)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_reader :idx, :bucket
|
29
|
+
|
30
|
+
def initialize(idx:, bucket: MemDB::Bucket)
|
31
|
+
@idx = idx
|
32
|
+
@bucket = bucket
|
33
|
+
end
|
34
|
+
|
35
|
+
def add(obj, value)
|
36
|
+
obj.idx_value(@idx).each do |pattern|
|
37
|
+
type = pattern[0]
|
38
|
+
sequence = pattern[1]
|
39
|
+
index =
|
40
|
+
case type
|
41
|
+
when :enum
|
42
|
+
enums
|
43
|
+
when :prefix
|
44
|
+
prefixes
|
45
|
+
when :suffix
|
46
|
+
suffixes
|
47
|
+
when :substring
|
48
|
+
substrings
|
49
|
+
end
|
50
|
+
|
51
|
+
obj[self] = sequence
|
52
|
+
index.add(obj, value)
|
53
|
+
obj.delete(self)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def query(query, out: MemDB::Out.new)
|
58
|
+
query.idx_value(@idx).each do |q|
|
59
|
+
query[self] = q
|
60
|
+
|
61
|
+
@enums&.query(query, out: out)
|
62
|
+
@suffixes&.query(query, out: out)
|
63
|
+
@prefixes&.query(query, out: out)
|
64
|
+
@substrings&.query(query, out: out)
|
65
|
+
|
66
|
+
query.delete(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
out
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def enums
|
75
|
+
@enums ||= MemDB::Index::Enum.new(
|
76
|
+
idx: MemDB::Idx::Itself.new(self),
|
77
|
+
bucket: @bucket
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
def prefixes
|
82
|
+
@prefixes ||= MemDB::Index::PrefixTree.new(
|
83
|
+
idx: MemDB::Idx::Bytes.new(self),
|
84
|
+
bucket: @bucket
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
def suffixes
|
89
|
+
@suffixes ||= MemDB::Index::PrefixTree.new(
|
90
|
+
idx: MemDB::Idx::Reverse.new(MemDB::Idx::Bytes.new(self)),
|
91
|
+
bucket: @bucket
|
92
|
+
)
|
93
|
+
end
|
94
|
+
|
95
|
+
def substrings
|
96
|
+
@substrings ||= MemDB::Index::SequenceMatch.new(
|
97
|
+
# reverse somehow make it faster
|
98
|
+
idx: MemDB::Idx::Reverse.new(MemDB::Idx::Bytes.new(self)),
|
99
|
+
bucket: @bucket
|
100
|
+
)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|