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.
@@ -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
@@ -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