187 lines
5.0 KiB
TypeScript
187 lines
5.0 KiB
TypeScript
import { tap, expect } from '@push.rocks/tapbundle';
|
|
import * as smartmongo from '@push.rocks/smartmongo';
|
|
import * as smartdata from '../ts/index.js';
|
|
import { searchable } from '../ts/classes.doc.js';
|
|
import { smartunique } from '../ts/plugins.js';
|
|
|
|
// Set up database connection
|
|
let smartmongoInstance: smartmongo.SmartMongo;
|
|
let testDb: smartdata.SmartdataDb;
|
|
|
|
// Define a test class for advanced search scenarios
|
|
@smartdata.Collection(() => testDb)
|
|
class Product extends smartdata.SmartDataDbDoc<Product, Product> {
|
|
@smartdata.unI()
|
|
public id: string = smartunique.shortId();
|
|
|
|
@smartdata.svDb()
|
|
@searchable()
|
|
public name: string;
|
|
|
|
@smartdata.svDb()
|
|
@searchable()
|
|
public description: string;
|
|
|
|
@smartdata.svDb()
|
|
@searchable()
|
|
public category: string;
|
|
|
|
@smartdata.svDb()
|
|
public price: number;
|
|
|
|
constructor(
|
|
nameArg: string,
|
|
descriptionArg: string,
|
|
categoryArg: string,
|
|
priceArg: number,
|
|
) {
|
|
super();
|
|
this.name = nameArg;
|
|
this.description = descriptionArg;
|
|
this.category = categoryArg;
|
|
this.price = priceArg;
|
|
}
|
|
}
|
|
|
|
// Initialize DB and insert sample products
|
|
tap.test('setup advanced search database', async () => {
|
|
smartmongoInstance = await smartmongo.SmartMongo.createAndStart();
|
|
testDb = new smartdata.SmartdataDb(
|
|
await smartmongoInstance.getMongoDescriptor(),
|
|
);
|
|
await testDb.init();
|
|
});
|
|
|
|
tap.test('insert products for advanced search', async () => {
|
|
const products = [
|
|
new Product(
|
|
'Night Owl Lamp',
|
|
'Bright lamp for night reading',
|
|
'Lighting',
|
|
29,
|
|
),
|
|
new Product(
|
|
'Day Light Lamp',
|
|
'Daytime lamp with adjustable brightness',
|
|
'Lighting',
|
|
39,
|
|
),
|
|
new Product(
|
|
'Office Chair',
|
|
'Ergonomic chair for office',
|
|
'Furniture',
|
|
199,
|
|
),
|
|
new Product(
|
|
'Gaming Chair',
|
|
'Comfortable for long gaming sessions',
|
|
'Furniture',
|
|
299,
|
|
),
|
|
new Product(
|
|
'iPhone 12',
|
|
'Latest iPhone with A14 Bionic chip',
|
|
'Electronics',
|
|
999,
|
|
),
|
|
new Product(
|
|
'AirPods',
|
|
'Wireless earbuds with noise cancellation',
|
|
'Electronics',
|
|
249,
|
|
),
|
|
];
|
|
for (const p of products) {
|
|
await p.save();
|
|
}
|
|
const all = await Product.getInstances({});
|
|
expect(all.length).toEqual(products.length);
|
|
});
|
|
|
|
// Simple exact field:value matching
|
|
tap.test('simpleExact: category:Furniture returns chairs', async () => {
|
|
const res = await Product.search('category:Furniture');
|
|
expect(res.length).toEqual(2);
|
|
const names = res.map((r) => r.name).sort();
|
|
expect(names).toEqual(['Gaming Chair', 'Office Chair']);
|
|
});
|
|
|
|
// simpleExact invalid field should throw
|
|
tap.test('simpleExact invalid field errors', async () => {
|
|
let error: Error;
|
|
try {
|
|
await Product.search('price:29');
|
|
} catch (e) {
|
|
error = e as Error;
|
|
}
|
|
expect(error).toBeTruthy();
|
|
expect(error.message).toMatch(/not searchable/);
|
|
});
|
|
|
|
// Quoted phrase search
|
|
tap.test('quoted phrase "Bright lamp" matches Night Owl Lamp', async () => {
|
|
const res = await Product.search('"Bright lamp"');
|
|
expect(res.length).toEqual(1);
|
|
expect(res[0].name).toEqual('Night Owl Lamp');
|
|
});
|
|
|
|
tap.test("quoted phrase 'night reading' matches Night Owl Lamp", async () => {
|
|
const res = await Product.search("'night reading'");
|
|
expect(res.length).toEqual(1);
|
|
expect(res[0].name).toEqual('Night Owl Lamp');
|
|
});
|
|
|
|
|
|
tap.test('wildcard description:*gaming* matches Gaming Chair', async () => {
|
|
const res = await Product.search('description:*gaming*');
|
|
expect(res.length).toEqual(1);
|
|
expect(res[0].name).toEqual('Gaming Chair');
|
|
});
|
|
|
|
// Boolean AND and OR
|
|
tap.test('boolean AND: category:Lighting AND lamp', async () => {
|
|
const res = await Product.search('category:Lighting AND lamp');
|
|
expect(res.length).toEqual(2);
|
|
});
|
|
|
|
tap.test('boolean OR: Furniture OR Electronics', async () => {
|
|
const res = await Product.search('Furniture OR Electronics');
|
|
expect(res.length).toEqual(4);
|
|
});
|
|
|
|
// Multi-term unquoted -> AND across terms
|
|
tap.test('multi-term unquoted adjustable brightness', async () => {
|
|
const res = await Product.search('adjustable brightness');
|
|
expect(res.length).toEqual(1);
|
|
expect(res[0].name).toEqual('Day Light Lamp');
|
|
});
|
|
|
|
tap.test('multi-term unquoted Night Lamp', async () => {
|
|
const res = await Product.search('Night Lamp');
|
|
expect(res.length).toEqual(1);
|
|
expect(res[0].name).toEqual('Night Owl Lamp');
|
|
});
|
|
|
|
// Grouping with parentheses
|
|
tap.test('grouping: (Furniture OR Electronics) AND Chair', async () => {
|
|
const res = await Product.search(
|
|
'(Furniture OR Electronics) AND Chair',
|
|
);
|
|
expect(res.length).toEqual(2);
|
|
const names = res.map((r) => r.name).sort();
|
|
expect(names).toEqual(['Gaming Chair', 'Office Chair']);
|
|
});
|
|
|
|
// Teardown
|
|
tap.test('cleanup advanced search database', async () => {
|
|
await testDb.mongoDb.dropDatabase();
|
|
await testDb.close();
|
|
if (smartmongoInstance) {
|
|
await smartmongoInstance.stopAndDumpToDir(
|
|
`.nogit/dbdump/test.search.advanced.ts`,
|
|
);
|
|
}
|
|
setTimeout(() => process.exit(), 2000);
|
|
});
|
|
|
|
tap.start({ throwOnError: true }); |