import { describe, expect, it } from 'vitest'; import { BackendQueryFormat, LogicNodeAttributes, MathFilters, NumberFunctions, QueryElementTypes } from '../model'; import { QueryMultiGraphology } from '../model/graphology/utils'; import { NumberAggregationTypes, NumberFilterTypes, NumberFunctionTypes } from '../model/logic/general'; import { Query2BackendQuery, calculateQueryLogic } from './query2backend'; import { SerializedNode } from 'graphology-types'; import { MathAggregations } from '../model/logic/numberAggregations'; import { QueryBuilderSettings } from '../../data-access/store/querybuilderSlice'; const defaultQuery = { saveStateID: 'database', cached: false, query: [], limit: 500, return: ['*'], machineLearning: [], }; const defaultSettings: QueryBuilderSettings = { limit: 500, depth: { min: 0, max: 1 }, layout: 'manual', autocompleteRelation: true, unionTypes: {}, }; describe('QueryUtils Entity and Relations', () => { it('should create an instance', () => { const graph = new QueryMultiGraphology(); expect(true).toBeTruthy(); }); it('should run simple query translation', () => { const graph = new QueryMultiGraphology(); const entity1 = graph.addPill2Graphology({ id: '0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }); const entity2 = graph.addPill2Graphology({ id: '10', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [], }); const relation1 = graph.addPill2Graphology({ id: '1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(entity1, relation1); graph.addEdge2Graphology(relation1, entity2); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: '0', label: 'Airport 1', relation: { id: '1', label: 'Flight between airports', direction: 'TO', node: { id: '10', label: 'Airport 2', relation: undefined, }, }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run multiple path query translation', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1, { type: 'connection' }); graph.addEdge2Graphology(r1, e2, { type: 'connection' }); const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] }); const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] }); const r12 = graph.addPill2Graphology({ id: 'r12', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports 2', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e12, r12); graph.addEdge2Graphology(r12, e22); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e0', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 2', relation: undefined, }, }, }, }, { id: 'path_1', node: { id: 'e12', label: 'Airport 12', relation: { id: 'r12', label: 'Flight between airports 2', direction: 'TO', node: { id: 'e22', label: 'Airport 22', relation: undefined, }, }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run one relation multiple entity query translation', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] }); const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] }); const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1); graph.addEdge2Graphology(r1, e2); graph.addEdge2Graphology(e12, r1); graph.addEdge2Graphology(r1, e22); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } }, }, }, { id: 'path_1', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e22', label: 'Airport 22' } }, }, }, { id: 'path_2', node: { id: 'e12', label: 'Airport 12', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } }, }, }, { id: 'path_3', node: { id: 'e12', label: 'Airport 12', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e22', label: 'Airport 22' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run lone entities query translation', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] }); const e12 = graph.addPill2Graphology({ id: 'e12', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 12', attributes: [] }); const e22 = graph.addPill2Graphology({ id: 'e22', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 22', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1); graph.addEdge2Graphology(r1, e2); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e0', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 2' } }, }, }, { id: 'path_1', node: { id: 'e12', label: 'Airport 12' } }, { id: 'path_2', node: { id: 'e22', label: 'Airport 22' } }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run lone relations query translation', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 200, y: 200, name: 'Airport 2', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); const r2 = graph.addPill2Graphology({ id: 'r2', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports 2', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1); graph.addEdge2Graphology(r1, e2); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e0', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 2' } }, }, }, { id: 'path_1', node: { relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run relation only left side connected query translation', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e0', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: {} }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run relation only right side connected query translation', () => { const graph = new QueryMultiGraphology(); const e2 = graph.addPill2Graphology({ id: 'e0', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(r1, e2); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e0', label: 'Airport 2' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run entity and relations multi connection', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); const r2 = graph.addPill2Graphology({ id: 'r2', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports 2', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1); graph.addEdge2Graphology(e1, r2); graph.addEdge2Graphology(r1, e2); graph.addEdge2Graphology(r2, e2); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } }, }, }, { id: 'path_1', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: { id: 'e2', label: 'Airport 2' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of loops', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1, { type: 'connection' }); graph.addEdge2Graphology(r1, e1, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of loops and regular', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1, { type: 'connection' }); graph.addEdge2Graphology(r1, e1, { type: 'connection' }); graph.addEdge2Graphology(r1, e2, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e2', label: 'Airport 1', relation: undefined }, }, }, }, { id: 'path_1', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of loops and regular left', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(e1, r1, { type: 'connection' }); graph.addEdge2Graphology(e2, r1, { type: 'connection' }); graph.addEdge2Graphology(r1, e1, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } }, }, }, { id: 'path_1', node: { id: 'e2', label: 'Airport 1', relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { id: 'e1', label: 'Airport 1' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of direct entity connection', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); graph.addEdge2Graphology(e1, e2, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { direction: 'BOTH', node: { id: 'e2', label: 'Airport 1' } }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of direct relation connection', () => { const graph = new QueryMultiGraphology(); const r1 = graph.addPill2Graphology({ id: 'r1', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); const r2 = graph.addPill2Graphology({ id: 'r2', type: QueryElementTypes.Relation, x: 140, y: 140, name: 'Flight between airports 2', collection: 'Relation Pill', depth: { min: 0, max: 1 }, attributes: [], }); graph.addEdge2Graphology(r1, r2, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { relation: { id: 'r1', label: 'Flight between airports', direction: 'TO', node: { relation: { id: 'r2', label: 'Flight between airports 2', direction: 'TO', node: {} } }, }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of entity only loop', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); const e2 = graph.addPill2Graphology({ id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [] }); graph.addEdge2Graphology(e1, e2, { type: 'connection' }); graph.addEdge2Graphology(e2, e1, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { direction: 'BOTH', node: { id: 'e2', label: 'Airport 2', relation: { direction: 'BOTH', node: { id: 'e1', label: 'Airport 1' } }, } }, }, } ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run in case of entity only self loop', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology({ id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [] }); graph.addEdge2Graphology(e1, e1, { type: 'connection' }); const expected = { ...defaultQuery, query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: { direction: 'BOTH', node: { id: 'e1', label: 'Airport 1' } } }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); }); describe('QueryUtils calculateQueryLogic', () => { it('should run simple logic', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const l1 = graph.addLogicPill2Graphology({ id: 'l1', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic 1', logic: MathFilters[NumberFilterTypes.EQUAL], attributes: [], inputs: {}, }); graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); const graphExport = graph.export(); let logics = graphExport.nodes.filter((n) => n?.attributes?.type === QueryElementTypes.Logic) as SerializedNode<LogicNodeAttributes>[]; const ret = calculateQueryLogic(logics[0], graphExport, logics); }); }); describe('QueryUtils with Logic', () => { it('should run simple query with logic', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const l1 = graph.addLogicPill2Graphology({ id: 'l1', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic 1', logic: MathFilters[NumberFilterTypes.EQUAL], attributes: [], inputs: {}, }); graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' }); const expected: BackendQueryFormat = { ...defaultQuery, logic: ['==', '@e1.age', 0], machineLearning: [], query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: undefined, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should run add age and filter', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const e2 = graph.addPill2Graphology( { id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [], }, [{ name: 'age', type: 'string' }], ); const l1 = graph.addLogicPill2Graphology({ id: 'l1', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Filter EQ', logic: MathFilters[NumberFilterTypes.EQUAL], attributes: [], inputs: {}, }); const l2 = graph.addLogicPill2Graphology({ id: 'l2', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic ADD', logic: NumberFunctions[NumberFunctionTypes.ADD], attributes: [], inputs: {}, }); graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' }); graph.addEdge2Graphology(e2, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: '1' }); graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: NumberFilterTypes.EQUAL, targetHandleName: 'Value' }); const expected: BackendQueryFormat = { ...defaultQuery, logic: ['==', ['+', '@e1.age', '@e2.age'], 0], machineLearning: [], query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: undefined, }, }, { id: 'path_1', node: { id: 'e2', label: 'Airport 2', relation: undefined, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should handle average logic', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const l1 = graph.addLogicPill2Graphology({ id: 'l1', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic LT', logic: MathFilters[NumberFilterTypes.LESS_THAN], attributes: [], inputs: {}, }); const l2 = graph.addLogicPill2Graphology({ id: 'l2', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic average', logic: MathAggregations[NumberAggregationTypes.AVG], attributes: [], inputs: {}, }); graph.addEdge2Graphology(e1, l2, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' }); graph.addEdge2Graphology(l2, l1, { type: 'connection' }, { sourceHandleName: NumberAggregationTypes.AVG, targetHandleName: 'Value' }); const expected: BackendQueryFormat = { ...defaultQuery, logic: ['<', ['Avg', '@e1.age'], 0], machineLearning: [], query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: undefined, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); it('should allow custom values in logic pills', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const l1 = graph.addLogicPill2Graphology( { id: 'l1', type: QueryElementTypes.Logic, x: 100, y: 100, name: 'Logic LT', logic: MathFilters[NumberFilterTypes.LESS_THAN], attributes: [], inputs: {}, }, { '1': 5 }, ); graph.addEdge2Graphology(e1, l1, { type: 'connection' }, { sourceHandleName: 'age', targetHandleName: 'Value' }); const expected: BackendQueryFormat = { ...defaultQuery, logic: ['<', '@e1.age', 5], machineLearning: [], query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: undefined, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); }); }); it('should no connections between entities and relations', () => { const graph = new QueryMultiGraphology(); const e1 = graph.addPill2Graphology( { id: 'e1', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 1', attributes: [], }, [{ name: 'age', type: 'string' }], ); const e2 = graph.addPill2Graphology( { id: 'e2', type: QueryElementTypes.Entity, x: 100, y: 100, name: 'Airport 2', attributes: [], }, [{ name: 'age', type: 'string' }], ); const r1 = graph.addPill2Graphology( { id: 'r1', type: QueryElementTypes.Relation, x: 100, y: 100, name: 'Relation 1', depth: { min: 0, max: 1 }, attributes: [], collection: 'r', }, [{ name: 'age', type: 'string' }], ); const expected: BackendQueryFormat = { ...defaultQuery, logic: undefined, machineLearning: [], query: [ { id: 'path_0', node: { id: 'e1', label: 'Airport 1', relation: undefined, }, }, { id: 'path_1', node: { id: 'e2', label: 'Airport 2', relation: undefined, }, }, { id: 'path_2', node: { relation: { id: 'r1', label: 'Relation 1', direction: 'TO', node: {}, depth: { min: 0, max: 1 }, }, }, }, ], }; let ret = Query2BackendQuery('database', graph.export(), defaultSettings); expect(ret).toMatchObject(expected); });