changeset 11:6989cd00e8d7

relations work now as well as longer queries.
author Robert Casties <casties@mpiwg-berlin.mpg.de>
date Wed, 20 Jan 2016 19:49:10 +0100
parents 66dce99cef4e
children 1843b12eff9a
files app/query-mode.ts app/query-result.component.ts app/query-select.component.ts app/query.service.ts
diffstat 4 files changed, 134 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/app/query-mode.ts	Wed Jan 20 17:02:00 2016 +0100
+++ b/app/query-mode.ts	Wed Jan 20 19:49:10 2016 +0100
@@ -1,4 +1,12 @@
 export interface QueryMode {
     id: string;
     label: string;
-}
\ No newline at end of file
+}
+
+export var QUERY_MODES: QueryMode[] = [
+    {id: 'type_is', label:'Object type is'},
+    {id: 'att_contains', label: 'Attribute'},
+    {id: 'att_contains_norm', label: 'Attribute (normalized)'},
+    {id: 'relation_is', label: 'Relation type is'}
+];
+        
--- a/app/query-result.component.ts	Wed Jan 20 17:02:00 2016 +0100
+++ b/app/query-result.component.ts	Wed Jan 20 19:49:10 2016 +0100
@@ -8,8 +8,8 @@
     selector: 'query-result',
     template: `
         <div *ngIf="queryState">
-          <p>Cypher: {{queryState.cypherQuery}}</p>
-          <p>Query results ({{queryState.numResults}}):</p>
+          <pre>{{queryState.resultCypherQuery}}</pre>
+          <p>Query results ({{queryState.resultInfo}}):</p>
           <table>
             <tr>
               <th *ngIf="queryState.resultTypes=='node'">Type</th>
--- a/app/query-select.component.ts	Wed Jan 20 17:02:00 2016 +0100
+++ b/app/query-select.component.ts	Wed Jan 20 19:49:10 2016 +0100
@@ -9,8 +9,8 @@
 @Component({
     selector: 'query-select',
     template: `
-        <div>
-        <form (ngSubmit)="onSubmit()">
+<div>
+    <form (ngSubmit)="onSubmit()">
         <select (change)="onSelectMode($event)">
             <option></option>
             <option *ngFor="#mode of queryModes" [value]="mode.id">
@@ -18,26 +18,28 @@
             </option>
         </select>
 
-        <select *ngIf="selectedMode.id=='type_is' && queryOptions" (change)="onSelectOption($event)">
-            <option></option>
-            <option *ngFor="#option of queryOptions" [value]="option">
-                {{option}}
-            </option>
-        </select>
+        <span *ngIf="selectedMode.id=='type_is' || selectedMode.id=='relation_is'">
+            <select *ngIf="queryOptions" [(ngModel)]="selectedOption" (change)="onSelectOption($event)">
+                <option></option>
+                <option *ngFor="#option of queryOptions" [value]="option">
+                    {{option}}
+                </option>
+            </select>
+        </span>
 
-        <span *ngIf="selectedMode.id=='att_contains'">
-        <select [(ngModel)]="selectedOption">
-            <option></option>
-            <option *ngFor="#option of queryOptions" [value]="option">
-                {{option}}
-            </option>
-        </select>
-        <span>contains</span>
-        <input type="text" [(ngModel)]="queryInput"/>
+        <span *ngIf="selectedMode.id=='att_contains' || selectedMode.id=='att_contains_norm'">
+            <select [(ngModel)]="selectedOption">
+                <option></option>
+                <option *ngFor="#option of queryOptions" [value]="option">
+                    {{option}}
+                </option>
+            </select>
+            <span>contains</span>
+            <input type="text" [(ngModel)]="queryInput"/>
         </span>
         <button type="submit">Submit</button>
