changeset 39:7578b21cdf2e

make relation types configurable. relations can have custom labels for incoming or outgoing direction.
author casties
date Sun, 14 Feb 2016 19:40:07 +0100
parents 313a5360c2d3
children 896ae7eefb33
files app/ismi-relation-types.ts app/query-select.component.ts app/query.service.ts app/relation-type.ts app/result-type.ts
diffstat 5 files changed, 158 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/ismi-relation-types.ts	Sun Feb 14 19:40:07 2016 +0100
@@ -0,0 +1,54 @@
+import {RelationType, invNamePrefix} from './relation-type';
+
+export var RELATION_TYPES: {[name:string]: RelationType} = {};
+
+addRelationType('is_part_of', 'is included in', 'includes');
+addRelationType('is_exemplar_of', 'title of witness', 'witnesses to title');
+addRelationType('was_created_by', 'created by', 'works of');
+addRelationType('has_subject', 'subject of title', 'titles with subject');
+addRelationType('has_role', 'role of person', 'persons with role');
+addRelationType('is_alias_name_of', 'person name for alias', 'alias of person');
+addRelationType('is_alias_title_of', 'title name for alias', 'alias of title');
+addRelationType('is_commentary_on', 'is commentary on', 'list of commentaries');
+addRelationType('has_title_written_as', 'title in witness', 'witness with title as');
+addRelationType('has_author_written_as', 'author in witness', 'witness with author as');
+addRelationType('lived_in', 'place person lived in', 'persons who lived in');
+addRelationType('was_copied_by', 'witness copied by', 'witnesses that were copied by');
+addRelationType('was_born_in', 'place person was born in', 'persons who were born in');
+addRelationType('owned_by', 'codex owned by', 'persons who owned codex');
+addRelationType('was_student_of', 'studied with', 'persons studying with');
+addRelationType('died_in', 'place person died in', 'persons who died in');
+addRelationType('was_created_in', 'place title was created', 'titles that were created in');
+addRelationType('was_copied_in', 'place witness was copied', 'witnesses that were copied in');
+addRelationType('misattributed_to', 'title misattributed to', 'misattributions to person');
+addRelationType('was_dedicated_to', 'text dedicated to', 'texts that were dedicated to');
+addRelationType('is_version_of', 'standard text of different version', 'different version of standard text');
+addRelationType('is_translation_of', 'original text of a translation', 'translation of a text');
+addRelationType('has_floruit_date', 'floruit date of person', 'persons with floruit date');
+addRelationType('was_studied_by', 'persons studying this text', 'text studied by');
+//addRelationType('', '', '');
+
+export function getRelationType(name: string, isOutgoing?: boolean): RelationType {
+    if (isOutgoing === false) {
+        // add prefix to name
+        name = invNamePrefix + name;
+    } 
+    let rt = RELATION_TYPES[name];
+    if (rt == null) {
+        if (name.indexOf(invNamePrefix) == 0) {
+            // inverse relation
+            name = name.substr(invNamePrefix.length);
+            rt = new RelationType(name, false);
+        } else {
+            rt = new RelationType(name, true);
+        }
+    }
+    return rt;
+}
+
+function addRelationType(name: string, outLabel: string, inLabel: string) {
+    // add outgoing relation
+    RELATION_TYPES[name] = new RelationType(name, true, outLabel);
+    // add inverse relation
+    RELATION_TYPES[invNamePrefix + name] = new RelationType(name, false, inLabel);
+}
\ No newline at end of file
--- a/app/query-select.component.ts	Sun Feb 14 19:38:36 2016 +0100
+++ b/app/query-select.component.ts	Sun Feb 14 19:40:07 2016 +0100
@@ -6,6 +6,7 @@
 
 import {QueryService} from './query.service';
 import {NormalizationService} from './normalization.service';
