diff --git a/frontend/src/semantic-search/model-test.ts b/frontend/src/semantic-search/model-test.ts new file mode 100644 index 00000000..74fee635 --- /dev/null +++ b/frontend/src/semantic-search/model-test.ts @@ -0,0 +1,117 @@ +import { map } from 'lodash'; + +import { startStore, endStore } from '../test-util'; +import mockOntology from '../mock-data/mock-ontology'; + +import Model from '../core/model'; +import Collection from '../core/collection'; +import Graph from '../common-rdf/graph'; +import abstractDeepEqual from '../utilities/abstractDeepEqual'; + +import { logic, filters } from './dropdown-constants'; +import SemanticQuery from './model'; + +describe('semantic search model', function() { + beforeAll(startStore); + beforeAll(function() { + this.ontology = new Graph(mockOntology); + this.model = new SemanticQuery({ + id: '1', + label: 'test 123', + query: new Model({ + chain: new Collection([{ + range: this.ontology, + assertion: true, + selection: this.ontology.at(0), + }, { + range: new Graph([this.ontology.at(0)]), + precedent: this.ontology.at(0), + selection: logic.get('logic:and'), + }, { + range: new Graph([this.ontology.at(0)]), + precedent: this.ontology.at(0), + scheme: 'logic', + action: 'and', + branches: new Collection([{ + chain: new Collection([{ + range: new Graph([this.ontology.at(0)]), + precedent: this.ontology.at(0), + traversal: true, + selection: this.ontology.at(3), + }, { + range: this.ontology, + precedent: this.ontology.at(3), + selection: filters.get('filter:isIRI'), + }, { + filter: filters.get('filter:isIRI'), + }]), + }, { + chain: new Collection([{ + range: this.ontology, + precedent: this.ontology.at(3), + selection: filters.get('filter:equals'), + }, { + filter: filters.get('filter:equals'), + value: 'https://example.example', + }]), + }]), + }]), + }), + }); + const idOnlyOntology = this.ontology.map(m => ({ '@id': m.id })); + this.minimalJSON = { + id: '1', + label: 'test 123', + query: { + chain: [{ + range: idOnlyOntology, + assertion: true, + selection: idOnlyOntology[0], + }, { + range: [idOnlyOntology[0]], + precedent: idOnlyOntology[0], + selection: { id: 'logic:and' }, + }, { + range: [idOnlyOntology[0]], + precedent: idOnlyOntology[0], + scheme: 'logic', + action: 'and', + branches: [{ + chain: [{ + range: [idOnlyOntology[0]], + precedent: idOnlyOntology[0], + traversal: true, + selection: idOnlyOntology[3], + }, { + range: idOnlyOntology, + precedent: idOnlyOntology[3], + selection: { id: 'filter:isIRI' }, + }, { + filter: { id: 'filter:isIRI' }, + }], + }, { + chain: [{ + range: idOnlyOntology, + precedent: idOnlyOntology[3], + selection: { id: 'filter:equals' }, + }, { + filter: { id: 'filter:equals' }, + value: 'https://example.example', + }], + }], + }], + }, + }; + }); + afterAll(endStore); + + it('sends a lean representation to the backend', function() { + const json = this.model.toJSON(); + expect(abstractDeepEqual(json, this.minimalJSON)).toBe(true); + }); + + it('can reconstruct the full representation from lean JSON', function() { + const parsed = this.model.parse(this.minimalJSON); + expect(abstractDeepEqual(parsed, this.model.attributes)).toBe(true); + }); +}); diff --git a/frontend/src/semantic-search/model.ts b/frontend/src/semantic-search/model.ts new file mode 100644 index 00000000..e73a0a4f --- /dev/null +++ b/frontend/src/semantic-search/model.ts @@ -0,0 +1,41 @@ +import { pick, extend, map, mapValues, has, isArray, isObject } from 'lodash'; + +import Model from '../core/model'; +import Collection from '../core/collection'; +import ldChannel from '../common-rdf/radio'; +import Graph from '../common-rdf/graph'; + +import { logic, filters } from './dropdown-constants'; + +function pruneResources(json: any): any { + if (!isObject(json)) return json; + if (isArray(json)) return map(json, pruneResources); + if (has(json, '@id')) return pick(json, '@id'); + if (has(json, 'id')) return pick(json, 'id'); + return mapValues(json, pruneResources); +} + +function parseQuery(json: any, key?: string | number): any { + if (!isObject(json)) return json; + if (isArray(json)) { + const data = map(json, parseQuery); + if (key === 'range') return new Graph(data); + return new Collection(data); + } + if (has(json, '@id')) return ldChannel.request('obtain', json); + if (has(json, 'id')) { + return logic.get(json as Model) || filters.get(json as Model); + } + return new Model(mapValues(json, parseQuery)); +} + +export default class SemanticQuery extends Model { + toJSON(options?: any): any { + const json = super.toJSON(options); + return mapValues(json, pruneResources); + } + + parse(json: any): any { + return mapValues(json, parseQuery); + } +}