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,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
|