+import { describe, it, expect, beforeEach } from 'vitest';
+
+import { MilvusFilterTranslator } from './filter';
+
+describe('MilvusFilterTranslator', () => {
+ let translator: MilvusFilterTranslator;
+
+ beforeEach(() => {
+ translator = new MilvusFilterTranslator();
+ });
+
+ // Basic Filter Operations
+ describe('basic operations', () => {
+ it('handles empty filters', () => {
+ expect(translator.translate({})).toEqual('');
+ expect(translator.translate(null as any)).toEqual('');
+ expect(translator.translate(undefined as any)).toEqual('');
+ });
+
+ it('translates equality operation', () => {
+ const filter = { field: 'value' };
+ expect(translator.translate(filter)).toEqual('metadata["field"] == \'value\'');
+ });
+
+ it('translates numeric equality operation', () => {
+ const filter = { count: 42 };
+ expect(translator.translate(filter)).toEqual('metadata["count"] == 42');
+ });
+
+ it('translates boolean equality operation', () => {
+ const filter = { active: true };
+ expect(translator.translate(filter)).toEqual('metadata["active"] == true');
+ });
+
+ it('combines multiple fields with AND', () => {
+ const filter = {
+ field1: 'value1',
+ field2: 'value2',
+ };
+ expect(translator.translate(filter)).toEqual(
+ 'metadata["field1"] == \'value1\' AND metadata["field2"] == \'value2\'',
+ );
+ });
+
+ it('handles comparison operators', () => {
+ const filter = {
+ price: { $gt: 100 },
+ };
+ expect(translator.translate(filter)).toEqual('metadata["price"] > 100');
+ });
+
+ it('handles multiple operators on same field', () => {
+ const filter = {
+ price: { $gt: 100, $lt: 200 },
+ quantity: { $gte: 10, $lte: 20 },
+ };
+ expect(translator.translate(filter)).toEqual(
+ 'metadata["price"] > 100 AND metadata["price"] < 200 AND metadata["quantity"] >= 10 AND metadata["quantity"] <= 20',
+ );
+ });
+
+ it('handles date values', () => {
+ const date = new Date('2024-01-01');
+ const filter = { timestamp: date };
+ expect(translator.translate(filter)).toEqual(`metadata["timestamp"] == timestamp '${date.toISOString()}'`);
+ });
+
+ it('handles date comparison operators', () => {
+ const date = new Date('2024-01-01');
+ const filter = { timestamp: { $gt: date } };
+ expect(translator.translate(filter)).toEqual(`metadata["timestamp"] > timestamp '${date.toISOString()}'`);
+ });
+ });
+
+ // Array Operations
+ describe('array operations', () => {
+ it('translates arrays to IN operator', () => {
+ const filter = { tags: ['tag1', 'tag2'] };
+ expect(translator.translate(filter)).toEqual("metadata[\"tags\"] IN ['tag1', 'tag2']");
+ });
+
+ it('handles numeric arrays', () => {
+ const filter = { ids: [1, 2, 3] };
+ expect(translator.translate(filter)).toEqual('metadata["ids"] IN [1, 2, 3]');
+ });
+
+ it('handles empty array values', () => {
+ const filter = { tags: [] };
+ expect(translator.translate(filter)).toEqual('false'); // Empty IN is usually false
+ });
+
+ it('handles explicit $in operator', () => {
+ const filter = { tags: { $in: ['tag1', 'tag2'] } };
+ expect(translator.translate(filter)).toEqual("metadata[\"tags\"] IN ['tag1', 'tag2']");
+ });
+
+ it('handles $in with mixed type values', () => {
+ const filter = { field: { $in: [1, 'two', true] } };
+ expect(translator.translate(filter)).toEqual('metadata["field"] IN [1, \'two\', true]');
+ });
+
+ it('handles $in with date values', () => {
+ const date = new Date('2024-01-01');
+ const filter = { field: { $in: [date] } };
+ expect(translator.translate(filter)).toEqual(`metadata["field"] IN [timestamp '${date.toISOString()}']`);
+ });
+ });
+
+ // Logical Operators
+ describe('logical operators', () => {
+ it('handles $and operator', () => {
+ const filter = {
+ $and: [{ status: 'active' }, { age: { $gt: 25 } }],
+ };
+ expect(translator.translate(filter)).toEqual('metadata["status"] == \'active\' AND metadata["age"] > 25');
+ });
+
+ it('handles $or operator', () => {
+ const filter = {
+ $or: [{ status: 'active' }, { age: { $gt: 25 } }],
+ };
+ expect(translator.translate(filter)).toEqual('(metadata["status"] == \'active\' OR metadata["age"] > 25)');
+ });
+
+ it('handles $not operator', () => {
+ const filter = {
+ $not: { color: 'green' },
+ };
+ expect(translator.translate(filter)).toEqual('NOT (metadata["color"] == \'green\')');
+ });
+
+ it('handles $not operator with comparison', () => {
+ const filter = {
+ $not: { price: { $gt: 100 } },
+ };
+ expect(translator.translate(filter)).toEqual('NOT (metadata["price"] > 100)');
+ });
+
+ it('handles $not operator with complex conditions', () => {
+ const filter = {
+ $not: {
+ $or: [{ category: 'news' }, { importance: { $gt: 8 } }],
+ },
+ };
+ expect(translator.translate(filter)).toEqual(
+ 'NOT (metadata["category"] == \'news\' OR metadata["importance"] > 8)',
+ );
+ });
+
+ it('handles nested logical operators', () => {
+ const filter = {
+ $and: [
+ { status: 'active' },
+ {
+ $or: [{ category: { $in: ['A', 'B'] } }, { price: { $gt: 100 } }],
+ },
+ ],
+ };
+ expect(translator.translate(filter)).toEqual(
+ 'metadata["status"] == \'active\' AND (metadata["category"] IN [\'A\', \'B\'] OR metadata["price"] > 100)',
+ );
+ });
+
+ it('handles complex nested conditions', () => {
+ const filter = {
+ $or: [
+ { age: { $gt: 25 } },
+ {
+ $and: [{ status: 'active' }, { theme: 'dark' }],
+ },
+ ],
+ };
+ expect(translator.translate(filter)).toEqual(
+ '(metadata["age"] > 25 OR metadata["status"] == \'active\' AND metadata["theme"] == \'dark\')',
+ );