mongory 0.6.3 → 0.7.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +46 -0
  4. data/README.md +83 -176
  5. data/Rakefile +77 -0
  6. data/SUBMODULE_INTEGRATION.md +325 -0
  7. data/docs/advanced_usage.md +40 -0
  8. data/docs/clang_bridge.md +69 -0
  9. data/docs/field_names.md +30 -0
  10. data/docs/migration.md +30 -0
  11. data/docs/performance.md +61 -0
  12. data/examples/benchmark.rb +98 -19
  13. data/ext/mongory_ext/extconf.rb +91 -0
  14. data/ext/mongory_ext/mongory-core/LICENSE +3 -0
  15. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +105 -0
  16. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +206 -0
  17. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +82 -0
  18. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  19. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +108 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  23. data/ext/mongory_ext/mongory-core/src/foundations/array.c +246 -0
  24. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +18 -0
  25. data/ext/mongory_ext/mongory-core/src/foundations/config.c +406 -0
  26. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +30 -0
  27. data/ext/mongory_ext/mongory-core/src/foundations/error.c +30 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/table.c +458 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/value.c +459 -0
  33. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +309 -0
  34. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  35. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +161 -0
  36. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +115 -0
  37. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  39. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +539 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +144 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +121 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +199 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +334 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +198 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +107 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +50 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +55 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  54. data/ext/mongory_ext/mongory_ext.c +635 -0
  55. data/lib/mongory/c_query_builder.rb +44 -0
  56. data/lib/mongory/query_builder.rb +8 -0
  57. data/lib/mongory/utils/context.rb +7 -0
  58. data/lib/mongory/version.rb +1 -1
  59. data/lib/mongory.rb +7 -0
  60. data/mongory.gemspec +10 -4
  61. data/scripts/build_with_core.sh +292 -0
  62. metadata +69 -4
