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:
2025-08-18 20:24:16 +00:00
parent 8b37ebc8f9
commit 0dbaa1bc5d
6 changed files with 462 additions and 24 deletions

View File

@@ -213,6 +213,221 @@ tap.test('should filter by nested object fields', async () => {
expect(users[0].name).toEqual('John Doe');
});
// ============= COMPREHENSIVE NESTED FILTER TESTS =============
tap.test('should filter by nested object with direct object syntax', async () => {
// Direct nested object matching (exact match)
const users = await TestUser.getInstances({
metadata: {
loginCount: 5,
lastLogin: (await TestUser.getInstances({}))[0].metadata.lastLogin // Get the exact date
}
});
expect(users.length).toEqual(1);
expect(users[0].name).toEqual('John Doe');
});
tap.test('should filter by partial nested object match', async () => {
// When using object syntax, only specified fields must match
const users = await TestUser.getInstances({
metadata: { loginCount: 5 } // Only checks loginCount, ignores other fields
});
expect(users.length).toEqual(1);
expect(users[0].name).toEqual('John Doe');
});
tap.test('should combine nested object and dot notation', async () => {
const users = await TestUser.getInstances({
metadata: { loginCount: { $gte: 3 } }, // Object syntax with operator
'metadata.loginCount': { $lte: 10 } // Dot notation with operator
});
expect(users.length).toEqual(3); // Jane (3), John (5), and Alice (10) have loginCount between 3-10
});
tap.test('should filter nested fields with operators using dot notation', async () => {
const users = await TestUser.getInstances({
'metadata.loginCount': { $gte: 5 }
});
expect(users.length).toEqual(2); // John (5) and Alice (10)
const names = users.map(u => u.name).sort();
expect(names).toEqual(['Alice Brown', 'John Doe']);
});
tap.test('should filter nested fields with multiple operators', async () => {
const users = await TestUser.getInstances({
'metadata.loginCount': { $gte: 3, $lt: 10 }
});
expect(users.length).toEqual(2); // Jane (3) and John (5)
const names = users.map(u => u.name).sort();
expect(names).toEqual(['Jane Smith', 'John Doe']);
});
tap.test('should handle deeply nested object structures', async () => {
// First, create a user with deep nesting in preferences
const deepUser = new TestUser({
name: 'Deep Nester',
age: 40,
email: 'deep@example.com',
roles: ['admin'],
tags: [],
status: 'active',
metadata: {
loginCount: 1,
preferences: {
theme: {
colors: {
primary: '#000000',
secondary: '#ffffff'
},
fonts: {
heading: 'Arial',
body: 'Helvetica'
}
},
notifications: {
email: true,
push: false
}
}
},
scores: []
});
await deepUser.save();
// Test deep nesting with dot notation
const deepResults = await TestUser.getInstances({
'metadata.preferences.theme.colors.primary': '#000000'
});
expect(deepResults.length).toEqual(1);
expect(deepResults[0].name).toEqual('Deep Nester');
// Test deep nesting with operators
const boolResults = await TestUser.getInstances({
'metadata.preferences.notifications.email': { $eq: true }
});
expect(boolResults.length).toEqual(1);
expect(boolResults[0].name).toEqual('Deep Nester');
// Clean up
await deepUser.delete();
});
tap.test('should filter arrays of nested objects using $elemMatch', async () => {
const orders = await TestOrder.getInstances({
items: {
$elemMatch: {
product: 'laptop',
price: { $gte: 1000 }
}
}
});
expect(orders.length).toEqual(2); // Both laptop orders have price >= 1000
});
tap.test('should filter nested arrays with dot notation', async () => {
// Query for any order that has an item with specific product
const orders = await TestOrder.getInstances({
'items.product': 'laptop'
});
expect(orders.length).toEqual(2); // Two orders contain laptops
});
tap.test('should combine nested object filters with logical operators', async () => {
const users = await TestUser.getInstances({
$or: [
{ 'metadata.loginCount': { $gte: 10 } }, // Alice has 10
{
$and: [
{ 'metadata.loginCount': { $lt: 5 } }, // Jane has 3, Bob has 0, Charlie has 1
{ status: 'active' } // Jane is active, Bob is inactive, Charlie is pending
]
}
]
});
expect(users.length).toEqual(2); // Alice (loginCount >= 10), Jane (loginCount < 5 AND active)
const names = users.map(u => u.name).sort();
expect(names).toEqual(['Alice Brown', 'Jane Smith']);
});
tap.test('should handle null and undefined in nested fields', async () => {
// Users without lastLogin
const noLastLogin = await TestUser.getInstances({
'metadata.lastLogin': { $exists: false }
});
expect(noLastLogin.length).toEqual(4); // Everyone except John
// Users with preferences (none have it set)
const withPreferences = await TestUser.getInstances({
'metadata.preferences': { $exists: true }
});
expect(withPreferences.length).toEqual(0);
});
tap.test('should filter nested arrays by size', async () => {
// Create an order with specific number of items
const multiItemOrder = new TestOrder({
userId: 'test-user',
items: [
{ product: 'item1', quantity: 1, price: 10 },
{ product: 'item2', quantity: 2, price: 20 },
{ product: 'item3', quantity: 3, price: 30 },
{ product: 'item4', quantity: 4, price: 40 }
],
totalAmount: 100,
status: 'pending',
tags: ['test']
});
await multiItemOrder.save();
const fourItemOrders = await TestOrder.getInstances({
items: { $size: 4 }
});
expect(fourItemOrders.length).toEqual(1);
// Clean up
await multiItemOrder.delete();
});
tap.test('should handle nested field comparison between documents', async () => {
// Find users where loginCount equals their age divided by 6 (John: 30/6=5)
const users = await TestUser.getInstances({
$and: [
{ 'metadata.loginCount': 5 },
{ age: 30 }
]
});
expect(users.length).toEqual(1);
expect(users[0].name).toEqual('John Doe');
});
tap.test('should filter using $in on nested fields', async () => {
const users = await TestUser.getInstances({
'metadata.loginCount': { $in: [0, 1, 5] }
});
expect(users.length).toEqual(3); // Bob (0), Charlie (1), John (5)
const names = users.map(u => u.name).sort();
expect(names).toEqual(['Bob Johnson', 'Charlie Wilson', 'John Doe']);
});
tap.test('should filter nested arrays with $all', async () => {
// Create an order with multiple tags
const taggedOrder = new TestOrder({
userId: 'test-user',
items: [{ product: 'test', quantity: 1, price: 10 }],
totalAmount: 10,
status: 'completed',
tags: ['urgent', 'priority', 'electronics']
});
await taggedOrder.save();
const priorityElectronics = await TestOrder.getInstances({
tags: { $all: ['priority', 'electronics'] }
});
expect(priorityElectronics.length).toEqual(2); // Original order and new one
// Clean up
await taggedOrder.delete();
});
// ============= COMPARISON OPERATOR TESTS =============
tap.test('should filter using $gt operator', async () => {
const users = await TestUser.getInstances({