fix(classes.doc (convertFilterForMongoDb)): Improve filter conversion: handle logical operators, merge operator objects, add nested filter tests and docs, and fix test script
This commit is contained in:
202
readme.md
202
readme.md
@@ -144,40 +144,130 @@ await foundUser.delete();
|
||||
|
||||
SmartData provides the most advanced type-safe filtering system for MongoDB, supporting all operators while maintaining full IntelliSense:
|
||||
|
||||
#### Basic Filtering
|
||||
|
||||
```typescript
|
||||
// Simple equality
|
||||
const john = await User.getInstances({ name: 'John Doe' });
|
||||
|
||||
// Multiple fields (implicit AND)
|
||||
const activeAdults = await User.getInstances({
|
||||
status: 'active',
|
||||
age: { $gte: 18 }
|
||||
});
|
||||
|
||||
// Union types work perfectly
|
||||
const users = await User.getInstances({
|
||||
status: { $in: ['active', 'pending'] } // TypeScript validates these values!
|
||||
});
|
||||
```
|
||||
|
||||
// Comparison operators with type checking
|
||||
#### Deep Nested Object Filtering
|
||||
|
||||
SmartData supports **both** nested object notation and dot notation for querying nested fields, with intelligent merging when both are used:
|
||||
|
||||
```typescript
|
||||
// Nested object notation - natural TypeScript syntax
|
||||
const users = await User.getInstances({
|
||||
metadata: {
|
||||
loginCount: { $gte: 5 }
|
||||
}
|
||||
});
|
||||
|
||||
// Dot notation - MongoDB style
|
||||
const sameUsers = await User.getInstances({
|
||||
'metadata.loginCount': { $gte: 5 }
|
||||
});
|
||||
|
||||
// POWERFUL: Combine both notations - operators are merged!
|
||||
const filtered = await User.getInstances({
|
||||
metadata: { loginCount: { $gte: 3 } }, // Object notation
|
||||
'metadata.loginCount': { $lte: 10 } // Dot notation
|
||||
// Result: metadata.loginCount between 3 and 10
|
||||
});
|
||||
|
||||
// Deep nesting with full type safety
|
||||
const deepQuery = await User.getInstances({
|
||||
profile: {
|
||||
settings: {
|
||||
notifications: {
|
||||
email: true,
|
||||
frequency: { $in: ['daily', 'weekly'] }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Mix styles for complex queries
|
||||
const advanced = await User.getInstances({
|
||||
// Object notation for structure
|
||||
profile: {
|
||||
age: { $gte: 21 },
|
||||
verified: true
|
||||
},
|
||||
// Dot notation for specific overrides
|
||||
'profile.settings.theme': 'dark',
|
||||
'profile.lastSeen': { $gte: new Date('2024-01-01') }
|
||||
});
|
||||
```
|
||||
|
||||
#### Comparison Operators
|
||||
|
||||
```typescript
|
||||
// Numeric comparisons with type checking
|
||||
const adults = await User.getInstances({
|
||||
age: { $gte: 18, $lt: 65 } // Type-safe numeric comparisons
|
||||
});
|
||||
|
||||
// Date comparisons
|
||||
const recentUsers = await User.getInstances({
|
||||
createdAt: { $gte: new Date('2024-01-01') }
|
||||
});
|
||||
|
||||
// Not equal
|
||||
const nonAdmins = await User.getInstances({
|
||||
role: { $ne: 'admin' }
|
||||
});
|
||||
```
|
||||
|
||||
#### Array Operations
|
||||
|
||||
```typescript
|
||||
// Array operations with full type safety
|
||||
const experts = await User.getInstances({
|
||||
tags: { $all: ['typescript', 'mongodb'] }, // Must have all tags
|
||||
skills: { $size: 5 } // Exactly 5 skills
|
||||
});
|
||||
|
||||
// Complex nested queries
|
||||
// Array element matching
|
||||
const results = await Order.getInstances({
|
||||
items: {
|
||||
$elemMatch: { // Match array elements
|
||||
product: 'laptop',
|
||||
quantity: { $gte: 2 }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Check if value exists in array field
|
||||
const nodeUsers = await User.getInstances({
|
||||
skills: { $in: ['nodejs'] } // Has nodejs in skills array
|
||||
});
|
||||
```
|
||||
|
||||
#### Logical Operators
|
||||
|
||||
```typescript
|
||||
// Complex nested queries with $and
|
||||
const results = await Order.getInstances({
|
||||
$and: [
|
||||
{ status: { $in: ['pending', 'processing'] } },
|
||||
{ 'items.price': { $gte: 100 } }, // Dot notation for nested fields
|
||||
{
|
||||
items: {
|
||||
$elemMatch: { // Array element matching
|
||||
product: 'laptop',
|
||||
quantity: { $gte: 2 }
|
||||
}
|
||||
}
|
||||
}
|
||||
{ 'items.price': { $gte: 100 } },
|
||||
{ customer: { verified: true } }
|
||||
]
|
||||
});
|
||||
|
||||
// Logical operators
|
||||
// $or operator
|
||||
const urgentOrHighValue = await Order.getInstances({
|
||||
$or: [
|
||||
{ priority: 'urgent' },
|
||||
@@ -185,8 +275,94 @@ const urgentOrHighValue = await Order.getInstances({
|
||||
]
|
||||
});
|
||||
|
||||
// Security: $where is automatically blocked
|
||||
// $nor operator - none of the conditions
|
||||
const excluded = await User.getInstances({
|
||||
$nor: [
|
||||
{ status: 'banned' },
|
||||
{ role: 'guest' }
|
||||
]
|
||||
});
|
||||
|
||||
// Combine logical operators
|
||||
const complex = await Order.getInstances({
|
||||
$and: [
|
||||
{ status: 'active' },
|
||||
{
|
||||
$or: [
|
||||
{ priority: 'high' },
|
||||
{ value: { $gte: 1000 } }
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
#### Element Operators
|
||||
|
||||
```typescript
|
||||
// Check field existence
|
||||
const withEmail = await User.getInstances({
|
||||
email: { $exists: true }
|
||||
});
|
||||
|
||||
// Check for null or missing nested fields
|
||||
const noPreferences = await User.getInstances({
|
||||
'profile.preferences': { $exists: false }
|
||||
});
|
||||
```
|
||||
|
||||
#### Text and Pattern Matching
|
||||
|
||||
```typescript
|
||||
// Regex patterns
|
||||
const gmailUsers = await User.getInstances({
|
||||
email: { $regex: '@gmail\\.com$', $options: 'i' }
|
||||
});
|
||||
|
||||
// Starts with pattern
|
||||
const johnUsers = await User.getInstances({
|
||||
name: { $regex: '^John' }
|
||||
});
|
||||
```
|
||||
|
||||
#### Security Features
|
||||
|
||||
```typescript
|
||||
// Security: $where is automatically blocked for injection protection
|
||||
// await User.getInstances({ $where: '...' }); // ❌ Throws security error
|
||||
|
||||
// Invalid operators are caught
|
||||
// await User.getInstances({ $badOp: 'value' }); // ⚠️ Warning logged
|
||||
```
|
||||
|
||||
#### Advanced Patterns
|
||||
|
||||
```typescript
|
||||
// Combine multiple filtering approaches
|
||||
const advancedQuery = await User.getInstances({
|
||||
// Direct field matching
|
||||
status: 'active',
|
||||
|
||||
// Nested object with operators
|
||||
profile: {
|
||||
age: { $gte: 18, $lte: 65 },
|
||||
verified: true
|
||||
},
|
||||
|
||||
// Dot notation for deep paths
|
||||
'settings.notifications.email': true,
|
||||
'metadata.lastLogin': { $gte: new Date(Date.now() - 30*24*60*60*1000) },
|
||||
|
||||
// Array operations
|
||||
roles: { $in: ['admin', 'moderator'] },
|
||||
tags: { $all: ['verified', 'premium'] },
|
||||
|
||||
// Logical grouping
|
||||
$or: [
|
||||
{ 'subscription.plan': 'premium' },
|
||||
{ 'subscription.trial': true }
|
||||
]
|
||||
});
|
||||
```
|
||||
|
||||
### 🔎 Powerful Search Engine
|
||||
|
Reference in New Issue
Block a user