|
| 1 | +# JSON-SQL Enhanced - Modern Edition |
| 2 | + |
| 3 | +**A powerful, modern fork of json-sql with comprehensive MongoDB operators and multi-dialect support** |
| 4 | + |
| 5 | +[](https://nodejs.org/) |
| 6 | +[](https://opensource.org/licenses/MIT) |
| 7 | +[](https://github.com/forwardemail/json-sql-enhanced/actions/workflows/ci.yml) |
| 8 | +[](https://github.com/sindresorhus/xo) |
| 9 | +[](https://github.com/prettier/prettier) |
| 10 | +[](LICENSE) |
| 11 | + |
| 12 | +## 🚀 What's New in This Fork |
| 13 | + |
| 14 | +This is a comprehensive modernization and enhancement of the original [json-sql](https://github.com/2do2go/json-sql) library, featuring: |
| 15 | + |
| 16 | +### ✨ **Modern JavaScript & Tooling** |
| 17 | + |
| 18 | +- **Node.js 18+ support** with modern ES features |
| 19 | +- **Zero dependencies** - removed underscore.js, using native JavaScript |
| 20 | +- **Modern development workflow** with Prettier, XO, ESLint |
| 21 | +- **Ava test framework** replacing Mocha/Chai |
| 22 | +- **Git hooks** with Husky, lint-staged, and commitlint |
| 23 | +- **2-space indentation** throughout codebase |
| 24 | + |
| 25 | +### 🔥 **Enhanced MongoDB Operators** |
| 26 | + |
| 27 | +- **`$regex`** with `$options` support for case-insensitive pattern matching |
| 28 | +- **`$size`** for array length queries |
| 29 | +- **`$exists`** for field existence checks |
| 30 | +- **`$elemMatch`** for complex array element matching |
| 31 | +- **Field-level `$not`** for negation |
| 32 | +- **Complex `$and`/`$or`** with nested logical operations |
| 33 | + |
| 34 | +### 🗄️ **Multi-Dialect Optimization** |
| 35 | + |
| 36 | +- **PostgreSQL**: Native `~`, `~*` operators, ILIKE, JSONB functions |
| 37 | +- **MySQL**: REGEXP operator, JSON functions, case-insensitive handling |
| 38 | +- **SQL Server**: Pattern approximation, OPENJSON for arrays |
| 39 | +- **SQLite**: Progressive fallback (LIKE → GLOB → REGEXP), JSON1 support |
| 40 | + |
| 41 | +### 🐛 **GitHub Issues Fixed** |
| 42 | + |
| 43 | +- **#57**: Empty objects `{}` convert to `NULL` |
| 44 | +- **#56**: Buffer support with automatic hex conversion |
| 45 | +- **#55**: BSON ObjectId support with `toHexString()` method |
| 46 | + |
| 47 | +## 📦 Installation |
| 48 | + |
| 49 | +```bash |
| 50 | +npm install json-sql-enhanced |
| 51 | +``` |
| 52 | + |
| 53 | +## 🎯 Quick Start |
| 54 | + |
| 55 | +```javascript |
| 56 | +const jsonSql = require('json-sql-enhanced')(); |
| 57 | + |
| 58 | +// Basic query |
| 59 | +const result = jsonSql.build({ |
| 60 | + type: 'select', |
| 61 | + table: 'users', |
| 62 | + condition: { |
| 63 | + name: { $regex: 'John', $options: 'i' }, |
| 64 | + age: { $gt: 18 }, |
| 65 | + emails: { $size: { $gt: 0 } }, |
| 66 | + }, |
| 67 | +}); |
| 68 | + |
| 69 | +console.log(result.query); |
| 70 | +// Output: select * from "users" where "name" ILIKE $p1 and "age" > $p2 and JSON_LENGTH("emails") > $p3 |
| 71 | + |
| 72 | +console.log(result.values); |
| 73 | +// Output: { p1: '%John%', p2: 18, p3: 0 } |
| 74 | +``` |
| 75 | + |
| 76 | +## 🔍 MongoDB Operators |
| 77 | + |
| 78 | +### **$regex with $options** |
| 79 | + |
| 80 | +```javascript |
| 81 | +// Case-insensitive pattern matching |
| 82 | +{ fullName: { $regex: 'John', $options: 'i' } } |
| 83 | +// → PostgreSQL: "fullName" ILIKE '%John%' |
| 84 | +// → MySQL: LOWER("fullName") LIKE LOWER('%John%') |
| 85 | +// → SQLite: "fullName" LIKE '%John%' COLLATE NOCASE |
| 86 | + |
| 87 | +// Pattern optimization |
| 88 | +{ name: { $regex: '^John' } } // → "name" LIKE 'John%' |
| 89 | +{ name: { $regex: 'Smith$' } } // → "name" LIKE '%Smith' |
| 90 | +{ name: { $regex: '^John$' } } // → "name" = 'John' |
| 91 | +``` |
| 92 | + |
| 93 | +### **$elemMatch for Arrays** |
| 94 | + |
| 95 | +```javascript |
| 96 | +// Complex array element matching |
| 97 | +{ |
| 98 | + emails: { |
| 99 | + $elemMatch: { |
| 100 | + value: { $regex: '@company\\.com$', $options: 'i' }, |
| 101 | + type: 'work' |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | +// → PostgreSQL: EXISTS(SELECT 1 FROM jsonb_array_elements("emails") elem WHERE ...) |
| 106 | +// → MySQL: JSON_SEARCH("emails", 'one', '%@company.com%') IS NOT NULL |
| 107 | +// → SQLite: EXISTS(SELECT 1 FROM json_each("emails") WHERE ...) |
| 108 | +``` |
| 109 | + |
| 110 | +### **$size for Array Length** |
| 111 | + |
| 112 | +```javascript |
| 113 | +{ |
| 114 | + tags: { |
| 115 | + $size: 3; |
| 116 | + } |
| 117 | +} // → JSON_LENGTH("tags") = 3 |
| 118 | +{ |
| 119 | + emails: { |
| 120 | + $size: { |
| 121 | + $gt: 0; |
| 122 | + } |
| 123 | + } |
| 124 | +} // → JSON_LENGTH("emails") > 0 |
| 125 | +``` |
| 126 | + |
| 127 | +### **$exists for Field Presence** |
| 128 | + |
| 129 | +```javascript |
| 130 | +{ |
| 131 | + email: { |
| 132 | + $exists: true; |
| 133 | + } |
| 134 | +} // → "email" IS NOT NULL |
| 135 | +{ |
| 136 | + phone: { |
| 137 | + $exists: false; |
| 138 | + } |
| 139 | +} // → "phone" IS NULL |
| 140 | +``` |
| 141 | + |
| 142 | +### **Complex Logical Operations** |
| 143 | + |
| 144 | +```javascript |
| 145 | +{ |
| 146 | + $and: [ |
| 147 | + { age: { $gte: 18 } }, |
| 148 | + { |
| 149 | + $or: [{ status: 'active' }, { emails: { $size: { $gt: 0 } } }], |
| 150 | + }, |
| 151 | + ]; |
| 152 | +} |
| 153 | +``` |
| 154 | + |
| 155 | +## 🌐 Multi-Dialect Support |
| 156 | + |
| 157 | +```javascript |
| 158 | +// PostgreSQL optimizations |
| 159 | +const pgSql = require('json-sql-enhanced')({ dialect: 'postgresql' }); |
| 160 | + |
| 161 | +// MySQL optimizations |
| 162 | +const mysqlSql = require('json-sql-enhanced')({ dialect: 'mysql' }); |
| 163 | + |
| 164 | +// SQLite optimizations |
| 165 | +const sqliteSql = require('json-sql-enhanced')({ dialect: 'sqlite' }); |
| 166 | + |
| 167 | +// SQL Server support |
| 168 | +const mssqlSql = require('json-sql-enhanced')({ dialect: 'mssql' }); |
| 169 | +``` |
| 170 | + |
| 171 | +## 🔄 Migration from Original json-sql |
| 172 | + |
| 173 | +This fork is **100% backward compatible**. Simply replace your import: |
| 174 | + |
| 175 | +```javascript |
| 176 | +// Before |
| 177 | +const jsonSql = require('json-sql')(); |
| 178 | + |
| 179 | +// After |
| 180 | +const jsonSql = require('json-sql-enhanced')(); |
| 181 | + |
| 182 | +// All existing code works unchanged! |
| 183 | +``` |
| 184 | + |
| 185 | +## 🧪 Example Queries |
| 186 | + |
| 187 | +All these complex MongoDB-style queries are supported: |
| 188 | + |
| 189 | +```javascript |
| 190 | +// Case-insensitive regex with options |
| 191 | +{ fullName: { $regex: 'John', $options: 'i' } } |
| 192 | + |
| 193 | +// Array element matching with nested conditions |
| 194 | +{ |
| 195 | + emails: { |
| 196 | + $elemMatch: { |
| 197 | + value: { $regex: 'john@example\\.com', $options: 'i' } |
| 198 | + } |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +// Complex logical operations |
| 203 | +{ |
| 204 | + $or: [ |
| 205 | + { emails: { $exists: false } }, |
| 206 | + { emails: { $size: 0 } } |
| 207 | + ] |
| 208 | +} |
| 209 | + |
| 210 | +// Negation with nested operators |
| 211 | +{ |
| 212 | + $not: { |
| 213 | + emails: { |
| 214 | + $elemMatch: { type: { $regex: '^WORK$', $options: 'i' } } |
| 215 | + } |
| 216 | + } |
| 217 | +} |
| 218 | + |
| 219 | +// Multiple conditions with $and |
| 220 | +{ |
| 221 | + $and: [ |
| 222 | + { |
| 223 | + emails: { |
| 224 | + $elemMatch: { value: { $regex: '@example\\.com', $options: 'i' } } |
| 225 | + } |
| 226 | + }, |
| 227 | + { |
| 228 | + emails: { $elemMatch: { value: { $regex: '^john', $options: 'i' } } } |
| 229 | + } |
| 230 | + ] |
| 231 | +} |
| 232 | +``` |
| 233 | + |
| 234 | +## 🛠️ Development |
| 235 | + |
| 236 | +```bash |
| 237 | +# Install dependencies |
| 238 | +npm install |
| 239 | + |
| 240 | +# Run tests |
| 241 | +npm test |
| 242 | + |
| 243 | +# Format code |
| 244 | +npm run format |
| 245 | + |
| 246 | +# Lint code |
| 247 | +npm run lint |
| 248 | + |
| 249 | +# Fix linting issues |
| 250 | +npm run lint:fix |
| 251 | +``` |
| 252 | + |
| 253 | +## 📋 Requirements |
| 254 | + |
| 255 | +- **Node.js 18+** |
| 256 | +- **Modern JavaScript environment** |
| 257 | + |
| 258 | +## 🤝 Contributing |
| 259 | + |
| 260 | +1. Fork the repository |
| 261 | +2. Create your feature branch (`git checkout -b feature/amazing-feature`) |
| 262 | +3. Commit your changes (`git commit -m 'feat: add amazing feature'`) |
| 263 | +4. Push to the branch (`git push origin feature/amazing-feature`) |
| 264 | +5. Open a Pull Request |
| 265 | + |
| 266 | +## 📄 License |
| 267 | + |
| 268 | +MIT License - see the [LICENSE](LICENSE) file for details. |
| 269 | + |
| 270 | +## 🙏 Acknowledgments |
| 271 | + |
| 272 | +- Original [json-sql](https://github.com/2do2go/json-sql) library by 2do2go |
| 273 | +- MongoDB query syntax inspiration |
| 274 | +- Modern JavaScript community for best practices |
| 275 | + |
| 276 | +## 📚 Documentation |
| 277 | + |
| 278 | +For detailed documentation, examples, and API reference, see the [docs](./docs) directory: |
| 279 | + |
| 280 | +- [MongoDB Operators Guide](./docs/MONGODB-OPERATORS.md) |
| 281 | +- [Multi-Dialect Support](./docs/README.md) |
| 282 | +- [GitHub Issues Fixed](./docs/GITHUB-ISSUES.md) |
| 283 | +- [Changelog](./docs/CHANGELOG.md) |
| 284 | + |
| 285 | +--- |
| 286 | + |
| 287 | +**Made with ❤️ for the JavaScript community** |
0 commit comments