jexidb 2.1.1 → 2.1.3
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.
- package/dist/Database.cjs +7981 -231
- package/package.json +9 -2
- package/src/Database.mjs +372 -154
- package/src/SchemaManager.mjs +325 -268
- package/src/Serializer.mjs +20 -1
- package/src/managers/QueryManager.mjs +74 -18
- package/.babelrc +0 -13
- package/.gitattributes +0 -2
- package/CHANGELOG.md +0 -140
- package/babel.config.json +0 -5
- package/docs/API.md +0 -1057
- package/docs/EXAMPLES.md +0 -701
- package/docs/README.md +0 -194
- package/examples/iterate-usage-example.js +0 -157
- package/examples/simple-iterate-example.js +0 -115
- package/jest.config.js +0 -24
- package/scripts/README.md +0 -47
- package/scripts/benchmark-array-serialization.js +0 -108
- package/scripts/clean-test-files.js +0 -75
- package/scripts/prepare.js +0 -31
- package/scripts/run-tests.js +0 -80
- package/scripts/score-mode-demo.js +0 -45
- package/test/$not-operator-with-and.test.js +0 -282
- package/test/README.md +0 -8
- package/test/close-init-cycle.test.js +0 -256
- package/test/coverage-method.test.js +0 -93
- package/test/critical-bugs-fixes.test.js +0 -1069
- package/test/deserialize-corruption-fixes.test.js +0 -296
- package/test/exists-method.test.js +0 -318
- package/test/explicit-indexes-comparison.test.js +0 -219
- package/test/filehandler-non-adjacent-ranges-bug.test.js +0 -175
- package/test/index-line-number-regression.test.js +0 -100
- package/test/index-missing-index-data.test.js +0 -91
- package/test/index-persistence.test.js +0 -491
- package/test/index-serialization.test.js +0 -314
- package/test/indexed-query-mode.test.js +0 -360
- package/test/insert-session-auto-flush.test.js +0 -353
- package/test/iterate-method.test.js +0 -272
- package/test/legacy-operator-compat.test.js +0 -154
- package/test/query-operators.test.js +0 -238
- package/test/regex-array-fields.test.js +0 -129
- package/test/score-method.test.js +0 -298
- package/test/setup.js +0 -17
- package/test/term-mapping-minimal.test.js +0 -154
- package/test/term-mapping-simple.test.js +0 -257
- package/test/term-mapping.test.js +0 -514
- package/test/writebuffer-flush-resilience.test.js +0 -204
|
@@ -337,9 +337,26 @@ export class QueryManager {
|
|
|
337
337
|
}
|
|
338
338
|
}
|
|
339
339
|
|
|
340
|
+
// Handle $not operator - include it if it can be processed by IndexManager
|
|
341
|
+
if (criteria.$not && typeof criteria.$not === 'object') {
|
|
342
|
+
// Check if $not condition contains only indexable fields
|
|
343
|
+
const notFields = Object.keys(criteria.$not);
|
|
344
|
+
const allNotFieldsIndexed = notFields.every(field =>
|
|
345
|
+
this.indexManager.opts.indexes && this.indexManager.opts.indexes[field]
|
|
346
|
+
);
|
|
347
|
+
|
|
348
|
+
if (allNotFieldsIndexed && notFields.length > 0) {
|
|
349
|
+
// Extract indexable criteria from $not condition
|
|
350
|
+
const indexableNotCriteria = this._extractIndexableCriteria(criteria.$not);
|
|
351
|
+
if (Object.keys(indexableNotCriteria).length > 0) {
|
|
352
|
+
indexableCriteria.$not = indexableNotCriteria;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
340
357
|
// Handle regular field conditions
|
|
341
358
|
for (const [field, condition] of Object.entries(criteria)) {
|
|
342
|
-
if (field.startsWith('$')) continue; // Skip logical operators
|
|
359
|
+
if (field.startsWith('$')) continue; // Skip logical operators (already handled above)
|
|
343
360
|
|
|
344
361
|
// RegExp conditions cannot be pre-filtered using indices
|
|
345
362
|
if (condition instanceof RegExp) {
|
|
@@ -690,30 +707,69 @@ export class QueryManager {
|
|
|
690
707
|
// Read specific records using the line numbers
|
|
691
708
|
if (lineNumbers.size > 0) {
|
|
692
709
|
const lineNumbersArray = Array.from(lineNumbers)
|
|
693
|
-
const
|
|
694
|
-
const groupedRanges = await this.database.fileHandler.groupedRanges(ranges)
|
|
710
|
+
const persistedCount = Array.isArray(this.database.offsets) ? this.database.offsets.length : 0
|
|
695
711
|
|
|
696
|
-
|
|
697
|
-
const
|
|
712
|
+
// Separate lineNumbers into file records and writeBuffer records
|
|
713
|
+
const fileLineNumbers = []
|
|
714
|
+
const writeBufferLineNumbers = []
|
|
698
715
|
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
716
|
+
for (const lineNumber of lineNumbersArray) {
|
|
717
|
+
if (lineNumber >= persistedCount) {
|
|
718
|
+
// This lineNumber points to writeBuffer
|
|
719
|
+
writeBufferLineNumbers.push(lineNumber)
|
|
720
|
+
} else {
|
|
721
|
+
// This lineNumber points to file
|
|
722
|
+
fileLineNumbers.push(lineNumber)
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
// Read records from file
|
|
727
|
+
if (fileLineNumbers.length > 0) {
|
|
728
|
+
const ranges = this.database.getRanges(fileLineNumbers)
|
|
729
|
+
if (ranges.length > 0) {
|
|
730
|
+
const groupedRanges = await this.database.fileHandler.groupedRanges(ranges)
|
|
731
|
+
|
|
732
|
+
const fs = await import('fs')
|
|
733
|
+
const fd = await fs.promises.open(this.database.fileHandler.file, 'r')
|
|
734
|
+
|
|
735
|
+
try {
|
|
736
|
+
for (const groupedRange of groupedRanges) {
|
|
737
|
+
for await (const row of this.database.fileHandler.readGroupedRange(groupedRange, fd)) {
|
|
738
|
+
try {
|
|
739
|
+
const record = this.database.serializer.deserialize(row.line)
|
|
740
|
+
const recordWithTerms = options.restoreTerms !== false ?
|
|
741
|
+
this.database.restoreTermIdsAfterDeserialization(record) :
|
|
742
|
+
record
|
|
743
|
+
results.push(recordWithTerms)
|
|
744
|
+
if (limit && results.length >= limit) break
|
|
745
|
+
} catch (error) {
|
|
746
|
+
// Skip invalid lines
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
if (limit && results.length >= limit) break
|
|
750
|
+
}
|
|
751
|
+
} finally {
|
|
752
|
+
await fd.close()
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// Read records from writeBuffer
|
|
758
|
+
if (writeBufferLineNumbers.length > 0 && this.database.writeBuffer) {
|
|
759
|
+
for (const lineNumber of writeBufferLineNumbers) {
|
|
760
|
+
if (limit && results.length >= limit) break
|
|
761
|
+
|
|
762
|
+
const writeBufferIndex = lineNumber - persistedCount
|
|
763
|
+
if (writeBufferIndex >= 0 && writeBufferIndex < this.database.writeBuffer.length) {
|
|
764
|
+
const record = this.database.writeBuffer[writeBufferIndex]
|
|
765
|
+
if (record) {
|
|
704
766
|
const recordWithTerms = options.restoreTerms !== false ?
|
|
705
767
|
this.database.restoreTermIdsAfterDeserialization(record) :
|
|
706
768
|
record
|
|
707
769
|
results.push(recordWithTerms)
|
|
708
|
-
if (limit && results.length >= limit) break
|
|
709
|
-
} catch (error) {
|
|
710
|
-
// Skip invalid lines
|
|
711
770
|
}
|
|
712
771
|
}
|
|
713
|
-
if (limit && results.length >= limit) break
|
|
714
772
|
}
|
|
715
|
-
} finally {
|
|
716
|
-
await fd.close()
|
|
717
773
|
}
|
|
718
774
|
}
|
|
719
775
|
|
|
@@ -1138,8 +1194,8 @@ export class QueryManager {
|
|
|
1138
1194
|
}
|
|
1139
1195
|
|
|
1140
1196
|
const allFieldsIndexed = Object.keys(criteria).every(field => {
|
|
1141
|
-
// Skip $and as
|
|
1142
|
-
if (field === '$and') return true;
|
|
1197
|
+
// Skip $and and $not as they're handled separately above
|
|
1198
|
+
if (field === '$and' || field === '$not') return true;
|
|
1143
1199
|
|
|
1144
1200
|
if (!this.opts.indexes || !this.opts.indexes[field]) {
|
|
1145
1201
|
if (this.opts.debugMode) {
|
package/.babelrc
DELETED
package/.gitattributes
DELETED
package/CHANGELOG.md
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
## [2.1.0] - 2024-12-19
|
|
9
|
-
|
|
10
|
-
### ⚠️ BREAKING CHANGES
|
|
11
|
-
|
|
12
|
-
This version is **NOT backward compatible** with databases created with previous versions.
|
|
13
|
-
|
|
14
|
-
### 🚀 Major Features
|
|
15
|
-
|
|
16
|
-
#### **Term Mapping Auto-Detection**
|
|
17
|
-
|
|
18
|
-
- **BREAKING**: `termMapping` is now `true` by default (was `false`)
|
|
19
|
-
- **BREAKING**: `termMappingFields` is now auto-detected from `indexes` (was manual configuration)
|
|
20
|
-
- **NEW**: Automatic detection of `string` and `array:string` fields for term mapping
|
|
21
|
-
- **NEW**: Zero-configuration term mapping for optimal performance
|
|
22
|
-
|
|
23
|
-
#### **Schema Requirements**
|
|
24
|
-
|
|
25
|
-
- **BREAKING**: `fields` option is now **MANDATORY** (was optional)
|
|
26
|
-
- **NEW**: Clear distinction between `fields` (schema definition) and `indexes` (performance optimization)
|
|
27
|
-
- **NEW**: Enhanced schema validation and error messages
|
|
28
|
-
|
|
29
|
-
#### **Index Management**
|
|
30
|
-
|
|
31
|
-
- **BREAKING**: `array:string` fields now use term IDs in indexes (was string values)
|
|
32
|
-
- **BREAKING**: `array:number` fields use direct numeric values (was incorrectly term-mapped)
|
|
33
|
-
- **NEW**: Improved index performance for array fields
|
|
34
|
-
- **NEW**: Better memory usage for repetitive string data
|
|
35
|
-
|
|
36
|
-
### 🔧 Improvements
|
|
37
|
-
|
|
38
|
-
#### **Database Constructor**
|
|
39
|
-
|
|
40
|
-
- **BREAKING**: `fields` parameter is now required
|
|
41
|
-
- **NEW**: Auto-detection of term mapping fields
|
|
42
|
-
- **NEW**: Enhanced error messages for missing schema
|
|
43
|
-
- **NEW**: Better validation of field types
|
|
44
|
-
|
|
45
|
-
#### **Query Performance**
|
|
46
|
-
|
|
47
|
-
- **NEW**: Optimized query processing for term-mapped fields
|
|
48
|
-
- **NEW**: Improved `$in` operator handling for arrays
|
|
49
|
-
- **NEW**: Better support for mixed field types in queries
|
|
50
|
-
|
|
51
|
-
#### **Documentation**
|
|
52
|
-
|
|
53
|
-
- **NEW**: Complete API documentation overhaul
|
|
54
|
-
- **NEW**: Practical examples with proper schema usage
|
|
55
|
-
- **NEW**: Performance optimization guidelines
|
|
56
|
-
- **NEW**: Migration guide for version 2.x
|
|
57
|
-
|
|
58
|
-
### 🐛 Bug Fixes
|
|
59
|
-
|
|
60
|
-
- Fixed `array:string` fields incorrectly using string values instead of term IDs
|
|
61
|
-
- Fixed `array:number` fields being incorrectly term-mapped
|
|
62
|
-
- Fixed term mapping not being enabled by default
|
|
63
|
-
- Fixed missing `termMappingFields` property on TermManager
|
|
64
|
-
- Fixed IndexManager not correctly identifying term mapping fields
|
|
65
|
-
|
|
66
|
-
### 📚 Documentation Updates
|
|
67
|
-
|
|
68
|
-
- **NEW**: Comprehensive API reference with examples
|
|
69
|
-
- **NEW**: Schema vs Indexes distinction clearly explained
|
|
70
|
-
- **NEW**: Performance tips and best practices
|
|
71
|
-
- **NEW**: Migration guide for existing users
|
|
72
|
-
- **NEW**: `beginInsertSession()` documentation
|
|
73
|
-
|
|
74
|
-
### 🔄 Migration Guide
|
|
75
|
-
|
|
76
|
-
#### **For Existing Users (1.x.x → 2.1.0)**
|
|
77
|
-
|
|
78
|
-
1. **Update your database initialization:**
|
|
79
|
-
|
|
80
|
-
```javascript
|
|
81
|
-
// ❌ OLD (1.x.x)
|
|
82
|
-
const db = new Database('db.jdb', {
|
|
83
|
-
indexes: { name: 'string', tags: 'array:string' }
|
|
84
|
-
})
|
|
85
|
-
|
|
86
|
-
// ✅ NEW (2.1.0)
|
|
87
|
-
const db = new Database('db.jdb', {
|
|
88
|
-
fields: { // REQUIRED - Define schema
|
|
89
|
-
id: 'number',
|
|
90
|
-
name: 'string',
|
|
91
|
-
tags: 'array:string'
|
|
92
|
-
},
|
|
93
|
-
indexes: { // OPTIONAL - Performance optimization
|
|
94
|
-
name: 'string',
|
|
95
|
-
tags: 'array:string'
|
|
96
|
-
}
|
|
97
|
-
})
|
|
98
|
-
```
|
|
99
|
-
2. **Database files are NOT compatible:**
|
|
100
|
-
|
|
101
|
-
- Existing `.jdb` files from 1.x.x will not work with 2.1.0
|
|
102
|
-
- You need to export data from 1.x.x and re-import to 2.1.0
|
|
103
|
-
- Consider this a fresh start for your database files
|
|
104
|
-
3. **Term mapping is now automatic:**
|
|
105
|
-
|
|
106
|
-
- No need to manually configure `termMapping: true`
|
|
107
|
-
- No need to specify `termMappingFields`
|
|
108
|
-
- Fields are auto-detected from your `indexes` configuration
|
|
109
|
-
|
|
110
|
-
### 🎯 Performance Improvements
|
|
111
|
-
|
|
112
|
-
- **Up to 77% reduction** in database size for repetitive string data
|
|
113
|
-
- **Faster queries** on term-mapped fields
|
|
114
|
-
- **Better memory usage** for large datasets
|
|
115
|
-
- **Optimized indexing** for array fields
|
|
116
|
-
|
|
117
|
-
### 🧪 Testing
|
|
118
|
-
|
|
119
|
-
- **NEW**: Comprehensive test suite for term mapping
|
|
120
|
-
- **NEW**: Performance benchmarks for large datasets
|
|
121
|
-
- **NEW**: Migration compatibility tests
|
|
122
|
-
- **NEW**: Edge case testing for array fields
|
|
123
|
-
|
|
124
|
-
---
|
|
125
|
-
|
|
126
|
-
## [1.1.0] - Previous Version
|
|
127
|
-
|
|
128
|
-
### Features
|
|
129
|
-
|
|
130
|
-
- Basic database functionality
|
|
131
|
-
- Manual term mapping configuration
|
|
132
|
-
- Optional schema definition
|
|
133
|
-
- Basic indexing support
|
|
134
|
-
|
|
135
|
-
### Limitations
|
|
136
|
-
|
|
137
|
-
- Term mapping required manual configuration
|
|
138
|
-
- Schema was optional, leading to confusion
|
|
139
|
-
- Array fields had indexing issues
|
|
140
|
-
- Performance not optimized for repetitive data
|