@@ -0,0 +1,325 @@
1
+ # Mongory-rb Submodule Integration Guide
2
+
3
+ This document describes how to integrate `mongory-core` as a Git submodule into `mongory-rb` to enable a high-performance C extension.
4
+
5
+ ## Architecture Overview
6
+
7
+ ```
8
+ mongory-rb/
9
+ ├── lib/ # Ruby code
10
+ │ ├── mongory.rb # Entry, loads the C extension
11
+ │ └── mongory/ # Other Ruby modules
12
+ ├── ext/ # C extension
13
+ │ └── mongory_ext/
14
+ │ ├── mongory-core/ # Git submodule (sources only; no prior CMake build required)
15
+ │ ├── extconf.rb # Build configuration (compiles submodule .c sources directly)
16
+ │ └── mongory_ext.c # Ruby C wrapper
17
+ └── scripts/
18
+ └── build_with_core.sh # (Optional) build script
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### 1. Clone and initialize
24
+
25
+ ```bash
26
+ # Clone the project
27
+ git clone <your-mongory-rb-repo>
28
+ cd mongory-rb
29
+
30
+ # Initialize the submodule
31
+ git submodule update --init --recursive
32
+ ```
33
+
34
+ ### 2. Install system dependencies
35
+
36
+ Installing `mongory-rb` (with the C extension) only requires basic build tools and Ruby headers:
37
+
38
+ **macOS:**
39
+ ```bash
40
+ xcode-select --install # Install Xcode Command Line Tools (includes clang)
41
+ ```
42
+
43
+ **Ubuntu/Debian:**
44
+ ```bash
45
+ sudo apt update
46
+ sudo apt install build-essential ruby-dev
47
+ ```
48
+
49
+ **CentOS/RHEL/Fedora:**
50
+ ```bash
51
+ sudo yum groupinstall "Development Tools" || sudo dnf groupinstall "Development Tools"
52
+ sudo yum install ruby-devel || sudo dnf install ruby-devel
53
+ ```
54
+
55
+ > Optional: CMake and cJSON are only needed if you want to run the core tests/benchmarks inside the `mongory-core` submodule (e.g., via CMake/ctest).
56
+
57
+ ### 3. Build the project
58
+
59
+ Using Rake (recommended):
60
+
61
+ ```bash
62
+ # Automatically initializes the submodule and builds the C extension (no prior CMake build needed)
63
+ bundle exec rake build_all
64
+
65
+ # If rake-compiler is not available, the fallback :compile will directly run extconf.rb + make
66
+ ```
67
+
68
+ Or use the script (optional):
69
+
70
+ ```bash
71
+ ./scripts/build_with_core.sh # Basic build
72
+ ./scripts/build_with_core.sh --debug # Debug build
73
+ ./scripts/build_with_core.sh --help # Show all options
74
+ ```
75
+
76
+ ## Detailed Steps
77
+
78
+ ### Submodule management
79
+
80
+ ```bash
81
+ # Initialize submodule
82
+ rake submodule:init
83
+
84
+ # Update submodule to latest
85
+ rake submodule:update
86
+
87
+ # Manually update submodule
88
+ git submodule update --remote
89
+ ```
90
+
91
+ ### Manual build flow (without rake-compiler)
92
+
93
+ If you prefer to control the build manually:
94
+
95
+ ```bash
96
+ # 1. Ensure submodule is initialized
97
+ git submodule update --init --recursive
98
+
99
+ # 2. (Optional) Only needed if you run core tests/benchmarks inside the submodule
100
+ # Note: This step requires cJSON; typical mongory-rb usage does not.
101
+ # cd ext/mongory_ext/mongory-core
102
+ # ./build.sh --test
103
+ # cd ../../..
104
+
105
+ # 3. Build the Ruby C extension (compiles submodule sources directly via extconf.rb)
106
+ cd ext/mongory_ext
107
+ ruby extconf.rb
108
+ make
109
+ cd ../..
110
+
111
+ # 4. Run tests
112
+ bundle exec rspec
113
+ ```
114
+
115
+ ### Clean builds
116
+
117
+ ```bash
118
+ # Clean all build artifacts
119
+ rake clean_all
120
+
121
+ # Or use the build script
122
+ ./scripts/build_with_core.sh --clean
123
+ ```
124
+
125
+ ## Development Guide
126
+
127
+ ### Modify the C extension
128
+
129
+ 1. Edit `ext/mongory_ext/mongory_ext.c`
130
+ 2. Rebuild:
131
+ ```bash
132
+ cd ext/mongory_ext
133
+ make
134
+ ```
135
+
136
+ ### Update mongory-core
137
+
138
+ 1. Enter the submodule directory:
139
+ ```bash
140
+ cd ext/mongory_ext/mongory-core
141
+ ```
142
+
143
+ 2. Checkout the desired version or branch:
144
+ ```bash
145
+ git checkout main
146
+ git pull origin main
147
+ ```
148
+
149
+ 3. Return to the main project and commit the submodule update:
150
+ ```bash
151
+ cd ../../..
152
+ git add ext/mongory_ext/mongory-core
153
+ git commit -m "Update mongory-core submodule"
154
+ ```
155
+
156
+ ### Debug the C extension
157
+
158
+ ```bash
159
+ # Build in debug mode
160
+ export DEBUG=1
161
+ cd ext/mongory_ext
162
+ ruby extconf.rb
163
+ make
164
+
165
+ # Or use the build script
166
+ ./scripts/build_with_core.sh --debug
167
+ ```
168
+
169
+ Debug mode will:
170
+ - Enable debug symbols (`-g`)
171
+ - Disable optimizations (`-O0`)
172
+ - Define the `DEBUG` macro
173
+
174
+ ### C extension API (current public surface)
175
+
176
+ ```ruby
177
+ require 'mongory'
178
+
179
+ condition = { "age" => { "$gt" => 18 } }
180
+ matcher = Mongory::CMatcher.new(condition)
181
+
182
+ data = { "name" => "John", "age" => 25 }
183
+ result = matcher.match(data) # => true/false
184
+
185
+ # Optional: get explanation (runs explain on the C side, currently returns nil)
186
+ matcher.explain
187
+ ```
188
+
189
+ ## Troubleshooting
190
+
191
+ ### Common issues
192
+
193
+ **1. Submodule is empty**
194
+ ```bash
195
+ # Fix
196
+ git submodule update --init --recursive
197
+ ```
198
+
199
+ **2. cJSON library not found (only required if you run mongory-core CMake tests)**
200
+ ```bash
201
+ # macOS
202
+ brew install cjson
203
+
204
+ # Ubuntu/Debian
205
+ sudo apt install libcjson-dev
206
+
207
+ # Verify installation
208
+ pkg-config --exists libcjson && echo "Found" || echo "Not found"
209
+ ```
210
+
211
+ **3. Build errors**
212
+ ```bash
213
+ # Clean and rebuild
214
+ ./scripts/build_with_core.sh --clean
215
+ ./scripts/build_with_core.sh --debug
216
+ ```
217
+
218
+ **4. C extension fails to load**
219
+ - Check Ruby version compatibility (>= 2.6.0)
220
+ - Ensure all system deps are installed
221
+ - Inspect error messages and compiler warnings
222
+
223
+ ### Logging and debugging
224
+
225
+ Enable verbose output:
226
+ ```bash
227
+ # Verbose during build
228
+ VERBOSE=1 ./scripts/build_with_core.sh
229
+
230
+ # Debug info when loading Ruby
231
+ RUBY_DEBUG=1 ruby -rmongory -e "puts Mongory::CoreInterface.version"
232
+ ```
233
+
234
+ ### Performance check
235
+
236
+ ```ruby
237
+ require 'benchmark'
238
+ require 'mongory'
239
+
240
+ records = 10000.times.map { |i| { "id" => i, "age" => rand(18..65) } }
241
+
242
+
243
+ ```
244
+
245
+ ## CI/CD integration
246
+
247
+ ### GitHub Actions example
248
+
249
+ ```yaml
250
+ name: Build and Test
251
+
252
+ on: [push, pull_request]
253
+
254
+ jobs:
255
+ test:
256
+ runs-on: ubuntu-latest
257
+ strategy:
258
+ matrix:
259
+ ruby-version: ['2.6', '2.7', '3.0', '3.1']
260
+
261
+ steps:
262
+ - uses: actions/checkout@v3
263
+ with:
264
+ submodules: recursive
265
+
266
+ - name: Set up Ruby
267
+ uses: ruby/setup-ruby@v1
268
+ with:
269
+ ruby-version: ${{ matrix.ruby-version }}
270
+ bundler-cache: true
271
+
272
+ - name: Install system dependencies
273
+ run: |
274
+ sudo apt update
275
+ sudo apt install -y build-essential ruby-dev
276
+
277
+ - name: Build with C extension
278
+ run: bundle exec rake build_all
279
+
280
+ - name: Run tests
281
+ run: bundle exec rspec
282
+
283
+ # Only needed if you also run mongory-core C tests/benchmarks (optional):
284
+ # - name: Install CMake & cJSON (only if running core tests)
285
+ # run: sudo apt install -y cmake libcjson-dev
286
+ # - name: Build mongory-core tests (optional)
287
+ # run: |
288
+ # cd ext/mongory_ext/mongory-core
289
+ # ./build.sh --test
290
+ ```
291
+
292
+ ## Contributing Guide
293
+
294
+ ### Development workflow
295
+
296
+ 1. Fork this repository
297
+ 2. Create a feature branch: `git checkout -b feature/amazing-feature`
298
+ 3. If you modify C code, make sure to update tests
299
+ 4. Run the full test suite: `./scripts/build_with_core.sh`
300
+ 5. Commit changes: `git commit -m 'Add amazing feature'`
301
+ 6. Push the branch: `git push origin feature/amazing-feature`
302
+ 7. Open a Pull Request
303
+
304
+ ### Coding standards
305
+
306
+ - **C code**: C99, follow mongory-core style
307
+ - **Ruby code**: follow project RuboCop config
308
+ - **Docs**: document all public APIs
309
+
310
+ ### Testing requirements
311
+
312
+ - Add tests for all new features
313
+ - Ensure both C extension and Ruby fallback are covered
314
+ - Run benchmarks to prevent regressions
315
+
316
+ ## References
317
+
318
+ - [mongory-core doc](https://github.com/mongoryhq/mongory-core)
319
+ - [Ruby C extension doc](https://docs.ruby-lang.org/en/master/extension_rdoc.html)
320
+ - [Git Submodules doc](https://git-scm.com/book/en/v2/Git-Tools-Submodules)
321
+ - [CMake doc](https://cmake.org/documentation/)
322
+
323
+ ## License
324
+
325
+ This integration follows the MIT license, consistent with mongory-core and mongory-rb.
@@ -0,0 +1,40 @@
1
+ # Advanced Usage
2
+
3
+ ## Complex Queries
4
+
5
+ ```ruby
6
+ # Nested conditions
7
+ users.mongory
8
+ .where(
9
+ :age.gte => 18,
10
+ :$or => [
11
+ { :status => 'active' },
12
+ { :status => 'pending', :created_at.gte => 1.week.ago }
13
+ ]
14
+ )
15
+
16
+ # Using any_of for nested OR conditions
17
+ users.mongory
18
+ .where(:age.gte => 18)
19
+ .any_of(
20
+ { :status => 'active' },
21
+ { :status => 'pending', :created_at.gte => 1.week.ago }
22
+ )
23
+
24
+ # Array operations
25
+ posts.mongory
26
+ .where(:tags.elem_match => { :name => 'ruby', :priority.gt => 5 })
27
+ .where(:comments.every => { :approved => true })
28
+ ```
29
+
30
+ ## Integration with ActiveRecord
31
+
32
+ ```ruby
33
+ class User < ActiveRecord::Base
34
+ def active_friends
35
+ friends.mongory
36
+ .where(:status => 'active')
37
+ .where(:last_seen.gte => 1.day.ago)
38
+ end
39
+ end
40
+ ```
@@ -0,0 +1,69 @@
1
+ # Clang Bridge (C Extension)
2
+
3
+ The Clang bridge connects the Ruby DSL to the `mongory-core` engine via a compact C layer. It exposes two key entry points:
4
+
5
+ - `Mongory::CMatcher`: a low-level matcher API backed by C.
6
+ - `QueryBuilder#c`: an ergonomic switch that reuses your current Ruby condition and executes it through `CMatcher`.
7
+
8
+ ## Build/Install
9
+
10
+ ```bash
11
+ bundle install
12
+ bundle exec rake compile
13
+ # or, when installing the gem, the extension will compile automatically if toolchain is present
14
+ ```
15
+
16
+ ## Quick check
17
+
18
+ ```ruby
19
+ require 'mongory'
20
+ if defined?(Mongory::CMatcher)
21
+ puts 'C extension available'
22
+ else
23
+ puts 'C extension not available, using pure Ruby'
24
+ end
25
+ ```
26
+
27
+ ## Basic usage
28
+
29
+ ```ruby
30
+ records = [
31
+ { 'name' => 'Jack', 'age' => 18 },
32
+ { 'name' => 'Jill', 'age' => 15 },
33
+ { 'name' => 'Bob', 'age' => 21 }
34
+ ]
35
+
36
+ # Switch existing Ruby query to C path
37
+ query = records.mongory.c # => returns Mongory::CQueryBuilder
38
+ .where(:age.gte => 18)
39
+
40
+ query.each.to_a # enumerate matches via C
41
+ query.fast.to_a # alias of each
42
+ query.trace.to_a # print value compare progression
43
+ query.explain # print core-level matcher tree
44
+
45
+ # Or use CMatcher directly
46
+ matcher = Mongory::CMatcher.new(:age.gte => 18)
47
+ records.select { |r| matcher.match?(r) }
48
+ ```
49
+
50
+ ## Tracing and debugging
51
+
52
+ ```ruby
53
+ matcher = Mongory::CMatcher.new(:$or => [ { :name.regex => /^J/ }, { :age.gt => 20 } ])
54
+ matcher.enable_trace
55
+ records.each { |r| matcher.match?(r) }
56
+ matcher.print_trace # prints detailed trace
57
+ matcher.disable_trace
58
+
59
+ # Or trace single record compare progression
60
+ matcher.trace(records.first)
61
+ ```
62
+
63
+ ## Notes
64
+
65
+ - Regexes use Ruby's `Regexp` internally; string patterns are compiled once and cached.
66
+ - Context (`Mongory::Utils::Context`) is shared between Ruby and C during matching, enabling custom converters.
67
+ - If the extension fails to load, `Mongory::CQueryBuilder` is unavailable and `.c` will not be used; the Ruby path continues to work.
68
+
69
+
@@ -0,0 +1,30 @@
1
+ # Handling Dots in Field Names
2
+
3
+ Mongory supports field names containing dots, which require escaping:
4
+
5
+ ```ruby
6
+ # Sample data
7
+ records = [
8
+ { "user.name" => "John", "age" => 25 }, # Field name contains a dot
9
+ { "user" => { "name" => "Bob" }, "age" => 30 } # Nested field
10
+ ]
11
+
12
+ # Field name contains a dot
13
+ records.mongory.where("user\\.name" => "John") # Two backslashes needed with double quotes
14
+ # => [{ "user.name" => "John", "age" => 25 }]
15
+
16
+ # or
17
+ records.mongory.where('user\.name' => "John") # One backslash needed with single quotes
18
+ # => [{ "user.name" => "John", "age" => 25 }]
19
+
20
+ # Nested field (no escaping needed)
21
+ records.mongory.where("user.name" => "Bob")
22
+ # => [{ "user" => { "name" => "Bob" }, "age" => 30 }]
23
+ ```
24
+
25
+ Notes:
26
+ - With double quotes, backslashes need to be escaped (`\\`)
27
+ - With single quotes, backslashes don't need to be escaped (`\`)
28
+ - This behavior is consistent with MongoDB's query syntax
29
+ - The escaped dot pattern (`\.`) matches fields where the dot is part of the field name
30
+ - The unescaped dot pattern (`.`) matches nested fields in the document structure
data/docs/migration.md ADDED
@@ -0,0 +1,30 @@
1
+ # Migration Guide
2
+
3
+ ## From Array#select
4
+ ```ruby
5
+ # Before
6
+ records.select { |r| r['age'] >= 18 && r['status'] == 'active' }
7
+
8
+ # After
9
+ records.mongory.where(:age.gte => 18, status: 'active')
10
+ ```
11
+
12
+ ## From ActiveRecord
13
+ ```ruby
14
+ # Before
15
+ indexed_query.where("age >= ? AND status = ?", 18, 'active')
16
+
17
+ # After
18
+ indexed_query.mongory.where(:age.gte => 18, status: 'active')
19
+ ```
20
+
21
+ ## From MongoDB
22
+ ```ruby
23
+ # Before (MongoDB)
24
+ users.where(:age.gte => 18, status: 'active')
25
+
26
+ # After (Mongory)
27
+ users.mongory.where(:age.gte => 18, status: 'active')
28
+
29
+ # Just the same.
30
+ ```
@@ -0,0 +1,61 @@
1
+ # Performance & Benchmarks
2
+
3
+ ## C Extension vs Pure Ruby
4
+
5
+ - C extension provides 3-10x performance improvement for large datasets
6
+ - Automatic fallback to pure Ruby if C extension unavailable
7
+ - Check availability: `defined?(Mongory::CMatcher)` or attempt to require the extension
8
+ - Memory management handled by mongory-core's memory pool
9
+
10
+ ## Memory Usage
11
+
12
+ - Mongory operates entirely in memory
13
+ - Consider your data size and memory constraints
14
+ - Proc-based implementation reduces memory usage
15
+ - Context system provides better memory management
16
+
17
+ ## Query Optimization
18
+
19
+ - Complex conditions are evaluated in sequence
20
+ - Use `explain` to analyze query performance
21
+ - Empty conditions are optimized with cached Procs
22
+ - Context system allows fine-grained control over conversion
23
+
24
+ ## Benchmarks
25
+
26
+ ```ruby
27
+ # Plain Ruby Simple query (100000 records)
28
+ records.select { |r| r['age'].is_a?(Numeric) && r['age'] >= 18 } # ~9ms
29
+
30
+ # Plain Ruby Complex query (100000 records)
31
+ records.select do |r|
32
+ next false unless r.key?('age') && r.key?('status')
33
+
34
+ r['age'].is_a?(Numeric) && r['age'] >= 18 || r['status'] == 'active'
35
+ end # ~20ms
36
+
37
+ # Simple query (100000 records)
38
+ records.mongory.where(:age.gte => 18) # ~119ms
39
+
40
+ # Complex query (100000 records)
41
+ records.mongory.where(:$or => [{:age.gte => 18}, {:status => 'active'}]) # ~107ms
42
+
43
+ # Complex query with fast mode (100000 records)
44
+ records.mongory.where(:$or => [{:age.gte => 18}, {:status => 'active'}]).fast # ~63ms
45
+
46
+ # Simple query with C extension (100000 records)
47
+ records.mongory.c.where(:age.gte => 18) # ~15ms
48
+ # Complex query with C extension (100000 records)
49
+ records.mongory.c.where(:$or => [{:age.gte => 18}, {:status => 'active'}]) # ~23ms, same with plain ruby
50
+ ```
51
+
52
+ Note: Performance varies based on:
53
+
54
+ - Data size
55
+ - Query complexity
56
+ - Hardware specifications
57
+ - Ruby version
58
+
59
+ Benchmark in your environment to validate.
60
+
61
+