mem_db 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/field"
|
4
|
+
require "mem_db/field/matching"
|
5
|
+
|
6
|
+
class MemDB
|
7
|
+
module Field
|
8
|
+
class MayMissing
|
9
|
+
include MemDB::Field
|
10
|
+
|
11
|
+
class Any
|
12
|
+
include MemDB::Field::Matching
|
13
|
+
|
14
|
+
def match?(_query)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
ANY_MATCHING = Any.new
|
20
|
+
|
21
|
+
def initialize(original)
|
22
|
+
@original = original
|
23
|
+
end
|
24
|
+
|
25
|
+
def field
|
26
|
+
@original.field
|
27
|
+
end
|
28
|
+
|
29
|
+
def query_field
|
30
|
+
@original.query_field
|
31
|
+
end
|
32
|
+
|
33
|
+
def new_matching(obj)
|
34
|
+
v = obj[field]
|
35
|
+
|
36
|
+
if v.nil?
|
37
|
+
ANY_MATCHING
|
38
|
+
else
|
39
|
+
@original.new_matching(obj)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/field"
|
4
|
+
require "mem_db/field/matching"
|
5
|
+
|
6
|
+
class MemDB
|
7
|
+
module Field
|
8
|
+
class Negative
|
9
|
+
include MemDB::Field
|
10
|
+
|
11
|
+
class NegativeMatching
|
12
|
+
include MemDB::Field::Matching
|
13
|
+
|
14
|
+
def initialize(original)
|
15
|
+
@original = original
|
16
|
+
end
|
17
|
+
|
18
|
+
def match?(query)
|
19
|
+
!@original.match?(query)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(original)
|
24
|
+
@original = original
|
25
|
+
end
|
26
|
+
|
27
|
+
def field
|
28
|
+
@original.field
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_field
|
32
|
+
@original.query_field
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_matching(obj)
|
36
|
+
NegativeMatching.new(@original.new_matching(obj))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/field"
|
4
|
+
require "mem_db/field/matching"
|
5
|
+
|
6
|
+
class MemDB
|
7
|
+
module Field
|
8
|
+
class Pattern
|
9
|
+
include MemDB::Field
|
10
|
+
|
11
|
+
class Pattern
|
12
|
+
WILDCARD = "*"
|
13
|
+
|
14
|
+
class Rx
|
15
|
+
def initialize(source)
|
16
|
+
parts = source.split(WILDCARD, -1).map { |part| ::Regexp.quote(part) }
|
17
|
+
parts[0] = "\\A#{parts[0]}"
|
18
|
+
parts[-1] = "#{parts[-1]}\\z"
|
19
|
+
@rx = ::Regexp.new(parts.join(".*"), ::Regexp::MULTILINE)
|
20
|
+
end
|
21
|
+
|
22
|
+
def match?(str)
|
23
|
+
@rx.match?(str)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Exact
|
28
|
+
def initialize(source)
|
29
|
+
@source = source
|
30
|
+
end
|
31
|
+
|
32
|
+
def match?(str)
|
33
|
+
@source == str
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Prefix
|
38
|
+
def initialize(prefix)
|
39
|
+
@prefix = prefix
|
40
|
+
end
|
41
|
+
|
42
|
+
def match?(str)
|
43
|
+
str.start_with?(@prefix)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class Suffix
|
48
|
+
def initialize(suffix)
|
49
|
+
@suffix = suffix
|
50
|
+
end
|
51
|
+
|
52
|
+
def match?(str)
|
53
|
+
str.end_with?(@suffix)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def initialize(source)
|
58
|
+
wildcard_count = source.count(WILDCARD)
|
59
|
+
@pat =
|
60
|
+
if wildcard_count.zero?
|
61
|
+
Exact.new(source)
|
62
|
+
elsif wildcard_count > 1
|
63
|
+
Rx.new(source)
|
64
|
+
elsif source.end_with?(WILDCARD)
|
65
|
+
Prefix.new(source[0..-2])
|
66
|
+
elsif source.start_with?(WILDCARD)
|
67
|
+
Suffix.new(source[1..-1])
|
68
|
+
else
|
69
|
+
Rx.new(source)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def match?(str)
|
74
|
+
@pat.match?(str)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class MultiMatching
|
79
|
+
include MemDB::Field::Matching
|
80
|
+
|
81
|
+
def initialize(f, arr)
|
82
|
+
@f = f
|
83
|
+
@patterns = arr.map { |source| Pattern.new(source) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def match?(query)
|
87
|
+
@f.query_value(query).each do |str|
|
88
|
+
return true if @patterns.any? { |pat| pat.match?(str) }
|
89
|
+
end
|
90
|
+
|
91
|
+
false
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class SingleMatching
|
96
|
+
include MemDB::Field::Matching
|
97
|
+
|
98
|
+
def initialize(f, el)
|
99
|
+
@f = f
|
100
|
+
@pat = Pattern.new(el)
|
101
|
+
end
|
102
|
+
|
103
|
+
def match?(query)
|
104
|
+
@f.query_value(query).any? { |str| @pat.match?(str) }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
attr_reader :field
|
109
|
+
|
110
|
+
def initialize(field)
|
111
|
+
@field = field
|
112
|
+
end
|
113
|
+
|
114
|
+
def new_matching(obj)
|
115
|
+
val = obj[field]
|
116
|
+
|
117
|
+
if val.is_a?(Array)
|
118
|
+
MultiMatching.new(self, val)
|
119
|
+
else
|
120
|
+
SingleMatching.new(self, val)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/field"
|
4
|
+
require "mem_db/field/matching"
|
5
|
+
|
6
|
+
class MemDB
|
7
|
+
module Field
|
8
|
+
class Regexp
|
9
|
+
include MemDB::Field
|
10
|
+
|
11
|
+
class Rx
|
12
|
+
def initialize(source)
|
13
|
+
@rx = ::Regexp.new(source, ::Regexp::MULTILINE)
|
14
|
+
end
|
15
|
+
|
16
|
+
def match?(str)
|
17
|
+
@rx.match?(str)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MultiMatching
|
22
|
+
include MemDB::Field::Matching
|
23
|
+
|
24
|
+
def initialize(field, arr)
|
25
|
+
@field = field
|
26
|
+
@patterns = arr.map { |source| Rx.new(source) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def match?(query)
|
30
|
+
@field.query_value(query).each do |str|
|
31
|
+
return true if @patterns.any? { |pat| pat.match?(str) }
|
32
|
+
end
|
33
|
+
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class SingleMatching
|
39
|
+
include MemDB::Field::Matching
|
40
|
+
|
41
|
+
def initialize(field, el)
|
42
|
+
@field = field
|
43
|
+
@pat = Rx.new(el)
|
44
|
+
end
|
45
|
+
|
46
|
+
def match?(query)
|
47
|
+
@field.query_value(query).any? { |str| @pat.match?(str) }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
attr_reader :field
|
52
|
+
|
53
|
+
def initialize(field)
|
54
|
+
@field = field
|
55
|
+
end
|
56
|
+
|
57
|
+
def new_matching(obj)
|
58
|
+
val = obj[field]
|
59
|
+
|
60
|
+
if val.is_a?(Array)
|
61
|
+
MultiMatching.new(self, val)
|
62
|
+
else
|
63
|
+
SingleMatching.new(self, val)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MemDB
|
4
|
+
class Fields
|
5
|
+
class Matching
|
6
|
+
def initialize(fields, obj)
|
7
|
+
@matchings = fields.map { |field| field.new_matching(obj) }.reject! do |matching|
|
8
|
+
matching == MemDB::Field::MayMissing::ANY_MATCHING
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(query)
|
13
|
+
@matchings.all? { |matching| matching.match?(query) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(fields)
|
18
|
+
@fields = fields
|
19
|
+
end
|
20
|
+
|
21
|
+
def new_matching(obj)
|
22
|
+
Matching.new(@fields, obj)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/mem_db/idx.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class MemDB
|
4
|
+
module Idx
|
5
|
+
ANY = Object.new
|
6
|
+
|
7
|
+
def default(default)
|
8
|
+
MemDB::Idx::Default.new(self, default)
|
9
|
+
end
|
10
|
+
|
11
|
+
def default_any
|
12
|
+
default(MemDB::Idx::ANY)
|
13
|
+
end
|
14
|
+
|
15
|
+
def field
|
16
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
17
|
+
end
|
18
|
+
|
19
|
+
def map_value(_obj)
|
20
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
21
|
+
end
|
22
|
+
|
23
|
+
def map_query(_obj)
|
24
|
+
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
|
25
|
+
end
|
26
|
+
|
27
|
+
def value(obj)
|
28
|
+
v = obj[field]
|
29
|
+
|
30
|
+
if v == ANY
|
31
|
+
v
|
32
|
+
else
|
33
|
+
v.map { |e| map_value(e) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepare_query(query)
|
38
|
+
query[field].map { |v| map_query(v) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
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 Bytes
|
8
|
+
include MemDB::Idx
|
9
|
+
|
10
|
+
attr_reader :field
|
11
|
+
|
12
|
+
def initialize(field)
|
13
|
+
@field = field
|
14
|
+
end
|
15
|
+
|
16
|
+
def map_value(raw)
|
17
|
+
raw.bytes
|
18
|
+
end
|
19
|
+
|
20
|
+
def map_query(text)
|
21
|
+
text.bytes
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mem_db/idx"
|
4
|
+
|
5
|
+
class MemDB
|
6
|
+
module Idx
|
7
|
+
class Chars
|
8
|
+
include MemDB::Idx
|
9
|
+
|
10
|
+
attr_reader :field
|
11
|
+
|
12
|
+
class Chars
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
def initialize(str)
|
16
|
+
@str = str
|
17
|
+
@length = str.length
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](pos)
|
21
|
+
@str[pos]
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :length
|
25
|
+
|
26
|
+
def reverse
|
27
|
+
ReversedChars.new(@str)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each
|
31
|
+
return to_enum unless block_given?
|
32
|
+
|
33
|
+
i = 0
|
34
|
+
while i < @length
|
35
|
+
yield @str[i]
|
36
|
+
i += 1
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ReversedChars
|
42
|
+
include Enumerable
|
43
|
+
|
44
|
+
def initialize(str)
|
45
|
+
@str = str
|
46
|
+
@length = str.length
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](pos)
|
50
|
+
@str[@str.length - pos - 1]
|
51
|
+
end
|
52
|
+
|
53
|
+
def length
|
54
|
+
@str.length
|
55
|
+
end
|
56
|
+
|
57
|
+
def reverse
|
58
|
+
Chars.new(@str)
|
59
|
+
end
|
60
|
+
|
61
|
+
def each
|
62
|
+
return to_enum unless block_given?
|
63
|
+
|
64
|
+
i = @length - 1
|
65
|
+
while i >= 0
|
66
|
+
yield @str[i]
|
67
|
+
i -= 1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(field)
|
73
|
+
@field = field
|
74
|
+
end
|
75
|
+
|
76
|
+
def map_value(raw)
|
77
|
+
raw.chars
|
78
|
+
end
|
79
|
+
|
80
|
+
def map_query(text)
|
81
|
+
Chars.new(text)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|