-        </form>
-        </div>
+    </form>
+</div>
         `,
     inputs: ['index']
     //outputs: ['queryChanged'] // this should work but doesn't
@@ -77,21 +79,34 @@
     onSelectOption(event: any) {
         var selected = event.target.value;
         console.debug("selected option:", selected);
-        var step = {'mode': this.selectedMode, 'objectType': selected};
-        this._queryService.setQueryStep(this.index, step);
-        this.queryChanged.emit(this._queryService.getState());
+        this.selectedOption = selected;
+        this.onSubmit();
     }
     
     onSubmit() {
         console.debug("Submit! selectedMode=", this.selectedMode, " selectedOption=", this.selectedOption, " queryInput=", this.queryInput);
-        if (this.selectedMode.id == 'att_contains') {
+        var step: QueryStep;
+        if (this.selectedMode.id == 'type_is') {
+            var opt = this.selectedOption;
+            if (opt) {
+                step = {'mode': this.selectedMode, 'objectType': opt};
+           }
+        } else if (this.selectedMode.id == 'relation_is') {
+            var opt = this.selectedOption;
+            if (opt) {
+                step = {'mode': this.selectedMode, 'relationType': opt};
+           }
+        } else if (this.selectedMode.id == 'att_contains' || this.selectedMode.id == 'att_contains_norm') {
             var att = this.selectedOption;
             var val = this.queryInput;
             if (att && val) {
-                var step = {'mode': this.selectedMode, 'attribute': att, 'value': val};
-                this._queryService.setQueryStep(this.index, step);
-                this.queryChanged.emit(this._queryService.getState());
+                step = {'mode': this.selectedMode, 'attribute': att, 'value': val};
            }
         }
+
+        if (step != null) {
+            this._queryService.setQueryStep(this.index, step);
+            this.queryChanged.emit(this._queryService.getState());
+        }
     }
 }
--- a/app/query.service.ts	Wed Jan 20 17:02:00 2016 +0100
+++ b/app/query.service.ts	Wed Jan 20 19:49:10 2016 +0100
@@ -3,7 +3,7 @@
 
 import 'rxjs/Rx'; // import all RxJS operators
 
-import {QueryMode} from './query-mode';
+import {QueryMode, QUERY_MODES} from './query-mode';
 import {QueryState} from './query-state';
 import {QueryStep} from './query-step';
 
@@ -14,19 +14,17 @@
     public state: QueryState;
     public ismiObjectTypes: any;
     
-    public QUERY_MODES: QueryMode[] = [
-        {id: 'type_is', label:'Object type is'},
-        {id: 'att_contains', label: 'Attribute'}];
-        
     constructor(private _http: Http) {
         this.state = {
             'steps': [],
             'resultCypherQuery': '',
-            'resultCypherParams': {},
-            'attributeCypherQuery': '',
+            'cypherQueryParams': {},
+            'attributesCypherQuery': '',
+            'relationsCypherQuery': '',
             'results': [],
             'resultTypes': '',
-            'numResults': 0
+            'numResults': 0,
+            'resultInfo': ''
         };
     }
     
@@ -39,15 +37,19 @@
     }
     
     getQueryModes(): QueryMode[] {
-        return this.QUERY_MODES;
+        return QUERY_MODES;
     }
     
     getQueryOptions(queryMode: QueryMode) {
         var options = ['a1', 'b1', 'c1'];
-        if (queryMode.id === 'att_contains') {
+        if (queryMode.id === 'type_is') {
+            options = this.ismiObjectTypes;
+        } else if (queryMode.id === 'relation_is') {
+            options = this.state.nextQueryRelations;
+        } else if (queryMode.id === 'att_contains') {
             options = this.state.nextQueryAttributes;
-        } else if (queryMode.id === 'type_is') {
-            options = this.ismiObjectTypes;
+        } else if (queryMode.id === 'att_contains_norm') {
+            options = this.state.nextQueryAttributes;
         }
         console.debug("getQueryOptions returns: ", options);
         return options;
@@ -79,30 +81,58 @@
         var queryMatch = '';
         var queryWhere = '';
         var queryReturn = '';
+        var queryParams = {};
         var resultQuery = '';
-        var attQuery = '';
+        var attributesQuery = '';
+        var relationsQuery = '';
         var returnType = '';
-        this.state.steps.forEach((step) => {
+        var nIdx = 1;
+        this.state.steps.forEach((step, stepIdx) => {
             // object type is
             if (step.mode.id === 'type_is') {
-                queryMatch = `MATCH (n:${step.objectType})`;
+                queryMatch = `MATCH (n${nIdx}:${step.objectType})`;
                 queryWhere = '';
-                queryReturn =  'RETURN n';
+                queryReturn =  `RETURN n${nIdx}`;
+                returnType = 'node';
+            }
+            
+            // relation type is
+            if (step.mode.id === 'relation_is') {
+                nIdx += 1;
+                queryMatch += `-[:\`${step.relationType}\`]->(n${nIdx})`;
+                queryReturn =  `RETURN n${nIdx}`;
                 returnType = 'node';
             }
             
             // attribute contains
             if (step.mode.id === 'att_contains') {
-                queryWhere = `WHERE lower(n.${step.attribute}) CONTAINS lower('${step.value}')`;
-                queryReturn =  'RETURN n';
-                returnType = 'node';
+                if (!queryWhere) {
+                    queryWhere = 'WHERE ';
+                } else {
+                    queryWhere += ' AND ';
+                }
+                queryWhere += `lower(n${nIdx}.${step.attribute}) CONTAINS lower({att_val${stepIdx}})`;
+                queryParams[`att_val${stepIdx}`] = step.value;
+            }
+            
+            // attribute contains (normalized)
+            if (step.mode.id === 'att_contains_norm') {
+                if (!queryWhere) {
+                    queryWhere = 'WHERE ';
+                } else {
+                    queryWhere += ' AND ';
+                }
+                queryWhere += `lower(n${nIdx}._n_${step.attribute}) CONTAINS lower('${step.value}')`;
             }
             
         });