+import {getRelationType} from './ismi-relation-types';
 
 
 @Component({
@@ -21,7 +22,7 @@
             </option>
         </select>
 
-        <span *ngIf="selectedMode?.id=='type_is' || selectedMode?.id=='relation_is'">
+        <span *ngIf="selectedMode?.id=='type_is'">
             <select *ngIf="queryOptions" [ngModel]="selectedOption" (change)="onSelectOption($event)">
                 <option></option>
                 <option *ngFor="#option of queryOptions" [value]="option">
@@ -30,6 +31,15 @@
             </select>
         </span>
 
+        <span *ngIf="selectedMode?.id=='relation_is'">
+            <select *ngIf="queryOptions" [ngModel]="selectedOption" (change)="onSelectOption($event)">
+                <option></option>
+                <option *ngFor="#option of queryOptions" [value]="option.getName()">
+                    {{option.getLabel()}}
+                </option>
+            </select>
+        </span>
+
         <span *ngIf="selectedMode?.id=='att_contains' || selectedMode?.id=='att_contains_norm'">
             <select [ngModel]="selectedOption" (change)="selectedOption=$event.target.value">
                 <option></option>
@@ -116,36 +126,57 @@
         console.debug("Submit! selectedMode=", this.selectedMode, " selectedOption=", this.selectedOption, " queryInput=", this.queryInput);
         var step: QueryStep;
         if (this.selectedMode.id == 'type_is') {
-            var opt = this.selectedOption;
+            /*
+             * type_is
+             */
+            let opt = this.selectedOption;
             if (opt) {
                 step = new QueryStep(this.selectedMode, {'objectType': opt});
             }
         } else if (this.selectedMode.id == 'relation_is') {
-            var opt = this.selectedOption;
+            /*
+             * relation_is
+             */
+            let opt = this.selectedOption;
             if (opt) {
-                step = new QueryStep(this.selectedMode, {'relationType': opt});
+                let rel = getRelationType(opt);
+                step = new QueryStep(this.selectedMode, {'relationType': rel});
+            }
+        } else if (this.selectedMode.id == 'id_is') {
+            /*
+             * id is
+             */
+            let val = this.queryInput;
+            if (val) {
+                step = new QueryStep(this.selectedMode, {'value': val});
             }
         } else if (this.selectedMode.id == 'att_contains') {
-            var att = this.selectedOption;
-            var val = this.queryInput;
+            /*
+             * att_contains
+             */
+            let att = this.selectedOption;
+            let val = this.queryInput;
             if (att && val) {
                 step = new QueryStep(this.selectedMode, {'attribute': att, 'value': val});
             }
-        } else if (this.selectedMode.id == 'id_is') {
-            var val = this.queryInput;
-            if (val) {
-                step = new QueryStep(this.selectedMode, {'value': val});
-            }
         } else if (this.selectedMode.id == 'att_num_range') {
-            var att = this.selectedOption;
-            var nlo = this.queryInput;
-            var nhi = this.queryInput2;
+            /*
+             * att_num_range
+             */
+            let att = this.selectedOption;
+            let nlo = this.queryInput;
+            let nhi = this.queryInput2;
             if (att && nlo && nhi) {
                 step = new QueryStep(this.selectedMode, {'attribute': att, 'numLo': nlo, 'numHi': nhi});
             }
         } else if (this.selectedMode.id == 'att_contains_norm') {
-            var att = this.selectedOption;
-            var val = this.queryInput;
+            /*
+             * att_contains_norm
+             * 
+             * calls normalization service and submits event in callback
+             */
+            let att = this.selectedOption;
+            let val = this.queryInput;
             if (att && val) {
                 // run search term through normalizer 
                 this._normService.fetchArabicTranslitNormalizedString(val)
@@ -165,6 +196,9 @@
            }
         }
 
+        /*
+         * set step and submit change event
+         */
         if (step != null) {
             this._queryService.setQueryStep(this.index, step);
             this.queryChanged.emit(this._queryService.getState());
--- a/app/query.service.ts	Sun Feb 14 19:38:36 2016 +0100
+++ b/app/query.service.ts	Sun Feb 14 19:40:07 2016 +0100
@@ -9,16 +9,16 @@
 import {QueryStep} from './query-step';
 import {getResultType} from './result-type';
 import {ISMI_RESULT_TYPES} from './ismi-result-types';
+import {getRelationType} from './ismi-relation-types';
 
 @Injectable()
 export class QueryService {
         
-    public neo4jBaseUrl = 'https://ismi-dev.mpiwg-berlin.mpg.de/neo4j-ismi/db/data';
-    //public neo4jBaseUrl = 'http://localhost:7474/db/data';
+    //public neo4jBaseUrl = 'https://ismi-dev.mpiwg-berlin.mpg.de/neo4j-ismi/db/data';
+    public neo4jBaseUrl = 'http://localhost:7474/db/data';
     public neo4jAuthentication = {'user': 'neo4j', 'password': 'neo5j'};
     public typeAttribute = '_type';
     public excludedAttributes = {};
-    public invRelPrefix = '<- ';
     public state: QueryState;
     public objectTypes: string[];
     
@@ -149,15 +149,14 @@
              */
             if (mode === 'relation_is') {
                 nIdx += 1;
-                var rel = params.relationType;
-                if (rel.indexOf(this.invRelPrefix) == 0) {
+                let rel = params.relationType;
+                if (rel.isOutgoing()) {
+                    queryMatch += `-[:\`${rel.getName()}\`]->(n${nIdx})`;
+                } else {
                     // inverse relation
-                    rel = rel.substr(this.invRelPrefix.length);
-                    queryMatch += `<-[:${rel}]-(n${nIdx})`;
-                } else {
-                    queryMatch += `-[:${rel}]->(n${nIdx})`;
+                    queryMatch += `<-[:\`${rel.getName()}\`]-(n${nIdx})`;
                 }
-                queryReturn =  `RETURN n${nIdx}`;
+                queryReturn =  `RETURN DISTINCT n${nIdx}`;
                 returnType = 'node';
             }
             
@@ -292,7 +291,8 @@
                     // outgoing aka forward relations
                     resIdx += 1;
                     let rels = data.results[resIdx].data.map(elem => elem.row[0])
-                        .filter(elem => elem[0] != "_");
+                        .filter(elem => elem[0] != "_")
+                        .map(elem => getRelationType(elem, true));
                     this.state.resultRelations = rels;
                 }
                 if (this.state.inRelsCypherQuery) {
@@ -300,7 +300,7 @@
                     resIdx += 1;
                     let rels = data.results[resIdx].data.map(elem => elem.row[0])
                         .filter(elem => elem[0] != "_")
-                        .map((r) => this.invRelPrefix + r);
+                        .map(elem => getRelationType(elem, false));
                     this.state.resultRelations = this.state.resultRelations.concat(rels);
                 }
             },
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/app/relation-type.ts	Sun Feb 14 19:40:07 2016 +0100
@@ -0,0 +1,38 @@
+
+export var invLabelPrefix = '<- ';
+export var invNamePrefix = '-';
+export var rawLabelPrefix = '(';
+export var rawLabelPostfix = ')';
+
+export class RelationType {
+    public name: string;
+    public label: string;
+    public outgoing: boolean;
+    
+    constructor (name: string, isOutgoing: boolean, label?:string) {
+        this.name = name;
+        this.outgoing = isOutgoing;
+        if (label != null) {
+            this.label = label;
+        } else {
+            // create label using name
+            if (isOutgoing) {
+                this.label = rawLabelPrefix + name + rawLabelPostfix;
+            } else {
+                this.label = rawLabelPrefix + invLabelPrefix + name + rawLabelPostfix;
+            }
+        }
+    }
+    
+    getLabel() {
+        return this.label;
+    }
+
+    getName() {
+        return this.name;
+    }
+    
+    isOutgoing() {
+        return this.outgoing;
+    }
+}
--- a/app/result-type.ts	Sun Feb 14 19:38:36 2016 +0100
+++ b/app/result-type.ts	Sun Feb 14 19:40:07 2016 +0100
@@ -13,7 +13,10 @@
         this.deniedAttributes = deniedAttributes;
     }
     
-    getColumns(attributes: string[], allAttributes=true) {
+    /**
+     * Return columns for the given list of attributes.
+     */
+    getColumns(attributes: string[], allAttributes=true): ResultColumn[] {
         let atts = attributes.slice();
         let cols = [];
         // allowed attributes