view app/query.service.ts @ 16:7d82ca32833c

omit some attributes from list.
author Robert Casties <casties@mpiwg-berlin.mpg.de>
date Thu, 21 Jan 2016 18:47:57 +0100
parents f84ff6781e57
children f6af2c8347de
line wrap: on
line source

import {Injectable} from 'angular2/core';
import {Http, Headers} from 'angular2/http';

import 'rxjs/Rx'; // import all RxJS operators

import {QueryMode, QUERY_MODES} from './query-mode';
import {QueryState} from './query-state';
import {QueryStep} from './query-step';

@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 openMindBaseUrl = 'https://ismi-dev.mpiwg-berlin.mpg.de/om4-ismi/';
    //public openMindBaseUrl = 'http://localhost:18080/ismi-richfaces/';
    public excludedAttributes = {'type': true};
    public state: QueryState;
    public ismiObjectTypes: any;
    
    constructor(private _http: Http) {
        this.state = {
            'steps': [],
            'resultCypherQuery': '',
            'cypherQueryParams': {},
            'attributesCypherQuery': '',
            'relationsCypherQuery': '',
            'results': [],
            'resultTypes': '',
            'numResults': 0,
            'resultInfo': ''
        };
    }
    
    setup() {
        this.setupIsmiObjectTypes();
    }
    
    getState() {
        return this.state;
    }
    
    getQueryModes(): QueryMode[] {
        return QUERY_MODES;
    }
    
    getQueryOptions(queryMode: QueryMode) {
        var options = [];
        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 === 'att_contains_norm') {
            options = this.state.nextQueryAttributes;
        } else if (queryMode.id === 'att_num_range') {
            options = this.state.nextQueryAttributes;
        }
        console.debug("getQueryOptions returns: ", options);
        return options;
    }
    
    setupIsmiObjectTypes() {
        var query = `MATCH (n) WITH DISTINCT labels(n) AS labels
                UNWIND labels AS label 
                RETURN DISTINCT label ORDER BY label`;

        var res = this.fetchCypherResult(query);
        res.subscribe(
            data => {
                console.debug("neo4j data=", data);
                this.ismiObjectTypes = data.results[0].data.map(elem => elem.row[0]).filter(elem => elem[0] != "_");
                console.debug("ismi types=", this.ismiObjectTypes);
                },
            err => console.error("neo4j error=", err),
            () => console.debug('neo4j query Complete')
        );
    }
    
    setQueryStep(index: number, step: QueryStep) {
        this.state.steps[index] = step;
        this.createCypherQuery();
    }
    
    createCypherQuery() {
        var queryMatch = '';
        var queryWhere = '';
        var queryReturn = '';
        var queryParams = {};
        var resultQuery = '';
        var attributesQuery = '';
        var relationsQuery = '';
        var returnType = '';
        var nIdx = 1;
        this.state.steps.forEach((step, stepIdx) => {
            /*
             * step: object type is
             */
            if (step.mode.id === 'type_is') {
                queryMatch = `MATCH (n${nIdx}:${step.objectType})`;
                queryWhere = '';
                queryReturn =  `RETURN n${nIdx}`;
                returnType = 'node';
            }
            
            /*
             * step: relation type is
             */
            if (step.mode.id === 'relation_is') {
                nIdx += 1;
                queryMatch += `-[:\`${step.relationType}\`]->(n${nIdx})`;
                queryReturn =  `RETURN n${nIdx}`;
                returnType = 'node';
            }
            
            /*
             * step: attribute contains(_norm)
             */
            if (step.mode.id === 'att_contains' || step.mode.id === 'att_contains_norm') {
                if (!queryWhere) {
                    queryWhere = 'WHERE ';
                } else {
                    queryWhere += ' AND ';
                }
                if (step.attribute === 'ismi_id') {
                    // ismi_id is integer
                    queryWhere += `n${nIdx}.ismi_id = {att_val${stepIdx}}`;                    
                    queryParams[`att_val${stepIdx}`] = parseInt(step.value, 10);
                } else {
                    if (step.mode.id === 'att_contains_norm') {
                        // match _n_attribute with normValue
                        queryWhere += `lower(n${nIdx}._n_${step.attribute}) CONTAINS lower({att_val${stepIdx}})`;
                        queryParams[`att_val${stepIdx}`] = step.normValue;                        
                    } else {
                        queryWhere += `lower(n${nIdx}.${step.attribute}) CONTAINS lower({att_val${stepIdx}})`;
                        queryParams[`att_val${stepIdx}`] = step.value;
                    }
                }
            }
            
            /*
             * step: attribute number range
             */
            if (step.mode.id === 'att_num_range') {
                if (!queryWhere) {
                    queryWhere = 'WHERE ';
                } else {
                    queryWhere += ' AND ';
                }
                queryWhere += `toint(n${nIdx}.${step.attribute}) >= toint({att_nlo${stepIdx}})`
                    + ` AND toint(n${nIdx}.${step.attribute}) <= toint({att_nhi${stepIdx}})`;
                queryParams[`att_nlo${stepIdx}`] = step.numLo;
                queryParams[`att_nhi${stepIdx}`] = step.numHi;
            }
            
        });
        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.cypherQueryParams = queryParams;
        this.state.attributesCypherQuery = attributesQuery;
        this.state.relationsCypherQuery = relationsQuery;
        this.state.resultTypes = returnType;
    }
    
    updateQuery() {
        this.createCypherQuery();
        this.state.resultInfo = 'loading...';
        /*
         * run query for result table
         */
        var resQuery = this.state.resultCypherQuery;
        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] + ') ';   
                }
                info = info.substr(0, info.length-1);
                this.state.resultInfo = info;
                // save info also in last step
                this.state.steps[this.state.steps.length-1].resultInfo = info;
            },
            err => console.error("neo4j result error=", err),
            () => console.debug('neo4j result query Complete')
        );
        /*
         * run query for attribute list
         */
        if (this.state.attributesCypherQuery) {
            var attRes = this.fetchCypherResult(this.state.attributesCypherQuery, queryParams);
            attRes.subscribe(
                data => {
                    console.debug("neo4j att data=", data);
                    this.state.nextQueryAttributes = data.results[0].data.map(elem => elem.row[0])
                    .filter(elem => elem[0] != "_" && !this.excludedAttributes[elem]);
                },
                err => console.error("neo4j att error=", err),
                () => 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 = {}) {
        console.debug("fetching cypher query: ", query);
        var headers = new Headers();
        headers.append('Authorization', 'Basic ' + btoa('neo4j' + ':' + 'neo5j'));
        headers.append('Content-Type', 'application/json');
        headers.append('Accept', 'application/json');
        // put headers in options
        var opts = {'headers': headers};
        // create POST data from query
        var data = JSON.stringify({'statements': [
            {'statement': query, 'parameters': params}
            ]});
        // make post request asynchronously
        var resp = this._http.post(this.neo4jBaseUrl+'/transaction/commit', data, opts)
        // filter result as JSON
        .map(res => res.json());
        // return Observable
        return resp;
    }
    
    fetchNormalizedString(text: string) {
        console.debug("fetching normalized string: ", text);
        var headers = new Headers();
        headers.append('Accept', 'application/json');
        // put headers in options
        var opts = {'headers': headers};
        // make get request asynchronously
        var url = this.openMindBaseUrl+'jsonInterface?method=normalize_string&type=arabic_translit&text=';
        url += text;
        var resp = this._http.get(url, opts)
        // filter result as JSON
        .map(res => res.json());
        // return Observable
        return resp;        
    }
}