-        resultQuery = queryMatch + ' ' + queryWhere + ' ' + queryReturn;
-        attQuery = queryMatch + ' ' + queryWhere + ' WITH DISTINCT keys(n) AS atts UNWIND atts AS att RETURN DISTINCT att ORDER BY att';
+        resultQuery = queryMatch + '\n' + queryWhere + '\n' + queryReturn;
+        attributesQuery = queryMatch + ' ' + queryWhere + ` WITH DISTINCT keys(n${nIdx}) AS atts UNWIND atts AS att RETURN DISTINCT att ORDER BY att`;
+        relationsQuery = queryMatch + '-[r]-() ' + queryWhere + ' RETURN DISTINCT type(r)';
         this.state.resultCypherQuery = resultQuery;
-        this.state.attributeCypherQuery = attQuery;
+        this.state.cypherQueryParams = queryParams;
+        this.state.attributesCypherQuery = attributesQuery;
+        this.state.relationsCypherQuery = relationsQuery;
         this.state.resultTypes = returnType;
     }
     
@@ -110,20 +140,34 @@
         this.createCypherQuery();
         // run query for result table
         var resQuery = this.state.resultCypherQuery;
-        var resParams = this.state.cypherParams;
-        var resRes = this.fetchCypherResult(resQuery, resParams);
+        var queryParams = this.state.cypherQueryParams;
+        var resRes = this.fetchCypherResult(resQuery, queryParams);
         resRes.subscribe(
             data => {
                 console.debug("neo4j result data=", data);
                 this.state.results = data.results[0].data.map(elem => elem.row[0]);
                 this.state.numResults = this.state.results.length;
+                // count all types
+                var resTypes = {};
+                this.state.results.forEach((r) => {
+                    if (resTypes[r.type] == null) {
+                        resTypes[r.type] = 1;
+                    } else {
+                        resTypes[r.type] += 1;
+                    }
+                });
+                var info = '';
+                for (var t in resTypes) {
+                    info += t + '(' + resTypes[t] + ') ';   
+                }
+                this.state.resultInfo = info;
             },
             err => console.error("neo4j result error=", err),
             () => console.debug('neo4j result query Complete')
         );
         // run query for attribute list
-        if (this.state.attributeCypherQuery) {
-            var attRes = this.fetchCypherResult(this.state.attributeCypherQuery);
+        if (this.state.attributesCypherQuery) {
+            var attRes = this.fetchCypherResult(this.state.attributesCypherQuery, queryParams);
             attRes.subscribe(
                 data => {
                     console.debug("neo4j att data=", data);
@@ -133,6 +177,18 @@
                 () => console.debug('neo4j att query Complete')
             );
         }
+        // run query for relations list
+        if (this.state.relationsCypherQuery) {
+            var attRes = this.fetchCypherResult(this.state.relationsCypherQuery, queryParams);
+            attRes.subscribe(
+                data => {
+                    console.debug("neo4j rel data=", data);
+                    this.state.nextQueryRelations = data.results[0].data.map(elem => elem.row[0]).filter(elem => elem[0] != "_");;
+                },
+                err => console.error("neo4j rel error=", err),
+                () => console.debug('neo4j rel query Complete')
+            );
+        }
     }
     
     fetchCypherResult(query: string, params = {}) {