├── ql ├── parse │ ├── __init__.py │ ├── lextab.py │ ├── ASTNode.py │ ├── lexer.py │ └── parser.py ├── solr │ └── __init__.py ├── dsl │ ├── Drop.py │ ├── Describe.py │ ├── Delete.py │ ├── Explain.py │ ├── Update.py │ ├── __init__.py │ ├── Aggregation.py │ ├── Insert.py │ ├── Create.py │ ├── Query.py │ ├── QueryBody.py │ └── Response.py ├── __init__.py └── utest.py ├── App ├── __init__.py ├── utils.py ├── app.py └── esql.py ├── MANIFEST.in ├── esql5.egg-info ├── dependency_links.txt ├── top_level.txt ├── requires.txt ├── PKG-INFO └── SOURCES.txt ├── elsh ├── __init__.py └── Command.py ├── bin ├── uwsgi ├── elsh └── service ├── elsh.png ├── explain.png ├── head ├── base │ ├── favicon.png │ ├── loading.gif │ └── reset.css ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── index.html ├── i18n.js ├── lang │ ├── zh_strings.js │ ├── ja_strings.js │ ├── en_strings.js │ ├── fr_strings.js │ ├── tr_strings.js │ └── pt_strings.js ├── app.css └── vendor.css ├── .eggs ├── elasticsearch-5.3.0-py3.6.egg └── README.txt ├── conf ├── esql.yml └── uwsgi.ini ├── Api ├── test.py └── esql.py ├── .pydevproject ├── .project ├── setup.py └── README.md /ql/parse/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ql/solr/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /App/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include conf/*.* 2 | -------------------------------------------------------------------------------- /esql5.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /elsh/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /esql5.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | App 2 | elsh 3 | ql 4 | -------------------------------------------------------------------------------- /bin/uwsgi: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/bin/uwsgi -------------------------------------------------------------------------------- /elsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/elsh.png -------------------------------------------------------------------------------- /explain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/explain.png -------------------------------------------------------------------------------- /head/base/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/base/favicon.png -------------------------------------------------------------------------------- /head/base/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/base/loading.gif -------------------------------------------------------------------------------- /head/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /head/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /head/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /.eggs/elasticsearch-5.3.0-py3.6.egg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/.eggs/elasticsearch-5.3.0-py3.6.egg -------------------------------------------------------------------------------- /head/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/unimassystem/esql5/HEAD/head/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /conf/esql.yml: -------------------------------------------------------------------------------- 1 | elastic: { 2 | hosts: [ 3 | { 4 | host: 10.68.23.81, 5 | port: 9200 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /esql5.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | elasticsearch>=5.3.0 2 | Flask>=0.12.0 3 | PyYAML>=3.12 4 | tabulate>=0.7.0 5 | prompt_toolkit>=1.0.14 6 | ply>=3.9 7 | pygments>=2.2.0 8 | -------------------------------------------------------------------------------- /conf/uwsgi.ini: -------------------------------------------------------------------------------- 1 | [uwsgi] 2 | http = 0.0.0.0:5000 3 | chdir = . 4 | module = App.app 5 | master = true 6 | workers = 8 7 | callable = app 8 | daemonize = /dev/null 9 | pidfile = var/run/uwsgi.pid 10 | vacuum = true 11 | -------------------------------------------------------------------------------- /.eggs/README.txt: -------------------------------------------------------------------------------- 1 | This directory contains eggs that were downloaded by setuptools to build, test, and run plug-ins. 2 | 3 | This directory caches those eggs to prevent repeated downloads. 4 | 5 | However, it is safe to delete this directory. 6 | 7 | -------------------------------------------------------------------------------- /esql5.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.0 2 | Name: esql5 3 | Version: 0.1 4 | Summary: Sql on Elasticsearch 5 | Home-page: http://www.hzhz.co 6 | Author: qs 7 | Author-email: qs@hzhz.co 8 | License: PSF 9 | Description: UNKNOWN 10 | Platform: UNKNOWN 11 | -------------------------------------------------------------------------------- /Api/test.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Jun 14, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from Api import esql 8 | 9 | conn = esql.connect("http://10.68.23.81:5000") 10 | 11 | c = conn.cursor(); 12 | 13 | c.execute("select * from flow_i") 14 | 15 | print(c.total) 16 | 17 | print(c.took) 18 | 19 | for col in c.description: 20 | print(col[0]) 21 | 22 | for row in c.fetchall(): 23 | print(row[10]) 24 | 25 | conn.close() -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /${PROJECT_DIR_NAME} 5 | 6 | python 3.6 7 | Default 8 | 9 | -------------------------------------------------------------------------------- /App/utils.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Apr 19, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from flask import make_response,jsonify 8 | 9 | 10 | 11 | def http_response_nor(message,code=200): 12 | return make_response(jsonify(message),code) 13 | 14 | def http_response_error(message,code=404): 15 | return make_response(jsonify({"error":{"message":message,"code":code}}),code) 16 | 17 | 18 | def http_response_succes(message): 19 | return make_response(jsonify(message)) 20 | 21 | -------------------------------------------------------------------------------- /ql/dsl/Drop.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Apr 21, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.parse.ASTNode import Node 8 | from ql.parse.parser import TK 9 | from ql.dsl import parse_tok_table_name 10 | 11 | 12 | class Drop(object): 13 | __slots__ = ('_index','_type') 14 | def __init__(self,tree: Node): 15 | for element in tree.get_children(): 16 | if element.get_type() == TK.TOK_TABLE_NAME: 17 | (self._index,self._type) = parse_tok_table_name(element) -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | esql5 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | org.python.pydev.django.djangoNature 17 | 18 | 19 | -------------------------------------------------------------------------------- /ql/dsl/Describe.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Mar 15, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.parse.ASTNode import Node 8 | from ql.parse.parser import TK 9 | from ql.dsl import parse_tok_table_name 10 | 11 | class Describe(object): 12 | 13 | __slots__ = ('_index','_type') 14 | def __init__(self,tree: Node): 15 | for element in tree.get_children(): 16 | if element.get_type() == TK.TOK_TABLE_NAME: 17 | (self._index,self._type) = parse_tok_table_name(element) 18 | 19 | 20 | -------------------------------------------------------------------------------- /bin/elsh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | #! /bin/bash 3 | SCRIPT="$0" 4 | # SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path. 5 | while [ -h "$SCRIPT" ] ; do 6 | ls=`ls -ld "$SCRIPT"` 7 | # Drop everything prior to -> 8 | link=`expr "$ls" : '.*-> \(.*\)$'` 9 | if expr "$link" : '/.*' > /dev/null; then 10 | SCRIPT="$link" 11 | else 12 | SCRIPT=`dirname "$SCRIPT"`/"$link" 13 | fi 14 | done 15 | # determine elasticsearch home 16 | path=`dirname "$SCRIPT"`/.. 17 | cd $path 18 | HOME=`pwd` 19 | export PYTHONHOME=$HOME/python36 20 | $PYTHONHOME/bin/python3 -m elsh.Command $1 21 | -------------------------------------------------------------------------------- /ql/__init__.py: -------------------------------------------------------------------------------- 1 | from ply.lex import lex 2 | from ply.yacc import yacc 3 | 4 | from ql.parse import lexer, parser 5 | from ql.dsl.Query import Query 6 | 7 | __debug = False 8 | __lexer = None 9 | __parser = None 10 | 11 | 12 | def init(optimize, debug): 13 | """ Init parser 14 | """ 15 | global __debug, __lexer, __parser 16 | __debug = debug 17 | __lexer = lex(module=lexer, optimize=optimize, debug=debug) 18 | __parser = yacc(debug=debug, module=parser) 19 | 20 | 21 | def parse(sql): 22 | ast = __parser.parse(input=sql, lexer=__lexer.clone(), debug=__debug) 23 | if not ast: 24 | return None 25 | ast.debug() 26 | return Query(ast) 27 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | 4 | requires = ['elasticsearch>=5.3.0', 5 | 'Flask>=0.12.0', 6 | 'PyYAML>=3.12', 7 | 'tabulate>=0.7.0', 8 | 'prompt_toolkit>=1.0.14', 9 | 'ply>=3.9', 10 | 'pygments>=2.2.0', 11 | ] 12 | 13 | 14 | setup( 15 | include_package_data = True, 16 | name = "esql5", 17 | version = "0.1", 18 | packages = find_packages(), 19 | install_requires = requires, 20 | author = "qs", 21 | author_email = "qs@hzhz.co ", 22 | description = "Sql on Elasticsearch", 23 | license = "PSF", 24 | url = "http://www.hzhz.co", 25 | 26 | ) 27 | -------------------------------------------------------------------------------- /head/base/reset.css: -------------------------------------------------------------------------------- 1 | BODY { 2 | font-family: Verdana, sans-serif; 3 | font-size: 73%; 4 | padding: 0; 5 | margin: 0; 6 | } 7 | 8 | INPUT, SELECT, TEXTAREA { 9 | border: 1px solid #cecece; 10 | padding: 1px 3px; 11 | background: white; 12 | } 13 | 14 | SELECT { 15 | padding: 0; 16 | } 17 | 18 | .saf SELECT { 19 | margin-top: 0; 20 | margin-bottom: 0; 21 | } 22 | 23 | TEXTAREA, CODE { 24 | font-family: monospace; 25 | font-size: 13px; 26 | } 27 | 28 | BUTTON::-moz-focus-inner { 29 | border: none; 30 | } 31 | 32 | .pull-left { 33 | float: left; 34 | } 35 | 36 | .pull-right { 37 | float: right; 38 | } 39 | 40 | .loading { 41 | background-image: url(loading.gif); 42 | background-repeat: no-repeat; 43 | text-indent: 20px; 44 | } 45 | -------------------------------------------------------------------------------- /esql5.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | MANIFEST.in 2 | setup.py 3 | App/__init__.py 4 | App/app.py 5 | App/esql.py 6 | App/utils.py 7 | conf/esql.yml 8 | conf/uwsgi.ini 9 | elsh/Command.py 10 | elsh/__init__.py 11 | esql5.egg-info/PKG-INFO 12 | esql5.egg-info/SOURCES.txt 13 | esql5.egg-info/dependency_links.txt 14 | esql5.egg-info/requires.txt 15 | esql5.egg-info/top_level.txt 16 | ql/__init__.py 17 | ql/utest.py 18 | ql/dsl/Aggregation.py 19 | ql/dsl/Create.py 20 | ql/dsl/Delete.py 21 | ql/dsl/Describe.py 22 | ql/dsl/Drop.py 23 | ql/dsl/Explain.py 24 | ql/dsl/Insert.py 25 | ql/dsl/Query.py 26 | ql/dsl/QueryBody.py 27 | ql/dsl/Response.py 28 | ql/dsl/Update.py 29 | ql/dsl/__init__.py 30 | ql/parse/ASTNode.py 31 | ql/parse/__init__.py 32 | ql/parse/lexer.py 33 | ql/parse/lextab.py 34 | ql/parse/parser.py 35 | ql/parse/parsetab.py 36 | ql/solr/__init__.py -------------------------------------------------------------------------------- /ql/dsl/Delete.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Mar 15, 2017 3 | 4 | @author: qs 5 | ''' 6 | from ql.parse.ASTNode import Node 7 | from ql.parse.parser import TK 8 | from ql.dsl import parse_tok_table_name 9 | from ql.dsl.QueryBody import QueryBody 10 | 11 | 12 | 13 | class Delete(object): 14 | __slots__ = ('_index','_type','conditions') 15 | def __init__(self,tree: Node): 16 | for element in tree.get_children(): 17 | if element.get_type() == TK.TOK_TABLE_NAME: 18 | (self._index,self._type) = parse_tok_table_name(element) 19 | if element.get_type() == TK.TOK_WHERE: 20 | self.conditions = QueryBody(element.get_child(0)) 21 | 22 | def dsl(self): 23 | dsl_body = {} 24 | if hasattr(self, 'conditions'): 25 | dsl_body['query'] = self.conditions.dsl() 26 | return dsl_body 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ql/dsl/Explain.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Mar 15, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.parse.ASTNode import Node 8 | from ql.parse.parser import TK 9 | from ql.dsl.Query import Query 10 | # from ql.dsl.Create import Create 11 | # from ql.dsl.Insert import Insert 12 | 13 | 14 | 15 | 16 | class Explain(object): 17 | 18 | 19 | def __init__(self,tree: Node): 20 | 21 | exec_node = tree.get_child(0) 22 | 23 | stmt = None 24 | self.dsl_body = {} 25 | # self.curl_str = '' 26 | # 27 | # es_url = 'http://localhost:9200/' 28 | 29 | if exec_node.get_type() == TK.TOK_QUERY: 30 | stmt = Query(exec_node) 31 | # self.curl_str = 'curl -XPOST ' + es_url + stmt._index + '/' + stmt._type + '/_search' 32 | self.dsl_body = stmt.dsl() 33 | 34 | def dsl(self): 35 | return self.dsl_body 36 | 37 | 38 | -------------------------------------------------------------------------------- /App/app.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Apr 21, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from flask import Flask 8 | from flask import request 9 | from App.utils import http_response_error 10 | from App.esql import Esql 11 | from flask import redirect 12 | 13 | 14 | esql = Esql() 15 | app = Flask(__name__,static_folder='../head') 16 | 17 | def request_sql(): 18 | if request.method == 'GET': 19 | return request.args.get('sql') 20 | else: 21 | return request.form.get('sql') 22 | 23 | 24 | 25 | @app.route('/') 26 | def home(): 27 | url = esql.get_host_url() 28 | return redirect('head/index.html?base_uri=' + url) 29 | 30 | 31 | @app.route('/esql',methods=('GET','POST')) 32 | def app_esql(): 33 | sql = request_sql() 34 | 35 | if sql == None: 36 | return http_response_error('Statement not found!') 37 | 38 | return esql.exec_statement(sql) 39 | 40 | 41 | if __name__ == "__main__": 42 | 43 | app.run(host='0.0.0.0',port=5000) 44 | 45 | -------------------------------------------------------------------------------- /head/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | elasticsearch-head 7 | 8 | 9 | 10 | 11 | 12 | 13 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /bin/service: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | SCRIPT="$0" 3 | # SCRIPT may be an arbitrarily deep series of symlinks. Loop until we have the concrete path. 4 | while [ -h "$SCRIPT" ] ; do 5 | ls=`ls -ld "$SCRIPT"` 6 | # Drop everything prior to -> 7 | link=`expr "$ls" : '.*-> \(.*\)$'` 8 | if expr "$link" : '/.*' > /dev/null; then 9 | SCRIPT="$link" 10 | else 11 | SCRIPT=`dirname "$SCRIPT"`/"$link" 12 | fi 13 | done 14 | # determine elasticsearch home 15 | path=`dirname "$SCRIPT"`/.. 16 | cd $path 17 | HOME=`pwd` 18 | export PYTHONHOME=$HOME/python36 19 | #nohup $PYTHONHOME/bin/python3 -m App.app >& /dev/null & 20 | 21 | CONF=conf/uwsgi.ini 22 | PID=var/run/uwsgi.pid 23 | RUN=var/run 24 | if [ ! -d $RUN ] 25 | then 26 | mkdir -p $RUN 27 | fi 28 | 29 | if [ $# == 0 ] 30 | then 31 | echo "Usage: service {start|stop}" 32 | exit 33 | fi 34 | 35 | if [ $1 == 'start' ] 36 | then 37 | if [ -f $PID ] 38 | then 39 | echo "uwsgi already running" 40 | exit 41 | fi 42 | bin/uwsgi --ini $CONF & 43 | exit 44 | elif [ $1 == 'stop' ] 45 | then 46 | if [ ! -f $PID ] 47 | then 48 | echo "uwsgi not running" 49 | exit 50 | fi 51 | bin/uwsgi --stop $PID 52 | rm -fr $PID 53 | fi 54 | -------------------------------------------------------------------------------- /ql/parse/lextab.py: -------------------------------------------------------------------------------- 1 | # lextab.py. This file automatically created by PLY (version 3.10). Don't edit! 2 | _tabversion = '3.10' 3 | _lextokens = set(('NOT', 'IS', 'COMMA', 'GROUP', 'QUOTE_STRING', 'DESC', 'TABLES', 'OR', 'LIKE', 'BULK', 'FROM', 'ORDER', 'NULL', 'UPSERT', 'TABLE', 'UPDATE', 'SET', 'LIMIT', 'EXPLAIN', 'WHERE', 'DELETE', 'SHOW', 'COMPARE_TYPE', 'TO', 'ASC', 'WORD', 'SELECT', 'BY', 'AS', 'INSERT', 'DROP', 'AND', 'CREATE', 'VALUES', 'WITH', 'META', 'DQUOTE_STRING', 'OPTION', 'INTO', 'BETWEEN', 'NUMBER')) 4 | _lexreflags = 64 5 | _lexliterals = '(){}@%.*[]:-^' 6 | _lexstateinfo = {'INITIAL': 'inclusive'} 7 | _lexstatere = {'INITIAL': [('(?P[_a-zA-Z][a-zA-Z_0-9]*|[\\u4e00-\\u9fa5]+)|(?P(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]? \\d+)?)|(?P\'((?<=\\\\)\\\'|[^\\\'])*\')|(?P"((?<=\\\\)\\"|[^\\"])*")|(?P<>|\\!=|==|>=|<=|=>|=<|=|>|<)|(?P,)|(?P;)', [None, ('t_WORD', 'WORD'), ('t_NUMBER', 'NUMBER'), None, None, None, ('t_QUOTE_STRING', 'QUOTE_STRING'), None, ('t_DQUOTE_STRING', 'DQUOTE_STRING'), None, (None, 'COMPARE_TYPE'), (None, 'COMMA'), (None, 'END_QUERY')])]} 8 | _lexstateignore = {'INITIAL': ' \t\n'} 9 | _lexstateerrorf = {'INITIAL': 't_error'} 10 | _lexstateeoff = {} 11 | -------------------------------------------------------------------------------- /ql/parse/ASTNode.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Dec 26, 2016 3 | 4 | @author: unimas 5 | ''' 6 | 7 | 8 | class Node(object): 9 | __slots__ = ('type','value','children') 10 | 11 | def __init__(self,_type,_value,_children): 12 | self.type = _type 13 | self.value = _value 14 | self.children = _children 15 | 16 | def set_type(self,_type): 17 | self.type = _type 18 | 19 | def get_type(self): 20 | return self.type 21 | 22 | def get_value(self): 23 | return self.value 24 | 25 | def get_children_count(self): 26 | return len(self.children) 27 | 28 | def get_child(self,index): 29 | return self.children[index] 30 | 31 | def get_children(self): 32 | return self.children 33 | 34 | def append_children(self,val): 35 | self.children.append(val) 36 | 37 | def debug(self,depth=0): 38 | tab = '' 39 | for i in range(depth): 40 | i = i 41 | tab += '\t' 42 | print( tab + '('+ self.get_type().name) 43 | if self.value != None: 44 | value = self.get_value() 45 | if type(value) != str: 46 | value = str(value) 47 | print( tab + '\t'+ value) 48 | if(self.children != None): 49 | depth += 1 50 | for node in self.get_children(): 51 | node.debug(depth) 52 | print(tab + ')') 53 | 54 | 55 | -------------------------------------------------------------------------------- /Api/esql.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Jun 14, 2017 3 | 4 | @author: qs 5 | ''' 6 | import urllib3 7 | import json 8 | 9 | def connect(db_host): 10 | return connector(db_host) 11 | 12 | class connector(): 13 | def __init__(self,db_host): 14 | self._db_host = db_host 15 | def cursor(self): 16 | return cursor(self._db_host) 17 | 18 | def close(self): 19 | pass 20 | 21 | 22 | class cursor(): 23 | __slots__ = ('_db_host','_res','description','total','took') 24 | 25 | def __init__(self,db_host): 26 | print("new cursor") 27 | self._db_host = db_host 28 | 29 | def execute(self,statement): 30 | http = urllib3.PoolManager() 31 | url = self._db_host + '/esql' 32 | try: 33 | res = http.request('POST', url, fields = {"sql":statement}) 34 | except Exception: 35 | raise Exception("Connection refused") 36 | if res.status != 200: 37 | raise Exception(res.data.decode('utf-8')) 38 | self._res = json.loads(res.data.decode('utf-8')) 39 | self.description = tuple(tuple((item,None,None,None,None,None,None)) for item in self._res['cols']) 40 | if 'total' in self._res.keys(): 41 | self.total = self._res['total'] 42 | if 'took' in self._res.keys(): 43 | self.took = self._res['took'] 44 | 45 | def fetchall(self): 46 | res = [] 47 | for row in self._res['rows']: 48 | res.append(tuple(item for item in row)) 49 | return res 50 | 51 | 52 | -------------------------------------------------------------------------------- /ql/dsl/Update.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Mar 15, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | 8 | from ql.parse.ASTNode import Node 9 | from ql.parse.parser import TK 10 | from ql.dsl import parse_tok_table_name,parse_kv 11 | from ql.dsl.QueryBody import QueryBody,CompareExpression 12 | 13 | def parse_update_sets(tree: Node): 14 | 15 | retval= {} 16 | for e in tree.get_children(): 17 | retval.update(parse_kv(e)) 18 | return retval 19 | 20 | 21 | def query_body_travel(q: QueryBody,condition): 22 | 23 | if type(q) == CompareExpression: 24 | condition[q.left_values[0][1:]] = q.right_value 25 | else: 26 | if q.combine not in ['and','must']: 27 | return 28 | if hasattr(q, 'rchild'): 29 | query_body_travel(q.rchild,condition) 30 | if hasattr(q, 'lchild'): 31 | query_body_travel(q.lchild,condition) 32 | pass 33 | 34 | 35 | def parse_conditions(tree: Node): 36 | query_body = QueryBody(tree.get_child(0)) 37 | condition = {} 38 | query_body_travel(query_body,condition) 39 | return condition 40 | 41 | class Update(object): 42 | __slots__ = ('_index','_type','update_sets','conditions') 43 | def __init__(self,tree: Node): 44 | for element in tree.get_children(): 45 | if element.get_type() == TK.TOK_TABLE_NAME: 46 | (self._index,self._type) = parse_tok_table_name(element) 47 | if element.get_type() == TK.TOK_SET_COLUMNS_CLAUSE: 48 | self.update_sets = parse_update_sets(element) 49 | if element.get_type() == TK.TOK_WHERE: 50 | self.conditions = parse_conditions(element) 51 | 52 | def dsl(self): 53 | return {'doc':self.update_sets} 54 | 55 | 56 | class Upsert(Update): 57 | def __init__(self,tree: Node): 58 | Update.__init__(self,tree) 59 | 60 | 61 | def dsl(self): 62 | retval = Update.dsl(self) 63 | retval['doc_as_upsert'] = True 64 | return retval 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /ql/parse/lexer.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Dec 15, 2016 3 | 4 | @author: qs 5 | ''' 6 | 7 | 8 | reserved = { 9 | 'select': 'SELECT', 10 | 'insert': 'INSERT', 11 | 'bulk': 'BULK', 12 | 'upsert': 'UPSERT', 13 | 'update': 'UPDATE', 14 | 'delete': 'DELETE', 15 | 'create': 'CREATE', 16 | 'set': 'SET', 17 | 'from': 'FROM', 18 | 'to': 'TO', 19 | 'is': 'IS', 20 | 'null': 'NULL', 21 | 'where': 'WHERE', 22 | 'into': 'INTO', 23 | 'values': 'VALUES', 24 | 'drop': 'DROP', 25 | 'and': 'AND', 26 | 'or': 'OR', 27 | 'in': 'IN', 28 | 'as': 'AS', 29 | 'with': 'WITH', 30 | 'meta': 'META', 31 | 'option': 'OPTION', 32 | 'like': 'LIKE', 33 | 'limit': 'LIMIT', 34 | 'between': 'BETWEEN', 35 | 'not': 'NOT', 36 | 'table': 'TABLE', 37 | 'asc': 'ASC', 38 | 'desc': 'DESC', 39 | 'order': 'ORDER', 40 | 'group': 'GROUP', 41 | 'by': 'BY', 42 | 'explain': 'EXPLAIN', 43 | 'show': 'SHOW', 44 | 'tables': 'TABLES', 45 | 'routing': 'ROUTING' 46 | } 47 | 48 | 49 | tokens = ( 50 | 'COMPARE_TYPE', 51 | 'WORD', 52 | 'NUMBER', 53 | 'COMMA', 54 | 'QUOTE_STRING', 55 | 'DQUOTE_STRING' 56 | ) + tuple(set(reserved.values())) 57 | 58 | literals = '(){}@%.*[]:-^' 59 | t_COMPARE_TYPE = '<>|\!=|==|>=|<=|=>|=<|=|>|<' 60 | t_END_QUERY = ';' 61 | t_COMMA = ',' 62 | t_ignore = ' \t\n' 63 | 64 | 65 | def t_WORD(t): 66 | r'[_a-zA-Z][a-zA-Z_0-9]*|[\u4e00-\u9fa5]+' 67 | t.type = reserved.get(t.value.lower(),'WORD') 68 | return t 69 | 70 | 71 | def t_NUMBER(t): 72 | r'(\d+(\.\d*)?|\.\d+)([eE][-+]? \d+)?' 73 | if '.' in t.value: 74 | t.value = float(t.value) 75 | else: 76 | t.value = int(t.value) 77 | return t 78 | 79 | 80 | def t_QUOTE_STRING(t): 81 | r"'((?<=\\)\'|[^\'])*'" 82 | t.value = t.value[1:-1] 83 | return t 84 | 85 | 86 | def t_DQUOTE_STRING(t): 87 | r'"((?<=\\)\"|[^\"])*"' 88 | t.value = t.value[1:-1] 89 | return t 90 | 91 | 92 | def t_error(t): 93 | raise Exception("Illegal character '%s'", t.value[0]) 94 | t.lexer.skip(1) 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /ql/dsl/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from ql.parse.ASTNode import Node 4 | from ql.parse.parser import TK 5 | 6 | 7 | def parse_tok_table_name(tree : Node): 8 | if tree.get_type() == TK.TOK_TABLE_NAME: 9 | return parse_table_name(tree.get_child(0)) 10 | else: 11 | pass 12 | 13 | 14 | def parse_value(tree: Node) -> str: 15 | if tree.get_type() == TK.TOK_DOT: 16 | retval = parse_value(tree.get_child(0)) 17 | retval += '.' 18 | retval += parse_value(tree.get_child(1)) 19 | return retval 20 | elif tree.get_type() == TK.TOK_VALUE: 21 | return tree.get_value() 22 | else: 23 | pass 24 | 25 | 26 | def parse_left_values(tree: Node) -> list: 27 | retval = [] 28 | for e in tree: 29 | retval.append(parse_value(e)) 30 | return retval 31 | 32 | def parse_tok_value(tree: Node): 33 | if tree.children != None: 34 | if tree.get_child(0).get_type() == TK.TOK_DQ_VALUE: 35 | return '"' + tree.get_value() + '"' 36 | return tree.get_value() 37 | 38 | 39 | def parse_right_values(tree: Node): 40 | retval = [] 41 | for e in tree: 42 | if e.get_type() == TK.TOK_DOT: 43 | retval.append(parse_value(e)) 44 | elif e.get_type() == TK.TOK_VALUE: 45 | retval.append(parse_tok_value(e)) 46 | else: 47 | retval.append(parse_object(e)) 48 | return retval 49 | 50 | 51 | def parse_table_name(tree: Node): 52 | if tree.get_type() == TK.TOK_DOT: 53 | _index = parse_value(tree.get_child(0)) 54 | _type = parse_value(tree.get_child(1)) 55 | elif tree.get_type() == TK.TOK_VALUE: 56 | _index = tree.get_value() 57 | _type = None 58 | return (_index,_type) 59 | 60 | 61 | def parse_kv(tree: Node): 62 | right=None 63 | left=None 64 | if tree.get_type() == TK.TOK_KEY_VALUE: 65 | left = parse_value(tree.get_child(0).get_child(0)) 66 | if tree.get_child(1).get_child(0).get_type() in (TK.TOK_DICT,TK.TOK_LIST): 67 | right = parse_object(tree.get_child(1).get_child(0)) 68 | else: 69 | right = parse_value(tree.get_child(1).get_child(0)) 70 | else: 71 | pass 72 | return {left:right} 73 | 74 | 75 | def parse_object(tree: Node): 76 | retval = None 77 | if tree.get_type() == TK.TOK_DICT: 78 | retval = {} 79 | for element in tree.get_children(): 80 | retval.update(parse_kv(element)) 81 | if tree.get_type() == TK.TOK_LIST: 82 | retval = [] 83 | for element in tree.get_children(): 84 | if element.get_type() in (TK.TOK_DICT,TK.TOK_LIST): 85 | retval.append(parse_object(element)) 86 | else: 87 | retval.append(parse_value(element)) 88 | return retval 89 | -------------------------------------------------------------------------------- /ql/dsl/Aggregation.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Feb 9, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.parse.ASTNode import Node 8 | from ql.parse.parser import TK 9 | from ql.dsl import parse_object,parse_value 10 | from ql.dsl import Query 11 | 12 | def bucket_function(tree: Node,_size): 13 | bucket = {} 14 | bucket[tree.get_value()] = {} 15 | for i in range(0,tree.get_children_count()): 16 | if tree.get_child(i).get_type() == TK.TOK_DICT: 17 | bucket[tree.get_value()].update(parse_object(tree.get_child(i))) 18 | if _size != -1: 19 | bucket[tree.get_value()]['size'] = _size 20 | aggs = {"aggs":{}} 21 | field=bucket[tree.get_value()]['field'] 22 | aggs['aggs'][field] = bucket 23 | return (field,aggs) 24 | 25 | 26 | def bucket_field(tree: Node,_size): 27 | bucket = {} 28 | bucket['terms'] = {} 29 | 30 | field = parse_value(tree) 31 | bucket['terms']['field'] = field 32 | if _size != -1: 33 | bucket['terms']['size'] = _size 34 | 35 | aggs = {"aggs":{}} 36 | aggs['aggs'][field] = bucket 37 | 38 | return (field,aggs) 39 | 40 | 41 | def bucket(tree: Node,_size): 42 | if tree.get_type() == TK.TOK_FUNCTION: 43 | return bucket_function(tree,_size) 44 | else: 45 | return bucket_field(tree,_size) 46 | 47 | 48 | def metrics_functions(selexpr,idx): 49 | alias = '' 50 | if hasattr(selexpr,'alias'): 51 | alias = selexpr.alias 52 | else: 53 | alias = '_' + str(idx) + '_'+ selexpr.selexpr.function_name 54 | metric = {} 55 | if selexpr.selexpr.function_name == 'count': 56 | metric['value_count'] = {} 57 | the_filed = selexpr.selexpr.function_parms[0] 58 | if the_filed == '*': 59 | the_filed = '_index' 60 | metric['value_count'] = {'field':the_filed} 61 | 62 | else: 63 | if selexpr.selexpr.function_name.lower() in ('avg','min','max','sum','cardinality','stats','extended_stats'): 64 | metric[selexpr.selexpr.function_name] = {'field':selexpr.selexpr.function_parms[0]} 65 | else: 66 | metric[selexpr.selexpr.function_name] = {} 67 | for parm in selexpr.selexpr.function_parms: 68 | if type(parm) == dict: 69 | metric[selexpr.selexpr.function_name].update(parm) 70 | return {alias:metric} 71 | 72 | 73 | 74 | def get_metrics(selexprs): 75 | retval = {} 76 | idx = 0 77 | for e in selexprs: 78 | if type(e.selexpr) == Query.FunctionXpr: 79 | retval.update(metrics_functions(e,idx)) 80 | idx = idx + 1 81 | return retval 82 | 83 | 84 | 85 | class AggBuckets(object): 86 | __slots__ = ('buckets') 87 | 88 | def __init__(self,tree: Node,_size,root=True): 89 | self.buckets = [] 90 | for element in tree.get_children(): 91 | self.buckets.append(bucket(element,_size)) 92 | def dsl(self,_selexpr): 93 | (field,bucket) = self.buckets[0] 94 | aggs_body = bucket 95 | cur_aggs = aggs_body['aggs'][field] 96 | for i in range(1,len(self.buckets)): 97 | (field,bucket) = self.buckets[i] 98 | cur_aggs.update(bucket) 99 | cur_aggs = cur_aggs['aggs'][field] 100 | metrics = get_metrics(_selexpr) 101 | cur_aggs['aggs'] = metrics 102 | return aggs_body 103 | 104 | -------------------------------------------------------------------------------- /head/i18n.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | /** 3 | * provides text formatting and i18n key storage features
4 | * implements most of the Sun Java MessageFormat functionality. 5 | * @see Sun's Documentation 6 | */ 7 | 8 | var keys = {}; 9 | var locale = undefined; 10 | 11 | var format = function(message, args) { 12 | var substitute = function() { 13 | var format = arguments[1].split(','); 14 | var substr = escape(args[format.shift()]); 15 | if(format.length === 0) { 16 | return substr; // simple substitution eg {0} 17 | } 18 | switch(format.shift()) { 19 | case "number" : return (new Number(substr)).toLocaleString(locale); 20 | case "date" : return (new Date(+substr)).toLocaleDateString(locale); // date and time require milliseconds since epoch 21 | case "time" : return (new Date(+substr)).toLocaleTimeString(locale); // eg i18n.text("Key", +(new Date())); for current time 22 | } 23 | var styles = format.join("").split("|").map(function(style) { 24 | return style.match(/(-?[\.\d]+)(#|<)([^{}]*)/); 25 | }); 26 | var match = styles[0][3]; 27 | for(var i=0; i 0: 127 | dsl_body['aggs'] = metrics 128 | dsl_body['size'] = 0 129 | return dsl_body 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /elsh/Command.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | import sys 3 | from pygments.token import (Keyword, 4 | Comment, 5 | Operator, 6 | Number, 7 | Literal, 8 | String, 9 | Error) 10 | from prompt_toolkit import AbortAction, prompt 11 | from prompt_toolkit.contrib.completers import WordCompleter 12 | from prompt_toolkit.history import InMemoryHistory 13 | from pygments.style import Style 14 | from pygments.styles.default import DefaultStyle 15 | from pygments.token import Token 16 | import os 17 | import urllib3 18 | from tabulate import tabulate 19 | import json 20 | from pygments.lexers.sql import SqlLexer 21 | 22 | 23 | sql_completer = WordCompleter(['create', 'select', 'insert', 'drop', 24 | 'delete', 'from', 'where', 'table', 25 | 'bulk','update','limit','group','by', 26 | 'index.number_of_shards','index.number_of_replicas', 27 | 'index.flush_inteval','moving_avg','date_range','date_histogram'], ignore_case=True) 28 | 29 | 30 | def do_esql(database,statement): 31 | url = database.host.rstrip() + database.url 32 | http = urllib3.PoolManager() 33 | try: 34 | res = http.request('POST', url, fields = {"sql":statement}) 35 | except Exception as e: 36 | print(str(e)) 37 | return 38 | if res.status != 200: 39 | print(res.data.decode('utf-8')) 40 | else: 41 | gb = json.loads(res.data.decode('utf-8')) 42 | table = gb['rows'] 43 | headers = gb['cols'] 44 | print(tabulate(table, headers, tablefmt="grid",disable_numparse=True)) 45 | if 'total' not in gb: 46 | gb['total'] = 0 47 | if 'took' not in gb: 48 | gb['took'] = 0 49 | print('%d rows affected (%.3f seconds) '%(gb['total'],gb['took']/1000)) 50 | 51 | 52 | class DocumentStyle(Style): 53 | styles = { 54 | Token.Menu.Completions.Completion.Current: 'bg:#00aaaa #000000', 55 | Token.Menu.Completions.Completion: 'bg:#008888 #ffffff', 56 | Token.Menu.Completions.ProgressButton: 'bg:#003333', 57 | Token.Menu.Completions.ProgressBar: 'bg:#00aaaa', 58 | Keyword: 'bold #4b95a3', 59 | Comment: '#757265', 60 | Operator: '#e83131', 61 | Number: '#be61ff', 62 | Literal: '#ae81ff', 63 | String: '#f4a33d', 64 | Error: '#ff3300', 65 | } 66 | styles.update(DefaultStyle.styles) 67 | 68 | 69 | class Database(): 70 | 71 | __slots__ = ('host','url','user','passwd') 72 | def __init__(self,host): 73 | self.url = '/esql' 74 | self.host = host 75 | self.user = '' 76 | self.passwd = '' 77 | 78 | def set_host(self,host): 79 | self.url = host 80 | 81 | def set_urer(self,user,passwd): 82 | self.user = user 83 | self.passwd = passwd 84 | 85 | 86 | def do_config(args,database): 87 | config = args.split(' ') 88 | if config[0] == 'connect': 89 | database.host = config[1] 90 | 91 | def main(database): 92 | history = InMemoryHistory() 93 | input_line = '' 94 | newline=True 95 | while True: 96 | try: 97 | if newline: 98 | text = prompt('esql> ',lexer = SqlLexer, completer=sql_completer, 99 | style=DocumentStyle, history=history, 100 | on_abort=AbortAction.RETRY) 101 | else: 102 | text = prompt('...> ', lexer = SqlLexer,completer=sql_completer, 103 | style=DocumentStyle, history=history, 104 | on_abort=AbortAction.RETRY) 105 | 106 | input_line += os.linesep + text.rstrip() 107 | if input_line.endswith(';'): 108 | if input_line[1] == '\\': 109 | do_config(input_line[2:len(input_line)-1],database) 110 | else: 111 | do_esql(database,input_line[:len(input_line)-1]) 112 | newline = True 113 | input_line = '' 114 | else: 115 | newline = False 116 | 117 | except EOFError: 118 | break # Control-D pressed. 119 | print('GoodBye!') 120 | 121 | if __name__ == '__main__': 122 | if len(sys.argv) < 2: 123 | db = 'http://127.0.0.1:5000' 124 | else: 125 | db = sys.argv[1] 126 | 127 | print('Ctrl + D Quit Esql shell') 128 | main(Database(db)) 129 | 130 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esql5 2 | elsh is an interactive esql5 command line interface (CLI) SQL shell with autocompletion. support elasticserch5.x,2.x,1.x 3 | ![](https://github.com/unimassystem/esql5/blob/master/elsh.png) 4 | 5 | explain 6 | ![](https://github.com/unimassystem/esql5/blob/master/explain.png) 7 | 8 | 特性介绍 9 | ---------------- 10 | #接近标准SQL语法 11 | 12 | create table my_index.my_table ( 13 | id keyword, 14 | name text, 15 | age long, 16 | birthday date 17 | ); 18 | 19 | select * from my_index.my_type; 20 | 21 | select count(*) from my_index.my_table group by age; 22 | 23 | 24 | #Create table 25 | 26 | 字段参数,ES中分词规则、索引类型、字段格式等高级参数的支持 27 | 28 | create table my_table ( 29 | name text (analyzer = ik_max_word), 30 | dd text (index=no), 31 | age long (include_in_all=false) 32 | ); 33 | 34 | 35 | 对象、嵌套字段支持 as 36 | 37 | create table my_index ( 38 | id long, 39 | name text, 40 | obj object as ( 41 | first_name text, 42 | second_name text (analyzer=pinyin) 43 | ) 44 | ); 45 | 46 | 47 | create table my_index ( 48 | id long, 49 | name text, 50 | obj nested as ( 51 | first_name text, 52 | second_name text (analyzer=pinyin) 53 | ) 54 | ); 55 | 56 | 57 | ES索引高级参数支持 with option 58 | 59 | create table my_index ( 60 | id long, 61 | name text 62 | ) with option ( 63 | index.number_of_shards=10, 64 | index.number_of_replicas = 1 65 | ); 66 | 67 | 68 | #Insert/Bulk 69 | 70 | 单条数据插入 71 | insert into my_index.index (name,age) values ('zhangsan',24); 72 | 73 | 多条插入 74 | bulk into my_index.index (name,age) values ('zhangsan',24),('lisi',24); 75 | 76 | 77 | 对象数据插入,[]list,{}Map 78 | 79 | insert into my_index.index (ds) values (['zhejiang','hangzhou']); 80 | 81 | insert into my_index.index (dd) values ({address='zhejiang',postCode='330010'}); 82 | 83 | 84 | #select/Aggregations 85 | 86 | select * from my_table.my_index where name like 'john *' and age between 20 and 30 and (hotel = 'hanting' or flight = 'MH4510'); 87 | 88 | 查询指定路由 89 | select * from my_table@'00001' where name = 'jack'; 90 | 91 | 地理位置中心点查询 92 | select * from hz_point where geo_distance({distance='1km',location='30.306378,120.247427'}); 93 | 94 | 地理坐标区域查询 95 | select * from hz_point where geo_bounding_box({location={top_left='31.306378,119.247427',bottom_right='29.285797,122.172329'}}); 96 | 97 | pipeline统计 move_avg 98 | select count(*) as total, moving_avg({buckets_path=total}) from my_index group by date_histogram({field=timestamp,interval='1h'}); 99 | 100 | 101 | #explain 102 | 103 | 104 | 105 | esql> explain select max(KSSJ) from my_test group by date_histogram({field=ts,interval='1h'}); 106 | 107 | { 108 | "_source": [], 109 | "aggs": { 110 | "ts": { 111 | "aggs": { 112 | "_0_max": { 113 | "max": { 114 | "field": "KSSJ" 115 | } 116 | } 117 | }, 118 | "date_histogram": { 119 | "field": "ts", 120 | "interval": "1h" 121 | } 122 | } 123 | }, 124 | "query": { 125 | "match_all": {} 126 | }, 127 | "size": 0 128 | } 129 | 130 | 131 | 132 | Getting Started 133 | ---------------- 134 | 环境要求python >= 2.7 135 | 136 | export PYTHONHOME=(%python_path) 137 | export PATH=$PYTHONHOME/bin:$PATH 138 | 139 | 140 | 安装第三方依赖包 141 | pip install -r esql5.egg-info/requires.txt 142 | 或python setup.py install 143 | 144 | 修改配置文件esql5/conf 145 | 146 | elastic: { 147 | hosts: [ 148 | { 149 | host: 127.0.0.1 150 | port: 9200 151 | } 152 | ] 153 | } 154 | 155 | 运行esql5服务 156 | (standalone): 157 | cd esql5 158 | python -m App.app 159 | 160 | (with uwsgi) 161 | cd esql5/bin 162 | ./service start 163 | 164 | 165 | shell终端: 166 | cd esql5/bin 167 | ./elsh 168 | 169 | JDBC驱动: 170 | https://github.com/unimassystem/elasticsearch-jdbc 171 | 172 | String sql = "select SRC_IP,SRC_PORT from my_test* where SRC_PORT between 10 and 100 limit 1000"; 173 | String url = "jdbc:elasticsearch://127.0.0.1:5000"; 174 | Connection connection = DriverManager.getConnection(url, "test", null); 175 | Statement statement = connection.createStatement(); 176 | ResultSet rs = statement.executeQuery(sql); 177 | ResultSetMetaData meta = rs.getMetaData(); 178 | String columns = "|"; 179 | for (int i = 0; i < meta.getColumnCount(); i++) { 180 | columns += meta.getColumnLabel(i) + " | "; 181 | } 182 | System.out.println(columns); 183 | while (rs.next()) { 184 | String row = "|"; 185 | for (int i = 0; i < meta.getColumnCount(); i++) { 186 | row += rs.getString(i) + " | "; 187 | } 188 | System.out.println(row); 189 | } 190 | 191 | 192 | -------------------------------------------------------------------------------- /ql/dsl/QueryBody.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Jan 13, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.dsl import parse_left_values,parse_right_values,parse_value,parse_object 8 | from ql.parse.ASTNode import Node 9 | from ql.parse.parser import TK 10 | 11 | def query_string_query(left_val,right_val,operator='AND'): 12 | return { 13 | 'query_string':{ 14 | 'default_operator': operator, 15 | 'query': right_val, 16 | 'fields': left_val 17 | } 18 | } 19 | 20 | 21 | def range_query(compare,left_val,right_val): 22 | if compare == '>': 23 | compare = 'gt' 24 | elif compare == '<': 25 | compare = 'lt' 26 | elif compare == '>=': 27 | compare = 'gte' 28 | elif compare == '<=': 29 | compare = 'lte' 30 | return { 31 | 'range': { 32 | left_val[0]:{ 33 | compare:right_val 34 | } 35 | } 36 | } 37 | 38 | 39 | def wildcard_query(left_val,right_val): 40 | return {'wildcard': {left_val[0]:right_val}} 41 | 42 | 43 | 44 | 45 | def between_query(fun_parms): 46 | parms = parse_right_values(fun_parms) 47 | return { 48 | 'range': { 49 | parms[0]:{ 50 | 'gte':parms[1], 51 | 'lte':parms[2], 52 | } 53 | } 54 | } 55 | 56 | 57 | def query_missing(fun_parms): 58 | parms = parse_right_values(fun_parms) 59 | return { 60 | 'constant_score': { 61 | 'filter': { 62 | 'missing': { 63 | 'field': parms[0] 64 | } 65 | } 66 | } 67 | } 68 | 69 | def other_functions(name,parms): 70 | retval={name:{}} 71 | for e in parms: 72 | if e.get_type() == TK.TOK_DICT: 73 | retval[name].update(parse_object(e)) 74 | return retval 75 | 76 | 77 | 78 | 79 | def function_expression_dsl(name, parms): 80 | if name.lower() == 'between': 81 | return between_query(parms) 82 | if name.lower() == 'isnull': 83 | return query_missing(parms) 84 | else: 85 | return other_functions(name,parms) 86 | 87 | 88 | 89 | def compare_expression_dsl(compare,left_val,right_val): 90 | if compare == '=': 91 | return query_string_query(left_val,right_val) 92 | elif compare in ['>','>=','<','<=']: 93 | return range_query(compare,left_val,right_val) 94 | elif compare.lower() == 'like': 95 | return wildcard_query(left_val,right_val) 96 | 97 | 98 | def in_expression_dsl(left_val,right_val): 99 | return query_string_query(left_val,' '.join(right_val),'OR') 100 | 101 | 102 | class FunctionExpression(object): 103 | __slots__ = ('function_name','function_parms') 104 | def __init__(self,tree: Node): 105 | self.function_name = tree.get_value() 106 | self.function_parms = tree.get_children() 107 | def dsl(self): 108 | return function_expression_dsl(self.function_name,self.function_parms) 109 | 110 | 111 | class INExpression(object): 112 | __slots__ = ('left_values','right_value') 113 | def __init__(self,tree: Node): 114 | self.left_values = parse_left_values(tree.get_child(0).get_children()) 115 | self.right_value = parse_right_values(tree.get_child(1).get_children()) 116 | def dsl(self): 117 | return in_expression_dsl(self.left_values,self.right_value) 118 | 119 | 120 | def query_expression(tree: Node): 121 | if tree.get_type() == TK.TOK_COMPARE: 122 | return CompareExpression(tree) 123 | if tree.get_type() == TK.TOK_FUNCTION: 124 | return FunctionExpression(tree) 125 | if tree.get_type() == TK.TOK_IN: 126 | return INExpression(tree) 127 | 128 | 129 | class CompareExpression(object): 130 | 131 | __slots__ = ('compare','left_values','right_value') 132 | 133 | def __init__(self,tree: Node): 134 | self.compare = tree.get_value().lower() 135 | if tree.get_child(0).get_type() == TK.TOK_EXPRESSION_LEFT: 136 | self.left_values = parse_left_values(tree.get_child(0).get_children()) 137 | if tree.get_child(1).get_type() == TK.TOK_EXPRESSION_RIGHT: 138 | if len(parse_right_values(tree.get_child(1).get_children())) > 0: 139 | self.right_value = parse_right_values(tree.get_child(1).get_children())[0] 140 | else: 141 | self.right_value = None 142 | def dsl(self): 143 | return compare_expression_dsl(self.compare,self.left_values,self.right_value) 144 | 145 | 146 | 147 | class QueryBody(object): 148 | 149 | __slots__ = ('combine','root','reversed','lchild','rchild') 150 | 151 | def __init__(self,tree: Node,root=True): 152 | 153 | if tree.get_type() == TK.TOK_REVERSED: 154 | tree = tree.get_child(0) 155 | self.reversed = True 156 | 157 | self.root = root; 158 | self.combine = 'must' 159 | if tree.get_type() == TK.TOK_COMPOUND: 160 | self.combine = tree.get_value() 161 | self.lchild = QueryBody(tree.get_child(0),False) 162 | self.rchild = QueryBody(tree.get_child(1),False) 163 | else: 164 | self.lchild = query_expression(tree) 165 | 166 | 167 | def dsl(self): 168 | ret = {} 169 | if self.combine == 'and': 170 | self.combine = 'must' 171 | if self.combine == 'or': 172 | self.combine = 'should' 173 | if self.root: 174 | ret = {'bool': {}} 175 | ret['bool'][self.combine] = [self.lchild.dsl()] 176 | if hasattr(self, 'rchild'): 177 | ret['bool'][self.combine].append(self.rchild.dsl()) 178 | else: 179 | if hasattr(self, 'rchild'): 180 | ret = {'bool': {}} 181 | ret['bool'][self.combine] = [self.lchild.dsl()] + [self.rchild.dsl()] 182 | else: 183 | ret = self.lchild.dsl() 184 | if hasattr(self, 'reversed') and self.reversed == True: 185 | rev = {'bool': {}} 186 | rev['bool']['must_not'] = [ret] 187 | ret = rev 188 | return ret 189 | 190 | 191 | 192 | 193 | -------------------------------------------------------------------------------- /head/lang/zh_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs": "加载聚合查询...", 4 | "General.Searching": "搜索中...", 5 | "General.Search": "搜索", 6 | "General.Help": "帮助", 7 | "General.HelpGlyph": "?", 8 | "General.CloseGlyph": "X", 9 | "General.RefreshResults": "刷新", 10 | "General.ManualRefresh": "手动刷新", 11 | "General.RefreshQuickly": "快速刷新", 12 | "General.Refresh5seconds": "每5秒刷新", 13 | "General.Refresh1minute": "每1分钟刷新", 14 | "AliasForm.AliasName": "别名", 15 | "AliasForm.NewAliasForIndex": "为 {0} 创建新别名", 16 | "AliasForm.DeleteAliasMessage": "输入 ''{0}'' 删除 {1}. 此操作无法恢复", 17 | "AnyRequest.DisplayOptions" : "显示选项", 18 | "AnyRequest.AsGraph" : "图形视图", 19 | "AnyRequest.AsJson" : "原始 JSON", 20 | "AnyRequest.AsTable" : "表格视图", 21 | "AnyRequest.History" : "历史记录", 22 | "AnyRequest.RepeatRequest" : "重复请求", 23 | "AnyRequest.RepeatRequestSelect" : "重复周期 ", 24 | "AnyRequest.Transformer" : "结果转换器", 25 | "AnyRequest.Pretty": "易读", 26 | "AnyRequest.Query" : "查询", 27 | "AnyRequest.Request": "提交请求", 28 | "AnyRequest.Requesting": "请求中...", 29 | "AnyRequest.ValidateJSON": "验证 JSON", 30 | "Browser.Title": "数据浏览", 31 | "Browser.ResultSourcePanelTitle": "原始数据", 32 | "Command.DELETE": "删除", 33 | "Command.SHUTDOWN": "关闭", 34 | "Command.DeleteAliasMessage": "删除别名?", 35 | "ClusterOverView.IndexName": "索引名称", 36 | "ClusterOverview.NumShards": "分片数", 37 | "ClusterOverview.NumReplicas": "副本数", 38 | "ClusterOverview.NewIndex": "新建索引", 39 | "IndexActionsMenu.Title": "动作", 40 | "IndexActionsMenu.NewAlias": "新建别名...", 41 | "IndexActionsMenu.Refresh": "刷新", 42 | "IndexActionsMenu.Flush": "Flush刷新", 43 | "IndexActionsMenu.Optimize": "优化...", 44 | "IndexActionsMenu.Snapshot": "网关快照", 45 | "IndexActionsMenu.Analyser": "测试分析器", 46 | "IndexActionsMenu.Open": "开启", 47 | "IndexActionsMenu.Close": "关闭", 48 | "IndexActionsMenu.Delete": "删除...", 49 | "IndexInfoMenu.Title": "信息", 50 | "IndexInfoMenu.Status": "索引状态", 51 | "IndexInfoMenu.Metadata": "索引信息", 52 | "IndexCommand.TextToAnalyze": "文本分析", 53 | "IndexCommand.ShutdownMessage": "输入 ''{0}'' 以关闭 {1} 节点. 关闭的节点无法从此界面重新启动", 54 | "IndexOverview.PageTitle": "索引概览", 55 | "IndexSelector.NameWithDocs": "{0} ({1} 个文档)", 56 | "IndexSelector.SearchIndexForDocs": "搜索 {0} 的文档, 查询条件:", 57 | "FilterBrowser.OutputType": "返回格式: {0}", 58 | "FilterBrowser.OutputSize": "显示数量: {0}", 59 | "Header.ClusterHealth": "集群健康值: {0} ({1} of {2})", 60 | "Header.ClusterNotConnected": "集群健康值: 未连接", 61 | "Header.Connect": "连接", 62 | "Nav.AnyRequest": "复合查询", 63 | "Nav.Browser": "数据浏览", 64 | "Nav.ClusterHealth": "集群健康值", 65 | "Nav.ClusterState": "群集状态", 66 | "Nav.ClusterNodes": "集群节点", 67 | "Nav.Info": "信息", 68 | "Nav.NodeStats": "节点状态", 69 | "Nav.Overview": "概览", 70 | "Nav.Indices": "索引", 71 | "Nav.Plugins": "插件", 72 | "Nav.Status": "状态", 73 | "Nav.Templates": "模板", 74 | "Nav.StructuredQuery": "基本查询", 75 | "NodeActionsMenu.Title": "动作", 76 | "NodeActionsMenu.Shutdown": "关停...", 77 | "NodeInfoMenu.Title": "信息", 78 | "NodeInfoMenu.ClusterNodeInfo": "集群节点信息", 79 | "NodeInfoMenu.NodeStats": "节点状态", 80 | "NodeType.Client": "节点客户端", 81 | "NodeType.Coord": "协调器", 82 | "NodeType.Master": "主节点", 83 | "NodeType.Tribe": "分支结点", 84 | "NodeType.Worker": "工作节点", 85 | "NodeType.Unassigned": "未分配", 86 | "OptimizeForm.OptimizeIndex": "优化 {0}", 87 | "OptimizeForm.MaxSegments": "最大索引段数", 88 | "OptimizeForm.ExpungeDeletes": "只删除被标记为删除的", 89 | "OptimizeForm.FlushAfter": "优化后刷新", 90 | "OptimizeForm.WaitForMerge": "等待合并", 91 | "Overview.PageTitle" : "集群概览", 92 | "Output.JSON": "JSON", 93 | "Output.Table": "Table", 94 | "Output.CSV": "CSV", 95 | "Output.ShowSource": "显示查询语句", 96 | "Preference.SortCluster": "集群排序", 97 | "Sort.ByName": "按名称", 98 | "Sort.ByAddress": "按地址", 99 | "Sort.ByType": "按类型", 100 | "TableResults.Summary": "查询 {1} 个分片中用的 {0} 个. {2} 命中. 耗时 {3} 秒", 101 | "QueryFilter.AllIndices": "所有索引", 102 | "QueryFilter.AnyValue": "任意", 103 | "QueryFilter-Header-Indices": "索引", 104 | "QueryFilter-Header-Types": "类型", 105 | "QueryFilter-Header-Fields": "字段", 106 | "QueryFilter.DateRangeHint.from": "从 : {0}", 107 | "QueryFilter.DateRangeHint.to": " 到 : {0}", 108 | "Query.FailAndUndo": "查询失败. 撤消最近的更改", 109 | "StructuredQuery.ShowRawJson": "显示原始 JSON" 110 | }); 111 | 112 | i18n.setKeys({ 113 | "AnyRequest.TransformerHelp" : "\ 114 |

结果转换器用于返回结果原始JSON的后续处理, 将结果转换为更有用的格式.

\ 115 |

转换器应当包含javascript函数体. 函数的返回值将传递给json分析器

\ 116 |

Example:
\ 117 | return root.hits.hits[0];
\ 118 | 遍历结果并只显示第一个元素
\ 119 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0);
\ 120 | 将返回整个集群使用的总内存

\ 121 |

以下函数可以方便的处理数组与对象
\ 122 |

    \ 123 |
  • Object.keys(object) := array
  • \ 124 |
  • array.forEach(function(prop, index))
  • \ 125 |
  • array.map(function(prop, index)) := array
  • \ 126 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 127 |
\ 128 |

当启用重复请求时, prev 参数将会传递给转换器函数. 这将用于比较并累加图形

\ 129 |

Example:
\ 130 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la;
\ 131 | 将返回第一个集群节点最近一分钟内的平均负载\ 132 | 将会把结果送人图表以产生一个负载曲线图\ 133 | " 134 | }); 135 | 136 | i18n.setKeys({ 137 | "AnyRequest.DisplayOptionsHelp" : "\ 138 |

原始 Json: 将完整的查询结果转换为原始JSON格式

\ 139 |

图形视图: 将查询结果图形化, 将查询结果转换为数组值的形式

\ 140 |

表格视图: 如果查询是一个搜索, 可以将搜索结果以表格形式显示.

\ 141 | " 142 | }); 143 | 144 | i18n.setKeys({ 145 | "QueryFilter.DateRangeHelp" : "\ 146 |

Date 字段接受日期范围的形式查询.

\ 147 |

一下格式被支持:

\ 148 |
    \ 149 |
  • 关键词 / 关键短语
    \ 150 | now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 151 | 搜索关键字匹配的日期. last year 将搜索过去全年.
  • \ 152 |
  • 范围
    \ 153 | 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (空格可选, 同等于多个范围修饰词)
    \ 154 | 创建一个指定时间范围的搜索, 将围绕现在 并延伸至过去与未来时间段.
  • \ 155 |
  • DateTime 与 DateTime局部
    \ 156 | 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 157 | 指定一个特定的日期范围. 2011会搜索整个 2011年, 而 2011-01-18 12:32:45 将只搜索1秒范围内
  • \ 158 |
  • Time 与 Time局部
    \ 159 | 12
    12:32
    12:32:45

    \ 160 | 这些格式只搜索当天的特定时间. 12:32 将搜索当天的那一分钟
  • \ 161 |
  • 日期范围
    \ 162 | 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 163 | 日期范围是将两个日期格式串 (日期关键字 / DateTime / Time) 用 < 或 -> (效果相同) 分隔. 如果缺少任意一端,那么在这个方向上时间将没有限制.
  • \ 164 |
  • 偏移日期范围
    \ 165 | 2010 -> 1yr
    3mins < now
    \ 166 | 搜索包括指定方向上偏移的日期.
  • \ 167 |
  • 锚定范围
    \ 168 | 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 169 | 类似于上面的便宜日期,在两个方向上将锚定的日期延长
  • \ 170 |
\ 171 | " 172 | }); 173 | -------------------------------------------------------------------------------- /ql/dsl/Response.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Mar 10, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | def parse_hit(cols,metas,hit): 8 | row = [] 9 | for meta in metas: 10 | row.append(hit[meta]) 11 | if '_source' not in hit: 12 | return row 13 | 14 | for col in cols: 15 | if col in hit['_source'].keys(): 16 | row.append(hit['_source'][col]) 17 | else: 18 | row.append(None) 19 | return row 20 | 21 | 22 | def parse_hits_cols(hits): 23 | fields = {} 24 | for hit in hits: 25 | if '_source' in hit: 26 | for key in hit['_source'].keys(): 27 | fields[key] = True 28 | return list(fields.keys()) 29 | 30 | 31 | def parse_hits(cols,hits): 32 | if 'hits' not in hits: 33 | return 34 | hits = hits['hits'] 35 | metas = ['_id','_index','_type'] 36 | # cols = parse_hits_cols(hits) 37 | rows = [] 38 | for hit in hits: 39 | rows.append(parse_hit(cols,metas,hit)) 40 | retval = {} 41 | retval['cols'] = metas + cols 42 | retval['rows'] = rows 43 | return retval 44 | 45 | 46 | def parse_aggs_cols(aggs,bks): 47 | for k in aggs.keys(): 48 | if type(aggs[k]) != dict: 49 | continue 50 | if 'buckets' in aggs[k].keys(): 51 | bks.append(k) 52 | buckets = aggs[k]['buckets'] 53 | if len(buckets) > 0: 54 | parse_aggs_cols(buckets[0],bks) 55 | 56 | 57 | 58 | def parse_aggs_rows(aggs,bks,depth,rows,bucket_vals=[]): 59 | bucket_name = None 60 | if depth < len(bks): 61 | bucket_name = bks[depth] 62 | if 'buckets' in aggs[bucket_name] : 63 | for bucket in aggs[bucket_name]['buckets']: 64 | if 'key_as_string' in bucket: 65 | bucket_vals.append(bucket['key_as_string']) 66 | elif 'key' in bucket: 67 | bucket_vals.append(bucket['key']) 68 | else: 69 | bucket_vals.append(None) 70 | parse_aggs_rows(bucket,bks,depth + 1,rows,bucket_vals) 71 | if len(bucket_vals) > 0: 72 | bucket_vals.pop(len(bucket_vals) - 1) 73 | else: 74 | row = bucket_vals[:] 75 | mts = {} 76 | for mertic in aggs.keys(): 77 | if type(aggs[mertic]) == dict and 'value' in aggs[mertic].keys(): 78 | if 'value_as_string' in aggs[mertic].keys(): 79 | mts[mertic] = aggs[mertic]['value_as_string'] 80 | else: 81 | mts[mertic] = aggs[mertic]['value'] 82 | rows.append((row,mts)) 83 | if len(bucket_vals) > 0: 84 | bucket_vals.pop(len(bucket_vals) - 1) 85 | pass 86 | 87 | 88 | 89 | def get_agg_rows(bks,rows): 90 | 91 | mertics = {} 92 | for (bucket,mts) in rows: 93 | for m in mts.keys(): 94 | mertics[m] = True 95 | for m in mertics.keys(): 96 | bks.append(m) 97 | retval = [] 98 | for (bucket,mts) in rows: 99 | row = bucket 100 | for m in mertics.keys(): 101 | if m in mts.keys(): 102 | row.append(mts[m]) 103 | else: 104 | row.append(None) 105 | retval.append(row) 106 | return retval 107 | 108 | 109 | def parse_aggregations(aggs): 110 | bks = [] 111 | depth = 0 112 | rows = [] 113 | retval = {} 114 | parse_aggs_cols(aggs,bks) 115 | parse_aggs_rows(aggs,bks,depth,rows) 116 | row_sets = get_agg_rows(bks,rows) 117 | retval['cols'] = bks 118 | retval['rows'] = row_sets 119 | return retval 120 | 121 | 122 | def get_type_cols(tp): 123 | if 'properties' in tp.keys(): 124 | return tp['properties'].keys() 125 | 126 | def get_inx_cols(inx): 127 | cols=[] 128 | if 'mappings' in inx.keys(): 129 | for tp in inx['mappings']: 130 | cols.extend(get_type_cols(inx['mappings'][tp])) 131 | return cols 132 | 133 | def get_cols(mappings): 134 | cols=[] 135 | for inx in mappings.keys(): 136 | cols.extend(get_inx_cols(mappings[inx])) 137 | return list(set(cols)) 138 | 139 | 140 | def response_hits(res,mappings,selecols): 141 | response = {} 142 | if len(selecols) == 1 and selecols[0] == '*': 143 | cols = get_cols(mappings) 144 | else: 145 | cols = selecols 146 | if 'aggregations' in res.keys(): 147 | response = parse_aggregations(res['aggregations']) 148 | response['total'] = len(response['rows']) 149 | elif 'hits' in res.keys() and 'aggregations' not in res.keys(): 150 | response = parse_hits(cols,res['hits']) 151 | response['total'] = res['hits']['total'] 152 | else: 153 | response['cols'] = [] 154 | response['rows'] = [] 155 | response['total'] = 1 156 | if 'took' in res: 157 | response['took'] = res['took'] 158 | else: 159 | response['took'] = 0 160 | 161 | return response 162 | 163 | 164 | 165 | def response_nor(res,took): 166 | response = {} 167 | response['cols'] = [] 168 | response['rows'] = [] 169 | response['total'] = 1 170 | response['took'] = took 171 | return response 172 | 173 | 174 | def _parse_cat_json(res,response): 175 | cols= {} 176 | for row in res: 177 | for col in row.keys(): 178 | cols[col] = True 179 | response['cols'] = list(cols.keys()) 180 | for row in res: 181 | record=[] 182 | for col in response['cols']: 183 | record.append(row[col]) 184 | response['rows'].append(record) 185 | pass 186 | 187 | 188 | def _parse_cat_plain(res,response): 189 | lines = res.split('\n') 190 | head = ' '.join(lines[0].split()) 191 | response['cols'] = head.split() 192 | for line in lines[1:]: 193 | if len(line) <= 0: 194 | continue 195 | row = ' '.join(line.split()) 196 | response['rows'].append(row.split()) 197 | pass 198 | 199 | 200 | def response_cat(res,took): 201 | response = {} 202 | response['cols'] = [] 203 | response['rows'] = [] 204 | 205 | if type(res) == str: 206 | _parse_cat_plain(res,response) 207 | else: 208 | _parse_cat_json(res,response) 209 | 210 | response['total'] = len(response['rows']) 211 | response['took'] = took 212 | return response 213 | 214 | 215 | 216 | def response_mappings(res,took): 217 | 218 | response = {} 219 | response['cols'] = ['index','doc_type','col_name','data_type','properties'] 220 | response['rows'] = [] 221 | 222 | for _index in res.keys(): 223 | index = res[_index] 224 | if 'mappings' in index.keys(): 225 | mappings = index['mappings'] 226 | for _doc_type in mappings.keys(): 227 | doc_type = mappings[_doc_type] 228 | if 'properties' in doc_type.keys(): 229 | properties = doc_type['properties'] 230 | for _field in properties.keys(): 231 | _type = 'object' 232 | _properties = None 233 | field = properties[_field] 234 | if 'type' in field.keys(): 235 | _type = field['type'] 236 | if 'properties' in field.keys(): 237 | _properties = field['properties'] 238 | response['rows'].append([_index,_doc_type,_field,_type,_properties]) 239 | response['total'] = len(response['rows']) 240 | response['took'] = took 241 | return response 242 | 243 | 244 | def response_bulk(res): 245 | response = {} 246 | response['cols'] = [] 247 | response['rows'] = [] 248 | response['total'] = 0 249 | response['took'] = 0 250 | if 'took' in res.keys(): 251 | response['took'] = res['took'] 252 | if 'items' in res.keys(): 253 | response['total'] = len(res['items']) 254 | return response 255 | 256 | 257 | 258 | -------------------------------------------------------------------------------- /ql/utest.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Dec 23, 2016 3 | 4 | @author: qs 5 | ''' 6 | # -*- coding: utf-8 -*- 7 | 8 | 9 | 10 | 11 | from ql.parse import lexer 12 | from ql.parse import parser 13 | from ply.lex import lex 14 | from ply.yacc import yacc 15 | from ql.parse.parser import TK 16 | 17 | from ql.dsl.Explain import Explain 18 | from ql.dsl.Insert import Insert,Bulk 19 | from ql.dsl.Update import Update,Upsert 20 | from ql.dsl.Delete import Delete 21 | from ql.dsl.Query import Query 22 | from ql.dsl.Response import response_hits 23 | from ql.dsl.Create import Create 24 | from ql.dsl.Describe import Describe 25 | import sys 26 | import json 27 | from elasticsearch import Elasticsearch 28 | 29 | 30 | 31 | def exec_query(stmt): 32 | 33 | my_lexer=lex(module=lexer,optimize=True,debug=True) 34 | 35 | my_parser=yacc(debug=True,module=parser) 36 | 37 | val = my_parser.parse(lexer=my_lexer.clone(),debug=False,input=sql) 38 | 39 | es = Elasticsearch([{'host':"10.68.23.81","port":9200}]) 40 | 41 | 42 | val.debug() 43 | 44 | if val.get_type() == TK.TOK_QUERY: 45 | 46 | query = Query(val) 47 | 48 | print(query.dsl()) 49 | 50 | print(query._index,query._type) 51 | 52 | if hasattr(query, 'route'): 53 | res = es.search(index=query._index, doc_type = query._type, body=query.dsl(),routing=query.route, request_timeout=100) 54 | else: 55 | res = es.search(index=query._index, doc_type = query._type, body=query.dsl(), request_timeout=100) 56 | 57 | mappings = es.indices.get_mapping(index=query._index, doc_type = query._type) 58 | 59 | selecols = query.dsl()['_source'] 60 | 61 | stmt_res = None 62 | 63 | stmt_res = response_hits(res,mappings,selecols) 64 | 65 | print(json.dumps(stmt_res,indent=4)) 66 | 67 | elif val.get_type() == TK.TOK_CREATE_TABLE: 68 | 69 | stmt = Create(val) 70 | 71 | res = es.indices.create(index=stmt._index,body = stmt._options,request_timeout=100,ignore= 400) 72 | 73 | res = es.indices.put_mapping(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), request_timeout=100) 74 | 75 | print(json.dumps(res,indent=4)) 76 | 77 | elif val.get_type() == TK.TOK_INSERT_INTO: 78 | 79 | # val.debug() 80 | 81 | stmt = Insert(val) 82 | 83 | parms = stmt.metas 84 | 85 | res = es.index(index = stmt._index,doc_type = stmt._type, body = stmt.dsl(),**parms) 86 | 87 | print(json.dumps(res,indent=4)) 88 | 89 | elif val.get_type() == TK.TOK_BULK_INTO: 90 | 91 | # val.debug() 92 | 93 | 94 | stmt = Bulk(val) 95 | 96 | res = es.bulk(index = stmt._index,doc_type = stmt._type, body = stmt.dsl()) 97 | 98 | print(json.dumps(res,indent=4)) 99 | 100 | 101 | elif val.get_type() == TK.TOK_UPDATE: 102 | 103 | val.debug() 104 | 105 | stmt = Update(val) 106 | 107 | print(json.dumps(stmt.dsl(),indent=4)) 108 | 109 | res = es.update(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), **stmt.conditions) 110 | 111 | 112 | print(json.dumps(res,indent=4)) 113 | 114 | 115 | elif val.get_type() == TK.TOK_UPSERT_INTO: 116 | 117 | val.debug() 118 | 119 | stmt = Upsert(val) 120 | 121 | print(json.dumps(stmt.dsl(),indent=4)) 122 | 123 | res = es.update(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), **stmt.conditions) 124 | 125 | 126 | print(json.dumps(res,indent=4)) 127 | 128 | 129 | elif val.get_type() == TK.TOK_DELETE: 130 | 131 | val.debug() 132 | 133 | stmt = Delete(val) 134 | 135 | res = es.delete_by_query(index = stmt._index, doc_type = stmt._type, body = stmt.dsl()) 136 | # res = es.delete(index = stmt._index, doc_type = stmt._type, **stmt.conditions,ignore= 404) 137 | 138 | print(json.dumps(res,indent=4)) 139 | 140 | 141 | elif val.get_type() == TK.TOK_EXPLAIN: 142 | stmt = Explain(val) 143 | print(stmt.curl_str) 144 | print(json.dumps(stmt.dsl(),indent=4)) 145 | 146 | elif val.get_type() == TK.TOK_DESC_TABLE: 147 | 148 | stmt = Describe(val) 149 | 150 | 151 | res = es.indices.get_mapping(index = stmt._index,doc_type=stmt._type) 152 | 153 | print(res) 154 | 155 | 156 | else: 157 | res = es.cat.indices(index = 'qs_test*', v=True) 158 | val.debug() 159 | print(res) 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | if __name__ == "__main__": 168 | 169 | if len(sys.argv) < 2: 170 | sqls = [ 171 | 172 | # '''create table qs_test03.ccx ( 173 | # name string (analyzer = ik), 174 | # timestamp date, 175 | # age long 176 | # ) with option ( 177 | # index.number_of_shards=10, 178 | # index.number_of_replicas = 1, 179 | # index.flush_inteval='10s' 180 | # )''', 181 | # '''show tables''', 182 | # '''desc flow_i''' 183 | # 184 | # '''create table my_tb.ccx ( 185 | # a string (index=no), 186 | # c object as ( 187 | # raw string (index=not_analyzed,doc_values=false), 188 | # obj object as ( 189 | # ddd string (index=no) 190 | # ) 191 | # ) 192 | # ) with meta ( 193 | # _parent (type='people'), 194 | # _source (includes = [a,'*c']) 195 | # ) with option ( 196 | # index.number_of_shards=10, 197 | # index.number_of_replicas = 1, 198 | # index.flush_inteval='10s' 199 | # );''', 200 | 201 | # '''select * from _all''', 202 | 203 | # '''select count(*) as c,count(*) as cc ,sum(dd) as dd,moving_avg({buckets_path=c,window=30,model=simple}), moving_avg({buckets_path=dd,window=30,model=simple}) 204 | # from my_index02 205 | # group by name,date_histogram({field=ts,interval='1h'});''', 206 | # 207 | # '''select count(*) from my_index02 group by date_range({field=ts,ranges=[{to='now-10M/M',from=now},{to='now',from='now-10M/M'}]});''', 208 | # 209 | # '''insert into my_index.base (_id,_routing,name,age,address,message) values (200,200,'zhangsan',24,{address='zhejiang',postCode='330010'},['sms001','sms002'])''', 210 | # # 211 | # '''bulk into my_index_occ.base(_id,name,age,address,message) values 212 | # (1,'zhangsan',24,{address='zhejiang',postCode='330010'},['sms:001','sms:002']), 213 | # (2,'zhangsan',25,{address='zhejiang',postCode='330010'},['sms:001','sms:002'])''', 214 | # 215 | # 216 | # '''update my_index_occ.base set name = 'lisi' ,age = 30,address={address='shanghai',postCode='3300100009'} where _id = 1''', 217 | # 218 | # '''upsert my_index_occ.base set name1 = 'lisi' ,age1 = 30,address1={address='shanghai',postCode='3300100009'} where _id = 1''', 219 | # 220 | # '''delete from test_ts where 1 = 1''' 221 | # 222 | # 223 | # '''explain select count(*) as c,count(*) as cc ,sum(dd) as dd,moving_avg({buckets_path=c,window=30,model=simple}), moving_avg({buckets_path=dd,window=30,model=simple}) 224 | # from my_index02 225 | # group by name,date_histogram({field=ts,interval='1h'});''', 226 | # 227 | # '''select * from "config_log-'23'".base where app_name in ("login",'policy') and app_id > 1001 and app_ii = "2001"''', 228 | '''select * from qs_001@'3301' ''', 229 | 230 | ] 231 | 232 | for sql in sqls: 233 | print(sql) 234 | exec_query(sql) 235 | 236 | else: 237 | sql = sys.argv[1] 238 | exec_query(sql) 239 | 240 | 241 | -------------------------------------------------------------------------------- /head/lang/ja_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | // "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs": "Aggregations ロード中...", 4 | "General.Searching": "検索中...", 5 | "General.Search": "Search", 6 | "General.Help": "ヘルプ", 7 | // "General.HelpGlyph": "?", 8 | // "General.CloseGlyph": "X", 9 | "General.RefreshResults": "リフレッシュ", 10 | "General.ManualRefresh": "マニュアルモード", 11 | "General.RefreshQuickly": "クイックモード", 12 | "General.Refresh5seconds": "5秒毎にリフレッシュ", 13 | "General.Refresh1minute": "1分毎にリフレッシュ", 14 | "AliasForm.AliasName": "エイリアス名", 15 | "AliasForm.NewAliasForIndex": "{0} の新しいエイリアス", 16 | "AliasForm.DeleteAliasMessage": "インデックス ''{1}'' を削除するために ''{0}'' とタイプして下さい. この操作は undo できません.", 17 | "AnyRequest.DisplayOptions" : "表示オプション", 18 | "AnyRequest.AsGraph" : "グラフの表示", 19 | "AnyRequest.AsJson" : "生の JSON を表示", 20 | "AnyRequest.AsTable" : "テーブルの表示", 21 | "AnyRequest.History" : "履歴", 22 | "AnyRequest.RepeatRequest" : "繰り返しリクエストを投げる", 23 | "AnyRequest.RepeatRequestSelect" : "次の間隔でリクエストを繰り返す", 24 | "AnyRequest.Transformer" : "Result Transformer", 25 | // "AnyRequest.Pretty": "Pretty", 26 | "AnyRequest.Query" : "Query", 27 | "AnyRequest.Request": "Request", 28 | "AnyRequest.Requesting": "検索中...", 29 | "AnyRequest.ValidateJSON": "Validate JSON", 30 | "Browser.Title": "Browser", 31 | "Browser.ResultSourcePanelTitle": "Result Source", 32 | "Command.DELETE": "DELETE", 33 | "Command.SHUTDOWN": "SHUTDOWN", 34 | "Command.DeleteAliasMessage": "エイリアスを削除しますか?", 35 | "ClusterOverView.IndexName": "Index名", 36 | "ClusterOverview.NumShards": "Number of Shards", 37 | "ClusterOverview.NumReplicas": "Number of Replicas", 38 | "ClusterOverview.NewIndex": "インデックスの作成", 39 | // "IndexActionsMenu.Title": "Actions", 40 | "IndexActionsMenu.NewAlias": "新しいエイリアス...", 41 | "IndexActionsMenu.Refresh": "Refresh", 42 | "IndexActionsMenu.Flush": "Flush", 43 | "IndexActionsMenu.Optimize": "Optimize...", 44 | "IndexActionsMenu.Snapshot": "Gateway Snapshot", 45 | "IndexActionsMenu.Analyser": "Analyserテスト", 46 | // "IndexActionsMenu.Open": "Open", 47 | // "IndexActionsMenu.Close": "Close", 48 | // "IndexActionsMenu.Delete": "Delete...", 49 | // "IndexInfoMenu.Title": "Info", 50 | // "IndexInfoMenu.Status": "Index Status", 51 | // "IndexInfoMenu.Metadata": "Index Metadata", 52 | "IndexCommand.TextToAnalyze": "Analyse するテキストを入力", 53 | "IndexCommand.ShutdownMessage": " {1} をシャットダウンするために ''{0}'' と入力して下さい. このインターフェースからはリスタートはできません.", 54 | "IndexOverview.PageTitle": "インデックスのOverview", 55 | // "IndexSelector.NameWithDocs": "{0} ({1} docs)", 56 | "IndexSelector.SearchIndexForDocs": "Search {0} for documents where:", 57 | "FilterBrowser.OutputType": "結果の出力形式: {0} ", 58 | "FilterBrowser.OutputSize": "結果の取得サイズ: {0} ", 59 | "Header.ClusterHealth": "cluster health: {0} ({1} of {2})", 60 | "Header.ClusterNotConnected": "cluster health: not connected", 61 | "Header.Connect": "接続", 62 | "Nav.AnyRequest": "Any Request", 63 | "Nav.Browser": "Browser", 64 | "Nav.ClusterHealth": "Cluster Health", 65 | "Nav.ClusterState": "Cluster State", 66 | "Nav.ClusterNodes": "Nodes Info", 67 | // "Nav.Info": "Info", 68 | "Nav.NodeStats": "Nodes Stats", 69 | "Nav.Overview": "Overview", 70 | "Nav.Indices": "Indices", 71 | "Nav.Plugins": "Plugins", 72 | "Nav.Status": "Indices Stats", 73 | "Nav.Templates": "Templates", 74 | "Nav.StructuredQuery": "Structured Query", 75 | // "NodeActionsMenu.Title": "Actions", 76 | "NodeActionsMenu.Shutdown": "シャットダウン...", 77 | // "NodeInfoMenu.Title": "Info", 78 | "NodeInfoMenu.ClusterNodeInfo": "Cluster Node Info", 79 | // "NodeInfoMenu.NodeStats": "Node Stats", 80 | "NodeType.Client": "Client Node", 81 | "NodeType.Coord": "Coordinator", 82 | "NodeType.Master": "Master Node", 83 | "NodeType.Tribe": "Tribe Node", 84 | "NodeType.Worker": "Worker Node", 85 | "NodeType.Unassigned": "Unassigned", 86 | "OptimizeForm.OptimizeIndex": "Optimize {0}", 87 | "OptimizeForm.MaxSegments": "Maximum # Of Segments", 88 | "OptimizeForm.ExpungeDeletes": "Only Expunge Deletes", 89 | "OptimizeForm.FlushAfter": "Flush After Optimize", 90 | "OptimizeForm.WaitForMerge": "Wait For Merge", 91 | "Overview.PageTitle" : "クラスタのOverview", 92 | // "Output.JSON": "JSON", 93 | "Output.Table": "表", 94 | // "Output.CSV": "CSV", 95 | "Output.ShowSource": "Query の source を表示", 96 | "Preference.SortCluster": "ノードのソート", 97 | "Sort.ByName": "名前順", 98 | "Sort.ByAddress": "アドレス順", 99 | "Sort.ByType": "タイプ順", 100 | "Preference.SortIndices": "インデックスのソート", 101 | "SortIndices.Descending": "降順(desc)", 102 | "SortIndices.Ascending": "昇順(asc)", 103 | "Preference.ViewAliases": "エイリアスの表示方法", 104 | "ViewAliases.Grouped": "標準", 105 | "ViewAliases.List": "リスト形式", 106 | "ViewAliases.None": "表示しない", 107 | "Overview.IndexFilter": "インデックスの絞り込み", 108 | "TableResults.Summary": "検索結果: {0} / {1} シャード. {2} ヒット. {3} 秒", 109 | "QueryFilter.AllIndices": "全インデックス", 110 | "QueryFilter.AnyValue": "any", 111 | "QueryFilter-Header-Indices": "Indices", 112 | // "QueryFilter-Header-Types": "Types", 113 | "QueryFilter-Header-Fields": "Fields", 114 | "QueryFilter.DateRangeHint.from": "From : {0}", 115 | "QueryFilter.DateRangeHint.to": " To : {0}", 116 | "Query.FailAndUndo": "Query Failed. Undoing last changes", 117 | "StructuredQuery.ShowRawJson": "生の JSON を表示する" 118 | }); 119 | 120 | i18n.setKeys({ 121 | "AnyRequest.TransformerHelp" : "\ 122 |

Result Transformer は、ESから返ってきた JSON をより使いやすい形式に変換することができます.

\ 123 |

transformer には javascript の function の中身を記述します. 戻り値の新しい JSON が画面出力されます.

\ 124 |

例:
\ 125 | return root.hits.hits[0]; 検索結果の最初のドキュメントだけを表示するように JSON をトラバースする例
\ 126 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); クラスタ全体でのトータル使用メモリを返す例

\ 127 |

以下の関数は、配列やオブジェクトを扱うのに便利です
\ 128 |

    \ 129 |
  • Object.keys(object) := array
  • \ 130 |
  • array.forEach(function(prop, index))
  • \ 131 |
  • array.map(function(prop, index)) := array
  • \ 132 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 133 |
\ 134 |

繰り返しリクエストを投げた時, prev 引数に前の戻り値が入ります. 比較や、累積グラフの作成などに利用できます.

\ 135 |

例:
\ 136 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; 最初のノードの load_average を配列で返します.\ 137 | この配列をグラフの入力値に用いることで load_average の遷移をグラフとして視覚化することができます.\ 138 | " 139 | }); 140 | 141 | i18n.setKeys({ 142 | "AnyRequest.DisplayOptionsHelp" : "\ 143 |

生の JSON の表示: 検索結果を生の JSON で表示します

\ 144 |

グラフの表示: 検索結果をグラフで表示します. Result Transformer を使って、配列の値に変換する必要があります

\ 145 |

表の表示: 検索クエリの場合には、結果を表形式で表示できます

\ 146 | " 147 | }); 148 | 149 | //i18n.setKeys({ 150 | // "QueryFilter.DateRangeHelp" : "\ 151 | //

Date fields accept a natural language query to produce a From and To date that form a range that the results are queried over.

\ 152 | //

The following formats are supported:

\ 153 | //
    \ 154 | //
  • Keywords / Key Phrases
    \ 155 | // now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 156 | // searches for dates matching the keyword. last year would search all of last year.
  • \ 157 | //
  • Ranges
    \ 158 | // 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (spaces optional, many synonyms for range qualifiers)
    \ 159 | // Create a search range centered on now extending into the past and future by the amount specified.
  • \ 160 | //
  • DateTime and Partial DateTime
    \ 161 | // 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 162 | // these formats specify a specific date range. 2011 would search the whole of 2011, while 2011-01-18 12:32:45 would only search for results in that 1 second range
  • \ 163 | //
  • Time and Time Partials
    \ 164 | // 12
    12:32
    12:32:45

    \ 165 | // these formats search for a particular time during the current day. 12:32 would search that minute during today
  • \ 166 | //
  • Date Ranges
    \ 167 | // 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 168 | // A Date Range is created by specifying two dates in any format (Keyword / DateTime / Time) separated by < or -> (both do the same thing). If either end of the date range is missing, it is the same as having no constraint in that direction.
  • \ 169 | //
  • Date Range using Offset
    \ 170 | // 2010 -> 1yr
    3mins < now
    \ 171 | // Searches the specified date including the range in the direction specified.
  • \ 172 | //
  • Anchored Ranges
    \ 173 | // 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 174 | // Similar to above except the range is extend in both directions from the anchor date
  • \ 175 | //
\ 176 | // " 177 | //}); 178 | -------------------------------------------------------------------------------- /head/lang/en_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs": "Loading Aggregations...", 4 | "General.Searching": "Searching...", 5 | "General.Search": "Search", 6 | "General.Help": "Help", 7 | "General.HelpGlyph": "?", 8 | "General.CloseGlyph": "X", 9 | "General.RefreshResults": "Refresh", 10 | "General.ManualRefresh": "Manual Refresh", 11 | "General.RefreshQuickly": "Refresh quickly", 12 | "General.Refresh5seconds": "Refresh every 5 seconds", 13 | "General.Refresh1minute": "Refresh every minute", 14 | "AliasForm.AliasName": "Alias Name", 15 | "AliasForm.NewAliasForIndex": "New Alias for {0}", 16 | "AliasForm.DeleteAliasMessage": "type ''{0}'' to delete {1}. There is no undo", 17 | "AnyRequest.DisplayOptions" : "Display Options", 18 | "AnyRequest.AsGraph" : "Graph Results", 19 | "AnyRequest.AsJson" : "Show Raw JSON", 20 | "AnyRequest.AsTable" : "Show Search Results Table", 21 | "AnyRequest.History" : "History", 22 | "AnyRequest.RepeatRequest" : "Repeat Request", 23 | "AnyRequest.RepeatRequestSelect" : "Repeat request every ", 24 | "AnyRequest.Transformer" : "Result Transformer", 25 | "AnyRequest.Pretty": "Pretty", 26 | "AnyRequest.Query" : "Query", 27 | "AnyRequest.Request": "Request", 28 | "AnyRequest.Requesting": "Requesting...", 29 | "AnyRequest.ValidateJSON": "Validate JSON", 30 | "Browser.Title": "Browser", 31 | "Browser.ResultSourcePanelTitle": "Result Source", 32 | "Command.DELETE": "DELETE", 33 | "Command.SHUTDOWN": "SHUTDOWN", 34 | "Command.DeleteAliasMessage": "Delete Alias?", 35 | "ClusterOverView.IndexName": "Index Name", 36 | "ClusterOverview.NumShards": "Number of Shards", 37 | "ClusterOverview.NumReplicas": "Number of Replicas", 38 | "ClusterOverview.NewIndex": "New Index", 39 | "IndexActionsMenu.Title": "Actions", 40 | "IndexActionsMenu.NewAlias": "New Alias...", 41 | "IndexActionsMenu.Refresh": "Refresh", 42 | "IndexActionsMenu.Flush": "Flush", 43 | "IndexActionsMenu.Optimize": "Optimize...", 44 | "IndexActionsMenu.Snapshot": "Gateway Snapshot", 45 | "IndexActionsMenu.Analyser": "Test Analyser", 46 | "IndexActionsMenu.Open": "Open", 47 | "IndexActionsMenu.Close": "Close", 48 | "IndexActionsMenu.Delete": "Delete...", 49 | "IndexInfoMenu.Title": "Info", 50 | "IndexInfoMenu.Status": "Index Status", 51 | "IndexInfoMenu.Metadata": "Index Metadata", 52 | "IndexCommand.TextToAnalyze": "Text to Analyse", 53 | "IndexCommand.ShutdownMessage": "type ''{0}'' to shutdown {1}. Node can NOT be restarted from this interface", 54 | "IndexOverview.PageTitle": "Indices Overview", 55 | "IndexSelector.NameWithDocs": "{0} ({1} docs)", 56 | "IndexSelector.SearchIndexForDocs": "Search {0} for documents where:", 57 | "FilterBrowser.OutputType": "Output Results: {0}", 58 | "FilterBrowser.OutputSize": "Number of Results: {0}", 59 | "Header.ClusterHealth": "cluster health: {0} ({1} of {2})", 60 | "Header.ClusterNotConnected": "cluster health: not connected", 61 | "Header.Connect": "Connect", 62 | "Nav.AnyRequest": "Any Request", 63 | "Nav.Browser": "Browser", 64 | "Nav.ClusterHealth": "Cluster Health", 65 | "Nav.ClusterState": "Cluster State", 66 | "Nav.ClusterNodes": "Nodes Info", 67 | "Nav.Info": "Info", 68 | "Nav.NodeStats": "Nodes Stats", 69 | "Nav.Overview": "Overview", 70 | "Nav.Indices": "Indices", 71 | "Nav.Plugins": "Plugins", 72 | "Nav.Status": "Indices Stats", 73 | "Nav.Templates": "Templates", 74 | "Nav.StructuredQuery": "Structured Query", 75 | "NodeActionsMenu.Title": "Actions", 76 | "NodeActionsMenu.Shutdown": "Shutdown...", 77 | "NodeInfoMenu.Title": "Info", 78 | "NodeInfoMenu.ClusterNodeInfo": "Cluster Node Info", 79 | "NodeInfoMenu.NodeStats": "Node Stats", 80 | "NodeType.Client": "Client Node", 81 | "NodeType.Coord": "Coordinator", 82 | "NodeType.Master": "Master Node", 83 | "NodeType.Tribe": "Tribe Node", 84 | "NodeType.Worker": "Worker Node", 85 | "NodeType.Unassigned": "Unassigned", 86 | "OptimizeForm.OptimizeIndex": "Optimize {0}", 87 | "OptimizeForm.MaxSegments": "Maximum # Of Segments", 88 | "OptimizeForm.ExpungeDeletes": "Only Expunge Deletes", 89 | "OptimizeForm.FlushAfter": "Flush After Optimize", 90 | "OptimizeForm.WaitForMerge": "Wait For Merge", 91 | "Overview.PageTitle" : "Cluster Overview", 92 | "Output.JSON": "JSON", 93 | "Output.Table": "Table", 94 | "Output.CSV": "CSV", 95 | "Output.ShowSource": "Show query source", 96 | "Preference.SortCluster": "Sort Cluster", 97 | "Sort.ByName": "By Name", 98 | "Sort.ByAddress": "By Address", 99 | "Sort.ByType": "By Type", 100 | "Preference.SortIndices": "Sort Indices", 101 | "SortIndices.Descending": "Descending", 102 | "SortIndices.Ascending": "Ascending", 103 | "Preference.ViewAliases": "View Aliases", 104 | "ViewAliases.Grouped": "Grouped", 105 | "ViewAliases.List": "List", 106 | "ViewAliases.None": "None", 107 | "Overview.IndexFilter": "Index Filter", 108 | "TableResults.Summary": "Searched {0} of {1} shards. {2} hits. {3} seconds", 109 | "QueryFilter.AllIndices": "All Indices", 110 | "QueryFilter.AnyValue": "any", 111 | "QueryFilter-Header-Indices": "Indices", 112 | "QueryFilter-Header-Types": "Types", 113 | "QueryFilter-Header-Fields": "Fields", 114 | "QueryFilter.DateRangeHint.from": "From : {0}", 115 | "QueryFilter.DateRangeHint.to": " To : {0}", 116 | "Query.FailAndUndo": "Query Failed. Undoing last changes", 117 | "StructuredQuery.ShowRawJson": "Show Raw JSON" 118 | }); 119 | 120 | i18n.setKeys({ 121 | "AnyRequest.TransformerHelp" : "\ 122 |

The Result Transformer can be used to post process the raw json results from a request into a more useful format.

\ 123 |

The transformer should contain the body of a javascript function. The return value from the function becomes the new value passed to the json printer

\ 124 |

Example:
\ 125 | return root.hits.hits[0]; would traverse a result set to show just the first match
\ 126 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); would return the total memory used across an entire cluster

\ 127 |

The following functions are available and can be useful processing arrays and objects
\ 128 |

    \ 129 |
  • Object.keys(object) := array
  • \ 130 |
  • array.forEach(function(prop, index))
  • \ 131 |
  • array.map(function(prop, index)) := array
  • \ 132 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 133 |
\ 134 |

When Repeat Request is running, an extra parameter called prev is passed to the transformation function. This allows comparisons, and cumulative graphing

\ 135 |

Example:
\ 136 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; would return the load average on the first cluster node over the last minute\ 137 | This could be fed into the Graph to produce a load graph for the node\ 138 | " 139 | }); 140 | 141 | i18n.setKeys({ 142 | "AnyRequest.DisplayOptionsHelp" : "\ 143 |

Raw Json: shows complete results of the query and transformation in raw JSON format

\ 144 |

Graph Results: To produce a graph of your results, use the result transformer to produce an array of values

\ 145 |

Search Results Table: If your query is a search, you can display the results of the search in a table.

\ 146 | " 147 | }); 148 | 149 | i18n.setKeys({ 150 | "QueryFilter.DateRangeHelp" : "\ 151 |

Date fields accept a natural language query to produce a From and To date that form a range that the results are queried over.

\ 152 |

The following formats are supported:

\ 153 |
    \ 154 |
  • Keywords / Key Phrases
    \ 155 | now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 156 | searches for dates matching the keyword. last year would search all of last year.
  • \ 157 |
  • Ranges
    \ 158 | 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (spaces optional, many synonyms for range qualifiers)
    \ 159 | Create a search range centered on now extending into the past and future by the amount specified.
  • \ 160 |
  • DateTime and Partial DateTime
    \ 161 | 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 162 | these formats specify a specific date range. 2011 would search the whole of 2011, while 2011-01-18 12:32:45 would only search for results in that 1 second range
  • \ 163 |
  • Time and Time Partials
    \ 164 | 12
    12:32
    12:32:45

    \ 165 | these formats search for a particular time during the current day. 12:32 would search that minute during today
  • \ 166 |
  • Date Ranges
    \ 167 | 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 168 | A Date Range is created by specifying two dates in any format (Keyword / DateTime / Time) separated by < or -> (both do the same thing). If either end of the date range is missing, it is the same as having no constraint in that direction.
  • \ 169 |
  • Date Range using Offset
    \ 170 | 2010 -> 1yr
    3mins < now
    \ 171 | Searches the specified date including the range in the direction specified.
  • \ 172 |
  • Anchored Ranges
    \ 173 | 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 174 | Similar to above except the range is extend in both directions from the anchor date
  • \ 175 |
\ 176 | " 177 | }); 178 | -------------------------------------------------------------------------------- /head/lang/fr_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | // "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs" : "Chargement des facettes...", 4 | "General.Searching": "Recherche en cours...", 5 | "General.Search": "Recherche", 6 | "General.Help": "Aide", 7 | // "General.HelpGlyph": "?", 8 | // "General.CloseGlyph": "X", 9 | "General.RefreshResults": "Rafraîchir", 10 | "General.ManualRefresh": "Rafraîchissement manuel", 11 | "General.RefreshQuickly": "Rafraîchissement rapide", 12 | "General.Refresh5seconds": "Rafraîchissement toutes les 5 secondes", 13 | "General.Refresh1minute": "Rafraîchissement toutes les minutes", 14 | "AliasForm.AliasName": "Alias", 15 | "AliasForm.NewAliasForIndex": "Nouvel Alias pour {0}", 16 | "AliasForm.DeleteAliasMessage": "Entrez ''{0}'' pour effacer {1}. Attention, action irréversible.", 17 | "AnyRequest.DisplayOptions" : "Options d'affichage", 18 | "AnyRequest.AsGraph" : "En graphe", 19 | "AnyRequest.AsJson" : "En JSON brut", 20 | "AnyRequest.AsTable" : "En tableau", 21 | "AnyRequest.History" : "Historique", 22 | "AnyRequest.RepeatRequest" : "Répétition automatique de la requête", 23 | "AnyRequest.RepeatRequestSelect" : "Répéter la requête toutes les ", 24 | "AnyRequest.Transformer" : "Transformation des résultats", 25 | // "AnyRequest.Pretty": "Pretty", 26 | "AnyRequest.Query" : "Recherche", 27 | "AnyRequest.Request": "Requête", 28 | "AnyRequest.Requesting": "Requête en cours...", 29 | "AnyRequest.ValidateJSON": "Valider le JSON", 30 | "Browser.Title": "Navigateur", 31 | "Browser.ResultSourcePanelTitle": "Résultat au format JSON", 32 | "Command.DELETE": "SUPPRIMER", 33 | "Command.SHUTDOWN": "ETEINDRE", 34 | "Command.DeleteAliasMessage": "Supprimer l'Alias?", 35 | "ClusterOverView.IndexName": "Index", 36 | "ClusterOverview.NumShards": "Nombre de shards", 37 | "ClusterOverview.NumReplicas": "Nombre de replica", 38 | "ClusterOverview.NewIndex": "Nouvel Index", 39 | // "IndexActionsMenu.Title": "Actions", 40 | "IndexActionsMenu.NewAlias": "Nouvel Alias...", 41 | "IndexActionsMenu.Refresh": "Rafraîchir", 42 | "IndexActionsMenu.Flush": "Flusher", 43 | "IndexActionsMenu.Optimize": "Optimiser...", 44 | "IndexActionsMenu.Snapshot": "Dupliquer l'index (Snapshot)", 45 | "IndexActionsMenu.Analyser": "Tester un analyseur", 46 | "IndexActionsMenu.Open": "Ouvrir", 47 | "IndexActionsMenu.Close": "Fermer", 48 | "IndexActionsMenu.Delete": "Effacer...", 49 | // "IndexInfoMenu.Title": "Info", 50 | "IndexInfoMenu.Status": "Etat de l'Index", 51 | "IndexInfoMenu.Metadata": "Métadonnées de l'Index", 52 | "IndexCommand.TextToAnalyze": "Texte à analyser", 53 | "IndexCommand.ShutdownMessage": "Entrez ''{0}'' pour éteindre {1}. Le noeud NE PEUT PAS être redémarré depuis cette interface.", 54 | // "IndexSelector.NameWithDocs": "{0} ({1} docs)", 55 | "IndexSelector.SearchIndexForDocs": "Chercher dans {0} les documents correspondant à", 56 | "FilterBrowser.OutputType": "Format d'affichage des résultats {0}", 57 | "FilterBrowser.OutputSize": "Nombre de Résultats: {0}", 58 | "Header.ClusterHealth": "Santé du cluster: {0} ({1} {2})", 59 | "Header.ClusterNotConnected": "Santé du cluster: non connecté", 60 | "Header.Connect": "Se connecter", 61 | "Nav.AnyRequest": "Autres requêtes", 62 | "Nav.StructuredQuery": "Requêtes structurées", 63 | "Nav.Browser": "Navigateur", 64 | "Nav.ClusterHealth": "Santé du cluster", 65 | "Nav.ClusterState": "Etat du cluster", 66 | "Nav.ClusterNodes": "Noeuds du cluster", 67 | // "Nav.Info": "Info", 68 | "Nav.NodeStats": "Statistiques sur les noeuds", 69 | "Nav.Overview": "Aperçu", 70 | "Nav.Indices": "Index", 71 | "Nav.Plugins": "Plugins", 72 | "Nav.Status": "Etat", 73 | "Nav.Templates": "Templates", 74 | "Nav.StructuredQuery": "Recherche Structurée", 75 | // "NodeActionsMenu.Title": "Actions", 76 | "NodeActionsMenu.Shutdown": "Eteindre...", 77 | // "NodeInfoMenu.Title": "Info", 78 | "NodeInfoMenu.ClusterNodeInfo": "Infos sur le noeud du cluster", 79 | "NodeInfoMenu.NodeStats": "Statistiques du noeud", 80 | "NodeType.Client": "Noeud Client", 81 | "NodeType.Coord": "Coordinateur", 82 | "NodeType.Master": "Noeud Master", 83 | "NodeType.Tribe": "Noeud Tribe", 84 | "NodeType.Worker": "Noeud Worker", 85 | "NodeType.Unassigned": "Non assigné", 86 | "OptimizeForm.OptimizeIndex": "Optimiser {0}", 87 | "OptimizeForm.MaxSegments": "Nombre maximum de segments", 88 | "OptimizeForm.ExpungeDeletes": "Seulement purger les suppressions", 89 | "OptimizeForm.FlushAfter": "Flusher après l'optimisation", 90 | "OptimizeForm.WaitForMerge": "Attendre la fin de la fusion", 91 | "Overview.PageTitle" : "Aperçu du cluster", 92 | // "Output.JSON": "JSON", 93 | "Output.Table": "Tableau", 94 | "Output.ShowSource": "Voir la requête source", 95 | "TableResults.Summary": "Recherche sur {0} des {1} shards. {2} résultats. {3} secondes", 96 | "QueryFilter.AllIndices": "Tous les index", 97 | "QueryFilter.AnyValue": "Tout", 98 | "QueryFilter-Header-Indices": "Index", 99 | // "QueryFilter-Header-Types": "Types", 100 | "QueryFilter-Header-Fields": "Champs", 101 | "QueryFilter.DateRangeHint.from": "De : {0}", 102 | "QueryFilter.DateRangeHint.to": " A : {0}", 103 | "Query.FailAndUndo": "Requête en échec. Annulation des dernières modifications.", 104 | "StructuredQuery.ShowRawJson": "Voir le JSON brut" 105 | }); 106 | 107 | i18n.setKeys({ 108 | "AnyRequest.TransformerHelp" : "\ 109 |

Le transformateur de résultats peut être utilisé pour modifier a posteriori les résultats JSON bruts dans un format plus utile.

\ 110 |

Le transformateur devrait contenir le corps d'une fonction javascript. La valeur de retour de la fonction devient la nouvelle valeur qui sera passée à l'afficheur des documents JSON.

\ 111 |

Exemple:
\ 112 | return root.hits.hits[0]; ne renverra que le premier élément de l'ensemble des résultats.
\ 113 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); retournera la mémoire totale utilisée dans l'ensemble du cluster.

\ 114 |

Les fonctions suivantes sont disponibles et peuvent vous être utiles pour travailler sur les tableaux et les objets:
\ 115 |

    \ 116 |
  • Object.keys(object) := array
  • \ 117 |
  • array.forEach(function(prop, index))
  • \ 118 |
  • array.map(function(prop, index)) := array
  • \ 119 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 120 |
\ 121 |

Lorsque vous activez la répétition automatique de la requête, un paramètre supplémentaire nommé prev est passé à la fonction de transformation. Cela permet les comparaisons et les graphes cumulatifs.

\ 122 |

Exemple:
\ 123 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; retournera la charge moyenne du premier noeud du cluster pour la dernière minute écoulée.\ 124 | Cela peut alimenter ensuite le graphe pour produire un graphe de charge du noeud.\ 125 | " 126 | }); 127 | 128 | i18n.setKeys({ 129 | "AnyRequest.DisplayOptionsHelp" : "\ 130 |

En JSON brut: affiche les résultats complets de la recherche éventuellement transformée au format JSON brut.

\ 131 |

En graphe: pour fabriquer un graphe de vos résultats, utilsez la transformation de résultats pour générer un tableau de valeurs.

\ 132 |

En tableau: si votre requête est une recherche, vous pouvez alors afficher les résultats dans un tableau.

\ 133 | " 134 | }); 135 | 136 | i18n.setKeys({ 137 | "QueryFilter.DateRangeHelp" : "\ 138 |

Les champs Date acceptent une requête en langage naturel pour produire un écart de date (from/to) correspondant.

\ 139 |

Les formats suivants sont acceptés :

\ 140 |
    \ 141 |
  • Mots clés
    \ 142 | now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 143 | Cherchera pour des dates correspondant au mot clé. last year cherchera sur toute l'année précédente.
  • \ 144 |
  • Ecarts
    \ 145 | 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (les espaces sont optionnels et il existe beaucoup de synonymes pour qualifier les écarts)
    \ 146 | Créé un écart de date basé sur l'heure courante (maintenant) avec plus ou moins l'écart indiqué.
  • \ 147 |
  • Dates et Dates partielles
    \ 148 | 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 149 | Ces formats indiquent un écart de date spécifique. 2011 cherchera sur toute l'année 2011, alors que 2011-01-18 12:32:45 ne cherchera que pour la date précise à la seconde près.
  • \ 150 |
  • Heures et heures partielles
    \ 151 | 12
    12:32
    12:32:45

    \ 152 | Ces formats indiquent un espace de temps pour la date du jour. 12:32 cherchera les éléments d'aujourd'hui à cette minute précise.
  • \ 153 |
  • Ecart de Date
    \ 154 | 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 155 | Un écart de date est créé en spécifiant deux dates dans n'importe lequel des formats précédents (Mot clé / Dates / Heures) séparées par < ou -> (les deux produisent le même effet). Si la date de fin n'est pas indiquée, alors il n'y aura aucune contrainte de fin.
  • \ 156 |
  • Ecart de date avec décalage
    \ 157 | 2010 -> 1yr
    3mins < now
    \ 158 | Cherche en incluant un décalage de la date dans la direction indiquée.
  • \ 159 |
  • Ecart de date avec bornes
    \ 160 | 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 161 | Similaire à ci-dessus excepté que le décalage est appliqué dans les deux sens à partir de la date indiquée.
  • \ 162 |
\ 163 | " 164 | }); 165 | -------------------------------------------------------------------------------- /head/lang/tr_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs": "Gruplar Yükleniyor...", 4 | "General.Searching": "Aranıyor...", 5 | "General.Search": "Ara", 6 | "General.Help": "Yardım", 7 | "General.HelpGlyph": "?", 8 | "General.CloseGlyph": "X", 9 | "General.RefreshResults": "Yenile", 10 | "General.ManualRefresh": "Manuel Yenileme", 11 | "General.RefreshQuickly": "Hızlı yenile", 12 | "General.Refresh5seconds": "5 saniyede bir yenile", 13 | "General.Refresh1minute": "Her dakika yenile", 14 | "AliasForm.AliasName": "Alternatif İsim", 15 | "AliasForm.NewAliasForIndex": "{0} için yeni alternatif isim", 16 | "AliasForm.DeleteAliasMessage": "{1} silmek için ''{0}'' . Geriye dönüş yoktur.", 17 | "AnyRequest.DisplayOptions" : "Seçenekleri Göster", 18 | "AnyRequest.AsGraph" : "Sonuçları Çizdir", 19 | "AnyRequest.AsJson" : "JSON formatında göster", 20 | "AnyRequest.AsTable" : "Arama sonuçlarını tablo halinde göster", 21 | "AnyRequest.History" : "Geçmiş", 22 | "AnyRequest.RepeatRequest" : "İsteği Tekrarla", 23 | "AnyRequest.RepeatRequestSelect" : "İsteği sürekli tekrarla ", 24 | "AnyRequest.Transformer" : "Sonuç Dönüştürücü", 25 | "AnyRequest.Pretty": "Düzenli", 26 | "AnyRequest.Query" : "Sorgu", 27 | "AnyRequest.Request": "Gönder", 28 | "AnyRequest.Requesting": "İsteniyor...", 29 | "AnyRequest.ValidateJSON": "JSON Doğrula", 30 | "Browser.Title": "Browser", 31 | "Browser.ResultSourcePanelTitle": "Sonuç Kaynağı", 32 | "Command.DELETE": "SİL", 33 | "Command.SHUTDOWN": "KAPA", 34 | "Command.DeleteAliasMessage": "Alternatif ismi sil?", 35 | "ClusterOverView.IndexName": "Indeks İsmi", 36 | "ClusterOverview.NumShards": "Sektör Sayısı", 37 | "ClusterOverview.NumReplicas": "Yedek Sayısı", 38 | "ClusterOverview.NewIndex": "Yeni Indeks", 39 | "IndexActionsMenu.Title": "İşlemler", 40 | "IndexActionsMenu.NewAlias": "Yeni Alternatif İsim...", 41 | "IndexActionsMenu.Refresh": "Yenile", 42 | "IndexActionsMenu.Flush": "Boşalt", 43 | "IndexActionsMenu.Optimize": "Optimize et...", 44 | "IndexActionsMenu.Snapshot": "Gateway Snapshot (Kopya Al)", 45 | "IndexActionsMenu.Analyser": "Analizi test et", 46 | "IndexActionsMenu.Open": "Aç", 47 | "IndexActionsMenu.Close": "Kapa", 48 | "IndexActionsMenu.Delete": "Sil...", 49 | "IndexInfoMenu.Title": "Bilgi", 50 | "IndexInfoMenu.Status": "Indeks Durumu", 51 | "IndexInfoMenu.Metadata": "Indeks Metaveri", 52 | "IndexCommand.TextToAnalyze": "Analiz edilecek metin", 53 | "IndexCommand.ShutdownMessage": "{1} kapatmak için ''{0}'' yazın . Nod bu arayüzden tekrar BAŞLATILAMAZ", 54 | "IndexOverview.PageTitle": "Indeksler Genel Bakış", 55 | "IndexSelector.NameWithDocs": "{0} ({1} döküman)", 56 | "IndexSelector.SearchIndexForDocs": "{0} indeksinde ara:", 57 | "FilterBrowser.OutputType": "Sonuç Formatı: {0}", 58 | "FilterBrowser.OutputSize": "Sonuç Sayısı: {0}", 59 | "Header.ClusterHealth": "Küme Durumu: {0} ({1} de {2})", 60 | "Header.ClusterNotConnected": "Küme Durumu: Bağlı Değil", 61 | "Header.Connect": "Bağlan", 62 | "Nav.AnyRequest": "Özel Sorgu", 63 | "Nav.Browser": "Görüntüle", 64 | "Nav.ClusterHealth": "Küme Durumu", 65 | "Nav.ClusterState": "Küme Statüsü", 66 | "Nav.ClusterNodes": "Nod Bilgileri", 67 | "Nav.Info": "Bilgi", 68 | "Nav.NodeStats": "Nod İstatistikleri", 69 | "Nav.Overview": "Genel Bakış", 70 | "Nav.Indices": "Indeksler", 71 | "Nav.Plugins": "Eklentiler", 72 | "Nav.Status": "Indeks İstatistikleri", 73 | "Nav.Templates": "Şablonlar", 74 | "Nav.StructuredQuery": "Yapılandırılmış Sorgu", 75 | "NodeActionsMenu.Title": "İşlemler", 76 | "NodeActionsMenu.Shutdown": "Kapat...", 77 | "NodeInfoMenu.Title": "Bilgi", 78 | "NodeInfoMenu.ClusterNodeInfo": "Küme Nod Bilgileri", 79 | "NodeInfoMenu.NodeStats": "Nod İstatistikleri", 80 | "NodeType.Client": "Client Nod", 81 | "NodeType.Coord": "Coordinator", 82 | "NodeType.Master": "Master Nod", 83 | "NodeType.Tribe": "Tribe Nod", 84 | "NodeType.Worker": "Worker Nod", 85 | "NodeType.Unassigned": "Sahipsiz", 86 | "OptimizeForm.OptimizeIndex": "{0} Optimize Et", 87 | "OptimizeForm.MaxSegments": "Maksimum Segment Sayısı", 88 | "OptimizeForm.ExpungeDeletes": "Silme İşlemi Artıklarını Temizle", 89 | "OptimizeForm.FlushAfter": "Optimize Ettikten Sonra Boşalt", 90 | "OptimizeForm.WaitForMerge": "Birleştirme İçin Bekle", 91 | "Overview.PageTitle" : "Kümeler Genelbakış", 92 | "Output.JSON": "JSON", 93 | "Output.Table": "Tablo", 94 | "Output.CSV": "CSV", 95 | "Output.ShowSource": "Sorgu kaynağını göster", 96 | "Preference.SortCluster": "Kümeyi Sırala", 97 | "Sort.ByName": "İsme göre", 98 | "Sort.ByAddress": "Adrese göre", 99 | "Sort.ByType": "Tipe göre", 100 | "Preference.SortIndices": "Indeksleri sırala", 101 | "SortIndices.Descending": "Azalan", 102 | "SortIndices.Ascending": "Artan", 103 | "Preference.ViewAliases": "Alternatif isimleri görüntüle", 104 | "ViewAliases.Grouped": "Gruplanmış", 105 | "ViewAliases.List": "Liste", 106 | "ViewAliases.None": "Karışık", 107 | "Overview.IndexFilter": "Indeks Filtresi", 108 | "TableResults.Summary": "{0} parçanın {1} tanesi arandı. {2} sonuç. {3} saniye", 109 | "QueryFilter.AllIndices": "Tüm Indeksler", 110 | "QueryFilter.AnyValue": "herhangi", 111 | "QueryFilter-Header-Indices": "Indeksler", 112 | "QueryFilter-Header-Types": "Tipler", 113 | "QueryFilter-Header-Fields": "Alanlar", 114 | "QueryFilter.DateRangeHint.from": "{0}'dan", 115 | "QueryFilter.DateRangeHint.to": " {0}'a", 116 | "Query.FailAndUndo": "Sorgu Başarısız. Son değişiklikler geri alınıyor.", 117 | "StructuredQuery.ShowRawJson": "Formatsız JSON göster" 118 | }); 119 | 120 | i18n.setKeys({ 121 | "AnyRequest.TransformerHelp" : "\ 122 |

Sonuç Dönüştürücü sorgudan dönen JSON sonuçlarını işleyip daha kullanışlı bir formata dönüştürmek için kullanılabilir.

\ 123 |

Dönüştürücü içierisinde javascript fonksiyonu tanımlanmalıdır. Bu fonksiyondan dönen yeni sonuç çıktı kısmına yazdırılır.

\ 124 |

Örnek:
\ 125 | return root.hits.hits[0]; sonucu dolaşarak ilk eşleşmeyi göster
\ 126 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); tüm kümede kullanılan toplam belleği gösterir

\ 127 |

Aşağıdaki fonksiyonlar dizi ve objelerin işlenmesinde yardımcı olması için kullanılabilir
\ 128 |

    \ 129 |
  • Object.keys(object) := array
  • \ 130 |
  • array.forEach(function(prop, index))
  • \ 131 |
  • array.map(function(prop, index)) := array
  • \ 132 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 133 |
\ 134 |

Sorgu tekrarlama çalışırken, prev isimli ekstra bir parametre dönüştürücü fonksiyonuna verilir. Bu sayede karşılaştırmalar ve toplu grafik gösterimleri yapılabilir.

\ 135 |

Örnek:
\ 136 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; önceki dakika boyunca kümede bulunan ilk nod üzerindeki averaj yükü verir.\ 137 | Bu sonuç nod için yük grafiği yaratılmasında kullanılabilir.\ 138 | " 139 | }); 140 | 141 | i18n.setKeys({ 142 | "AnyRequest.DisplayOptionsHelp" : "\ 143 |

Sade Json: Sorgunun tüm sonuçlarını ve (yapıldıysa) dönüştürüldükten sonraki halini sade JSON formatında gösterir

\ 144 |

Sonuçları Çizdir: Sonuçları grafiksel olarak görüntülemek için sonuç dörücüyü kullanarak değerleri dizi haline getirin.

\ 145 |

Arama Sonuçları Tablosu: Eğer sorgunuz bir arama ise, sonuçları bir tabloda görüntüleyebilirsiniz.

\ 146 | " 147 | }); 148 | 149 | i18n.setKeys({ 150 | "QueryFilter.DateRangeHelp" : "\ 151 |

Tarih alanları ana dile yakın kelimeler kullanarak iki tarih aralığında sorgu yapılabilmesini sağlar.

\ 152 |

Aşağıdaki tanımlar kullanılabilir:

\ 153 |
    \ 154 |
  • Anahtar Kelimeler
    \ 155 | now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 156 | kelimeleri eşleşen tarihleri verir. Örneğin last year geçen yıl tarihli bütün verileri döndürür.
  • \ 157 |
  • Aralıklar
    \ 158 | 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (boşluklar isteğe bağlıdır, ayni kelime için farklı yazım şekilleri kullanılabilir)
    \ 159 | Şu anki tarihi (now) baz alarak geçmiş veya ileriki bir tarih aralığındaki kayıtları verir.
  • \ 160 |
  • Tarih ve Kısmi Tarihler
    \ 161 | 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 162 | bu formatlar spesifik bir tarihi tanımlarlar. 2011 tüm 2011 yılını ararken, 2011-01-18 12:32:45 şeklinde bir sorgu sadece o saniyedeki sonuçları verir.
  • \ 163 |
  • Zaman ve Kısmi Zamanlar
    \ 164 | 12
    12:32
    12:32:45

    \ 165 | bu formatlar gün içerisinde spesifik bir zamanı tanımlarlar. Örneğin 12:32 sadece bu saat ve dakikadaki kayıtları verir.
  • \ 166 |
  • Tarih Aralıkları
    \ 167 | 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 168 | Tarih aralıkları yukarda belirtilen herhangi bir formatı < or -> ile ayırarak yapılabilir. Eğer aralığın bir tarafı eksikse, sorgu ucu açıkmış gibi davranır.
  • \ 169 |
  • Ofsetli Tarih Aralığı
    \ 170 | 2010 -> 1yr
    3mins < now
    \ 171 | Verilen yöndeki tarih aralığına bakar.
  • \ 172 |
  • Çakılı Aralıklar
    \ 173 | 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 174 | Yukarıdakiyle ayni fakat belirtilen tarihten her iki yöne de bakılır.
  • \ 175 |
\ 176 | " 177 | }); 178 | -------------------------------------------------------------------------------- /head/lang/pt_strings.js: -------------------------------------------------------------------------------- 1 | i18n.setKeys({ 2 | "General.Elasticsearch": "Elasticsearch", 3 | "General.LoadingAggs": "Carregando Facetas...", 4 | "General.Searching": "Buscando...", 5 | "General.Search": "Busca", 6 | "General.Help": "Ajuda", 7 | "General.HelpGlyph": "?", 8 | "General.CloseGlyph": "X", 9 | "General.RefreshResults": "Atualizar", 10 | "General.ManualRefresh": "Atualização Manual", 11 | "General.RefreshQuickly": "Atualização rápida", 12 | "General.Refresh5seconds": "Atualização a cada 5 segundos", 13 | "General.Refresh1minute": "Atualização a cada minuto", 14 | "AliasForm.AliasName": "Apelido", 15 | "AliasForm.NewAliasForIndex": "Novo apelido para {0}", 16 | "AliasForm.DeleteAliasMessage": "digite ''{0}'' para deletar {1}. Não há como voltar atrás", 17 | "AnyRequest.DisplayOptions" : "Mostrar Opções", 18 | "AnyRequest.AsGraph" : "Mostrar como gráfico", 19 | "AnyRequest.AsJson" : "Mostrar JSON bruto", 20 | "AnyRequest.AsTable" : "Mostrar tabela de resultados da consulta", 21 | "AnyRequest.History" : "Histórico", 22 | "AnyRequest.RepeatRequest" : "Refazer requisição", 23 | "AnyRequest.RepeatRequestSelect" : "Repetir requisição a cada ", 24 | "AnyRequest.Transformer" : "Transformador de resultado", 25 | "AnyRequest.Pretty": "Amigável", 26 | "AnyRequest.Query" : "Consulta", 27 | "AnyRequest.Request": "Requisição", 28 | "AnyRequest.Requesting": "Realizando requisição...", 29 | "AnyRequest.ValidateJSON": "Validar JSON", 30 | "Browser.Title": "Navegador", 31 | "Browser.ResultSourcePanelTitle": "Resultado", 32 | "Command.DELETE": "DELETAR", 33 | "Command.SHUTDOWN": "DESLIGAR", 34 | "Command.DeleteAliasMessage": "Remover apelido?", 35 | "ClusterOverView.IndexName": "Nome do índice", 36 | "ClusterOverview.NumShards": "Número de Shards", 37 | "ClusterOverview.NumReplicas": "Número de Réplicas", 38 | "ClusterOverview.NewIndex": "Novo índice", 39 | "IndexActionsMenu.Title": "Ações", 40 | "IndexActionsMenu.NewAlias": "Novo apelido...", 41 | "IndexActionsMenu.Refresh": "Atualizar", 42 | "IndexActionsMenu.Flush": "Flush", 43 | "IndexActionsMenu.Optimize": "Otimizar...", 44 | "IndexActionsMenu.Snapshot": "Snapshot do Gateway", 45 | "IndexActionsMenu.Analyser": "Analizador de teste", 46 | "IndexActionsMenu.Open": "Abrir", 47 | "IndexActionsMenu.Close": "Fechar", 48 | "IndexActionsMenu.Delete": "Deletar...", 49 | "IndexInfoMenu.Title": "Info", 50 | "IndexInfoMenu.Status": "Status do índice", 51 | "IndexInfoMenu.Metadata": "Metadados do índice", 52 | "IndexCommand.TextToAnalyze": "Texto para analizar", 53 | "IndexCommand.ShutdownMessage": "digite ''{0}'' para desligar {1}. Nó NÃO PODE ser reiniciado à partir dessa interface", 54 | "IndexOverview.PageTitle": "Visão geral dos índices", 55 | "IndexSelector.NameWithDocs": "{0} ({1} documentoss)", 56 | "IndexSelector.SearchIndexForDocs": "Busca {0} por documentos onde:", 57 | "FilterBrowser.OutputType": "Resultados: {0}", 58 | "FilterBrowser.OutputSize": "Número de Resultados: {0}", 59 | "Header.ClusterHealth": "saúde do cluster: {0} ({1} {2})", 60 | "Header.ClusterNotConnected": "saúde do cluster: não conectado", 61 | "Header.Connect": "Conectar", 62 | "Nav.AnyRequest": "Qualquer requisição", 63 | "Nav.Browser": "Navegador", 64 | "Nav.ClusterHealth": "Saúde do Cluster", 65 | "Nav.ClusterState": "Estado do Cluster", 66 | "Nav.ClusterNodes": "Nós do Cluster", 67 | "Nav.Info": "Informações", 68 | "Nav.NodeStats": "Estatísticas do nó", 69 | "Nav.Overview": "Visão Geral", 70 | "Nav.Indices": "Índices", 71 | "Nav.Plugins": "Plugins", 72 | "Nav.Status": "Status", 73 | "Nav.Templates": "Modelos", 74 | "Nav.StructuredQuery": "Consulta Estruturada", 75 | "NodeActionsMenu.Title": "Ações", 76 | "NodeActionsMenu.Shutdown": "Desligar...", 77 | "NodeInfoMenu.Title": "Informações", 78 | "NodeInfoMenu.ClusterNodeInfo": "Informações do Nó do Cluster", 79 | "NodeInfoMenu.NodeStats": "Estatísticas do Nó", 80 | "NodeType.Client": "Nó cliente", 81 | "NodeType.Coord": "Coordenador", 82 | "NodeType.Master": "Nó mestre", 83 | "NodeType.Tribe": "Nó tribo", 84 | "NodeType.Worker": "Nó trabalhador", 85 | "NodeType.Unassigned": "Não atribuido", 86 | "OptimizeForm.OptimizeIndex": "Otimizar {0}", 87 | "OptimizeForm.MaxSegments": "# Máximo De Segmentos", 88 | "OptimizeForm.ExpungeDeletes": "Apenas Expurgar Exclusões", 89 | "OptimizeForm.FlushAfter": "Flush após Otimizar", 90 | "OptimizeForm.WaitForMerge": "Esperar Por Merge", 91 | "Overview.PageTitle": "Visão geral do Cluster", 92 | "Output.JSON": "JSON", 93 | "Output.Table": "Tabela", 94 | "Output.CSV": "CSV", 95 | "Output.ShowSource": "Mostrar consulta original", 96 | "Preference.SortCluster": "Ordenar Cluster", 97 | "Sort.ByName": "Por nome", 98 | "Sort.ByAddress": "Por endereço", 99 | "Sort.ByType": "Por tipo", 100 | "Preference.ViewAliases": "Ver Alias", 101 | "ViewAliases.Grouped": "Agrupado", 102 | "ViewAliases.List": "Lista", 103 | "ViewAliases.None": "Nenhum", 104 | "Overview.IndexFilter": "Filtar Índice", 105 | "TableResults.Summary": "Buscado {0} de {1} shards. {2} resultados. {3} segundos", 106 | "QueryFilter.AllIndices": "Todos os Índices", 107 | "QueryFilter.AnyValue": "qualquer", 108 | "QueryFilter-Header-Indices": "Índices", 109 | "QueryFilter-Header-Types": "Tipos", 110 | "QueryFilter-Header-Fields": "Campos", 111 | "QueryFilter.DateRangeHint.from": "De : {0}", 112 | "QueryFilter.DateRangeHint.to": " A : {0}", 113 | "Query.FailAndUndo": "Consulta falhou. Desfazendo últimas alterações", 114 | "StructuredQuery.ShowRawJson": "Mostrar JSON bruto" 115 | }); 116 | 117 | i18n.setKeys({ 118 | "AnyRequest.TransformerHelp" : "\ 119 |

O Transformador de Resultados pode ser usado para transformar os resultados de uma consulta de json bruto para um formato mais útil.

\ 120 |

O transformador deve possuir o corpo de uma função javascript. O retorno da função se torna o novo valor passado para o json printer

\ 121 |

Exemplo:
\ 122 | return root.hits.hits[0]; irá alterar a resposta para mostrar apenas o primeiro resultado
\ 123 | return Object.keys(root.nodes).reduce(function(tot, node) { return tot + root.nodes[node].os.mem.used_in_bytes; }, 0); irá retornar o total de memória utilizada pelo cluster

\ 124 |

As seguintes funções estão disponíveis e podem ser úteis no processamento de vetores e objetos
\ 125 |

    \ 126 |
  • Object.keys(object) := array
  • \ 127 |
  • array.forEach(function(prop, index))
  • \ 128 |
  • array.map(function(prop, index)) := array
  • \ 129 |
  • array.reduce(function(accumulator, prop, index), initial_value) := final_value
  • \ 130 |
\ 131 |

Durante a execução da opção Refazer Requisição, um parâmetro extra chamado prev é passado para a função de transformação. Isso permite fazer comparações e marcações cumulativas

\ 132 |

Exemplo:
\ 133 | var la = [ root.nodes[Object.keys(root.nodes)[0]].os.load_average[0] ]; return prev ? la.concat(prev) : la; irá retornar a carga média no primeiro nó do cluster no último minuto\ 134 | Essa informação pode ser inserida no Gráfico para fazer um gráfico de carga do nó\ 135 | " 136 | }); 137 | 138 | i18n.setKeys({ 139 | "AnyRequest.DisplayOptionsHelp" : "\ 140 |

Json Bruto: Exibe o resultado completo da consulta e da transformação no formato de JSON bruto

\ 141 |

Gráfico de Resultados: Para gerar um gráfico com seus resultados, utilize o tranformador de resultados para produzir um vetor de valores

\ 142 |

Tabela de Resultados da Consulta: Se sua consulta for uma busca, você pode exibir seus resultados no formato de uma tabela.

\ 143 | " 144 | }); 145 | 146 | i18n.setKeys({ 147 | "QueryFilter.DateRangeHelp" : "\ 148 |

Campos do tipo Data aceitam consultas em linguagem natural (em inglês) para produzir um From e um To de modo a formar um intervalo dentro do qual os resultados são filtrados.

\ 149 |

Os seguintes formatos são suportados:

\ 150 |
    \ 151 |
  • Palavras-chave
    \ 152 | now
    today
    tomorrow
    yesterday
    last / this / next + week / month / year

    \ 153 | buscam por datas de acordo com a palavra-chave. last year irá buscar tudo do último ano.
  • \ 154 |
  • Intervalos
    \ 155 | 1000 secs
    5mins
    1day
    2days
    80d
    9 months
    2yrs
    (espaços são opcionais, diversos sinônimos para qualificadores de intervalo)
    \ 156 | Cria um intervalo de busca a partir de agora (now), extendendo este intervalo no passado e no futuro de acordo com intervalo especificado.
  • \ 157 |
  • Data/Hora (DateTime) e Data/Hora parcial
    \ 158 | 2011
    2011-01
    2011-01-18
    2011-01-18 12
    2011-01-18 12:32
    2011-01-18 12:32:45

    \ 159 | esses formatos especificam um intervalo especifico. 2011 irá buscar todo o ano de 2011, enquanto 2011-01-18 12:32:45 irá buscar apenas por resultados dentro deste intervalo de 1 segundo
  • \ 160 |
  • Tempo (Time) e Tempo parcial
    \ 161 | 12
    12:32
    12:32:45

    \ 162 | esses formatos buscam por um horário específico no dia atual. 12:32 irá buscar este minuto específico do dia
  • \ 163 |
  • Intervalos de Data
    \ 164 | 2010 -> 2011
    last week -> next week
    2011-05 ->
    < now

    \ 165 | Um intervalo de data é criado especificando-se duas datas em qualquer formato (Palavras-chave, Data/Hora ou Tempo) separados por < ou -> (ambos fazem a mesma coisa). Se a data de início ou fim do intervalo não for especificada é a mesma coisa que não impor limites na busca nesta direção.
  • \ 166 |
  • Intervalo de Data com Deslocamento
    \ 167 | 2010 -> 1yr
    3mins < now
    \ 168 | Busca a data especificada incluindo o intervalo na direção determinada pelo deslocamento
  • \ 169 |
  • Intervalos Bidirecionais
    \ 170 | 2010-05-13 05:13 <> 10m
    now <> 1yr
    lastweek <> 1month

    \ 171 | Idêntico ao exemplo anterior porém o intervalo é extendido em ambas as direções a partir da data especificada
  • \ 172 |
\ 173 | " 174 | }); 175 | -------------------------------------------------------------------------------- /App/esql.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Apr 19, 2017 3 | 4 | @author: qs 5 | ''' 6 | 7 | from App.utils import http_response_error,http_response_succes,http_response_nor 8 | import yaml 9 | import os 10 | from elasticsearch import Elasticsearch 11 | from ql.parse import lexer 12 | from ql.parse import parser 13 | from ply.lex import lex 14 | from ply.yacc import yacc 15 | from ql.dsl.Query import Query 16 | from ql.dsl.Response import response_hits,response_nor,response_cat,response_mappings,response_bulk 17 | from elasticsearch import ElasticsearchException 18 | from ql.parse.parser import TK 19 | from ql.dsl.Create import Create 20 | from ql.dsl.Describe import Describe 21 | from ql.dsl.Insert import Insert,Bulk 22 | from ql.dsl.Update import Update,Upsert 23 | from ql.dsl.Delete import Delete 24 | from ql.dsl.Drop import Drop 25 | from ql.dsl.Explain import Explain 26 | import time 27 | 28 | 29 | ESQL_HOME = os.path.realpath(os.path.join(__file__, '..', '..')) 30 | 31 | 32 | class Esql(): 33 | def __init__(self): 34 | conf_file = open(os.path.join(ESQL_HOME,'conf','esql.yml'), 'r') 35 | app_conf = yaml.load(conf_file) 36 | conf_file.close() 37 | self.es_hosts = app_conf['elastic'].get('hosts') 38 | self.es_handler = Elasticsearch(self.es_hosts) 39 | self.lexer = lex(module=lexer,optimize=True,debug=False) 40 | self.parser = yacc(debug=False,module=parser) 41 | 42 | 43 | def get_host_url(self): 44 | return "http://" + self.es_hosts[0]['host'] + ":" + str(self.es_hosts[0]['port']) 45 | 46 | 47 | 48 | def _exec_query(self,ast): 49 | 50 | try: 51 | stmt = Query(ast) 52 | except Exception: 53 | return http_response_error('Parse statement to dsl error!') 54 | 55 | try: 56 | if hasattr(stmt, 'route'): 57 | hits = self.es_handler.search(index=stmt._index, doc_type = stmt._type, body = stmt.dsl(),routing = stmt.route, request_timeout=100) 58 | else: 59 | hits = self.es_handler.search(index=stmt._index, doc_type = stmt._type, body = stmt.dsl(), request_timeout=100) 60 | except ElasticsearchException as e: 61 | return http_response_error(str(e)) 62 | try: 63 | mappings = self.es_handler.indices.get_mapping(index=stmt._index, doc_type = stmt._type) 64 | except ElasticsearchException as e: 65 | return http_response_error(str(e)) 66 | selecols = stmt.dsl()['_source'] 67 | stmt_res = None 68 | try: 69 | stmt_res = response_hits(hits,mappings,selecols) 70 | except Exception as e: 71 | return http_response_nor(str(e)) 72 | return http_response_succes(stmt_res) 73 | 74 | 75 | def _exec_create_table(self,ast): 76 | 77 | start_time = time.time() 78 | try: 79 | stmt = Create(ast) 80 | except Exception: 81 | return http_response_error('Parse statement to dsl error!') 82 | try: 83 | res = self.es_handler.indices.create(index=stmt._index,body = stmt._options,request_timeout=100,ignore= 400) 84 | if stmt._type == None: 85 | stmt._type = 'base' 86 | res = self.es_handler.indices.put_mapping(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), request_timeout=100) 87 | except ElasticsearchException as e: 88 | return http_response_nor(str(e)) 89 | 90 | stmt_res = None 91 | 92 | end_time = time.time() 93 | 94 | took = int((end_time - start_time)*1000) 95 | try: 96 | stmt_res = response_nor(res,took) 97 | except Exception as e: 98 | return http_response_error(str(e)) 99 | return http_response_succes(stmt_res) 100 | 101 | 102 | def _exec_show_tables(self,ast): 103 | 104 | start_time = time.time() 105 | 106 | try: 107 | res = self.es_handler.cat.indices(v=True,bytes='b',h=['index','status','pri','rep','docs.count','store.size']) 108 | except ElasticsearchException as e: 109 | return http_response_error(str(e)) 110 | 111 | stmt_res = res 112 | 113 | end_time = time.time() 114 | 115 | took = int((end_time - start_time)*1000) 116 | try: 117 | stmt_res = response_cat(res,took) 118 | except Exception as e: 119 | return http_response_error(str(e)) 120 | return http_response_succes(stmt_res) 121 | 122 | 123 | 124 | def _exec_desc_table(self,ast): 125 | start_time = time.time() 126 | try: 127 | stmt = Describe(ast) 128 | except Exception: 129 | return http_response_error('Parse statement to dsl error!') 130 | try: 131 | res = self.es_handler.indices.get_mapping(index = stmt._index, doc_type = stmt._type) 132 | except ElasticsearchException as e: 133 | return http_response_error(e.error) 134 | 135 | stmt_res = None 136 | 137 | end_time = time.time() 138 | 139 | took = int((end_time - start_time)*1000) 140 | try: 141 | stmt_res = response_mappings(res,took) 142 | except Exception as e: 143 | return http_response_error(str(e)) 144 | return http_response_succes(stmt_res) 145 | 146 | 147 | def _exec_drop_table(self,ast): 148 | start_time = time.time() 149 | try: 150 | stmt = Drop(ast) 151 | except Exception: 152 | return http_response_error('Parse statement to dsl error!') 153 | try: 154 | res = self.es_handler.indices.delete(index = stmt._index) 155 | except ElasticsearchException as e: 156 | return http_response_error(e.error) 157 | 158 | stmt_res = None 159 | 160 | end_time = time.time() 161 | 162 | took = int((end_time - start_time)*1000) 163 | try: 164 | stmt_res = response_nor(res,took) 165 | except Exception as e: 166 | return http_response_error(str(e)) 167 | return http_response_succes(stmt_res) 168 | 169 | def _exec_insert_into(self,ast): 170 | start_time = time.time() 171 | try: 172 | stmt = Insert(ast) 173 | except Exception: 174 | return http_response_error('Parse statement to dsl error!') 175 | try: 176 | parms = stmt.metas 177 | if stmt._type == None: 178 | stmt._type = 'base' 179 | res = self.es_handler.index(index = stmt._index,doc_type = stmt._type, body = stmt.dsl(),**parms) 180 | 181 | except ElasticsearchException as e: 182 | return http_response_error(str(e)) 183 | 184 | stmt_res = None 185 | end_time = time.time() 186 | took = int((end_time - start_time)*1000) 187 | try: 188 | stmt_res = response_nor(res,took) 189 | except Exception as e: 190 | return http_response_error(str(e)) 191 | return http_response_succes(stmt_res) 192 | 193 | 194 | def _exec_bulk_into(self,ast): 195 | 196 | try: 197 | stmt = Bulk(ast) 198 | except Exception: 199 | return http_response_error('Parse statement to dsl error!') 200 | try: 201 | if stmt._type == None: 202 | stmt._type = 'base' 203 | res = self.es_handler.bulk(index = stmt._index,doc_type = stmt._type, body = stmt.dsl()) 204 | 205 | except ElasticsearchException as e: 206 | return http_response_error(str(e)) 207 | 208 | stmt_res = None 209 | 210 | try: 211 | stmt_res = response_bulk(res) 212 | except Exception as e: 213 | return http_response_error(str(e)) 214 | return http_response_succes(stmt_res) 215 | 216 | 217 | 218 | def _exec_update(self,ast): 219 | start_time = time.time() 220 | try: 221 | stmt = Update(ast) 222 | except Exception: 223 | return http_response_error('Parse statement to dsl error!') 224 | try: 225 | if stmt._type == None: 226 | stmt._type = 'base' 227 | res = self.es_handler.update(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), **stmt.conditions) 228 | 229 | except ElasticsearchException as e: 230 | return http_response_error(str(e)) 231 | 232 | stmt_res = None 233 | 234 | end_time = time.time() 235 | took = int((end_time - start_time)*1000) 236 | try: 237 | stmt_res = response_nor(res,took) 238 | except Exception as e: 239 | return http_response_error(str(e)) 240 | return http_response_succes(stmt_res) 241 | 242 | 243 | def _exec_upsert(self,ast): 244 | start_time = time.time() 245 | try: 246 | stmt = Upsert(ast) 247 | except Exception: 248 | return http_response_error('Parse statement to dsl error!') 249 | try: 250 | if stmt._type == None: 251 | stmt._type = 'base' 252 | res = self.es_handler.update(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(), **stmt.conditions) 253 | 254 | except ElasticsearchException as e: 255 | return http_response_error(str(e)) 256 | 257 | stmt_res = None 258 | 259 | end_time = time.time() 260 | took = int((end_time - start_time)*1000) 261 | try: 262 | stmt_res = response_nor(res,took) 263 | except Exception as e: 264 | return http_response_error(str(e)) 265 | return http_response_succes(stmt_res) 266 | 267 | 268 | def _exec_delete(self,ast): 269 | start_time = time.time() 270 | try: 271 | stmt = Delete(ast) 272 | except Exception: 273 | return http_response_error('Parse statement to dsl error!') 274 | try: 275 | res = self.es_handler.delete_by_query(index = stmt._index, doc_type = stmt._type, body = stmt.dsl(),timeout='30m') 276 | except ElasticsearchException as e: 277 | return http_response_error(str(e)) 278 | 279 | stmt_res = None 280 | 281 | end_time = time.time() 282 | took = int((end_time - start_time)*1000) 283 | try: 284 | stmt_res = response_nor(res,took) 285 | except Exception as e: 286 | return http_response_error(str(e)) 287 | return http_response_succes(stmt_res) 288 | 289 | 290 | def _exec_explain(self,ast): 291 | try: 292 | stmt = Explain(ast) 293 | except Exception: 294 | return http_response_error('Parse statement to dsl error!') 295 | return http_response_nor(stmt.dsl(),202) 296 | 297 | 298 | def exec_statement(self,sql): 299 | ast = None 300 | try: 301 | ast = self.parser.parse(lexer=self.lexer.clone(),debug=False,input=sql) 302 | except Exception as e: 303 | return http_response_error(str(e)) 304 | 305 | if ast == None: 306 | return http_response_error('parse statement error') 307 | 308 | if ast.get_type() == TK.TOK_QUERY: 309 | return self._exec_query(ast) 310 | elif ast.get_type() == TK.TOK_CREATE_TABLE: 311 | return self._exec_create_table(ast) 312 | elif ast.get_type() == TK.TOK_SHOW_TABLES: 313 | return self._exec_show_tables(ast) 314 | elif ast.get_type() == TK.TOK_DESC_TABLE: 315 | return self._exec_desc_table(ast) 316 | elif ast.get_type() == TK.TOK_INSERT_INTO: 317 | return self._exec_insert_into(ast) 318 | elif ast.get_type() == TK.TOK_BULK_INTO: 319 | return self._exec_bulk_into(ast) 320 | elif ast.get_type() == TK.TOK_UPDATE: 321 | return self._exec_update(ast) 322 | elif ast.get_type() == TK.TOK_UPSERT_INTO: 323 | return self._exec_upsert(ast) 324 | elif ast.get_type() == TK.TOK_DELETE: 325 | return self._exec_delete(ast) 326 | elif ast.get_type() == TK.TOK_DROP_TABLE: 327 | return self._exec_drop_table(ast) 328 | elif ast.get_type() == TK.TOK_EXPLAIN: 329 | return self._exec_explain(ast) 330 | else: 331 | return http_response_error('Syntax not supported!') 332 | 333 | 334 | 335 | -------------------------------------------------------------------------------- /head/app.css: -------------------------------------------------------------------------------- 1 | TABLE.table { 2 | border-collapse: collapse; 3 | } 4 | 5 | 6 | TABLE.table TH { 7 | font-weight: normal; 8 | text-align: left; 9 | vertical-align: middle; 10 | } 11 | 12 | TABLE.table TBODY.striped TR:nth-child(odd) { 13 | background: #eee; 14 | } 15 | 16 | TABLE.table H3 { 17 | margin: 0; 18 | font-weight: bold; 19 | font-size: 140%; 20 | } 21 | 22 | .require { color: #a00; } 23 | 24 | .uiButton { 25 | padding: 0; 26 | border: 0; 27 | margin: 3px; 28 | width: auto; 29 | overflow: visible; 30 | cursor: pointer; 31 | background: transparent; 32 | } 33 | 34 | .uiButton-content { 35 | height: 20px; 36 | border: 1px solid #668dc6; 37 | border-radius: 2px; 38 | background: #96c6eb; 39 | background: -moz-linear-gradient(top, #96c6eb, #5296c7); 40 | background: -webkit-linear-gradient(top, #96c6eb, #5296c7); 41 | color: white; 42 | font-weight: bold; 43 | } 44 | 45 | .moz .uiButton-content { margin: 0 -2px; } 46 | 47 | .uiButton-label { 48 | padding: 2px 6px; 49 | white-space: nowrap; 50 | } 51 | .uiButton:hover .uiButton-content { 52 | background: #2777ba; 53 | background: -moz-linear-gradient(top, #6aaadf, #2777ba); 54 | background: -webkit-linear-gradient(top, #6aaadf, #2777ba); 55 | } 56 | .uiButton.active .uiButton-content, 57 | .uiButton:active .uiButton-content { 58 | background: #2575b7; 59 | background: -moz-linear-gradient(top, #2576b8, #2575b7); 60 | background: -webkit-linear-gradient(top, #2576b8, #2575b7); 61 | } 62 | .uiButton.disabled .uiButton-content, 63 | .uiButton.disabled:active .uiButton-content { 64 | border-color: #c6c6c6; 65 | color: #999999; 66 | background: #ddd; 67 | background: -moz-linear-gradient(top, #ddd, #ddd); 68 | background: -webkit-linear-gradient(top, #ddd, #ddd); 69 | } 70 | 71 | .uiButton.disabled { 72 | cursor: default; 73 | } 74 | 75 | .uiMenuButton { 76 | display: inline-block; 77 | } 78 | 79 | .uiMenuButton .uiButton-label { 80 | background-image: url('data:image/gif;base64,R0lGODlhDwAPAIABAP///////yH5BAEAAAEALAAAAAAPAA8AAAITjI+py+0P4wG0gmavq1HLD4ZiAQA7'); 81 | background-position: right 50%; 82 | background-repeat: no-repeat; 83 | padding-right: 17px; 84 | text-align: left; 85 | } 86 | 87 | .uiSplitButton { 88 | white-space: nowrap; 89 | } 90 | 91 | .uiSplitButton .uiButton:first-child { 92 | margin-right: 0; 93 | display: inline-block; 94 | } 95 | 96 | .uiSplitButton .uiButton:first-child .uiButton-content { 97 | border-right-width: 1; 98 | border-right-color: #5296c7; 99 | border-top-right-radius: 0; 100 | border-bottom-right-radius: 0; 101 | } 102 | 103 | .uiSplitButton .uiMenuButton { 104 | margin-left: 0; 105 | } 106 | 107 | .uiSplitButton .uiButton:last-child .uiButton-content { 108 | border-radius: 2px; 109 | border-left-width: 1; 110 | border-left-color: #96c6eb; 111 | border-top-left-radius: 0; 112 | border-bottom-left-radius: 0; 113 | height: 20px; 114 | } 115 | 116 | .uiSplitButton .uiButton:last-child .uiButton-label { 117 | padding: 2px 17px 2px 6px; 118 | margin-left: -8px; 119 | } 120 | 121 | .uiToolbar { 122 | height: 28px; 123 | background: #fdfefe; 124 | background: -moz-linear-gradient(top, #fdfefe, #eaedef); 125 | background: -webkit-linear-gradient(top, #fdfefe, #eaedef); 126 | border-bottom: 1px solid #d2d5d7; 127 | padding: 3px 10px; 128 | } 129 | 130 | .uiToolbar H2 { 131 | display: inline-block; 132 | font-size: 120%; 133 | margin: 0; 134 | padding: 5px 20px 5px 0; 135 | } 136 | 137 | .uiToolbar .uiTextField { 138 | display: inline-block; 139 | } 140 | 141 | .uiToolbar .uiTextField INPUT { 142 | padding-top: 2px; 143 | padding-bottom: 5px; 144 | } 145 | #uiModal { 146 | background: black; 147 | } 148 | 149 | .uiPanel { 150 | box-shadow: -1px 2.5px 4px -3px black, -1px -2.5px 4px -3px black, 3px 2.5px 4px -3px black, 3px -2.5px 4px -3px black; 151 | position: absolute; 152 | background: #eee; 153 | border: 1px solid #666; 154 | } 155 | 156 | .uiPanel-titleBar { 157 | text-align: center; 158 | font-weight: bold; 159 | padding: 2px 0; 160 | background: rgba(223, 223, 223, 0.75); 161 | background: -moz-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); 162 | background: -webkit-linear-gradient(top, rgba(223, 223, 223, 0.75), rgba(193, 193, 193, 0.75), rgba(223, 223, 223, 0.75)); 163 | border-bottom: 1px solid #bbb; 164 | } 165 | 166 | .uiPanel-close { 167 | cursor: pointer; 168 | border: 1px solid #aaa; 169 | background: #fff; 170 | color: #fff; 171 | float: left; 172 | height: 10px; 173 | left: 3px; 174 | line-height: 9px; 175 | padding: 1px 0; 176 | position: relative; 177 | text-shadow: 0 0 1px #000; 178 | top: 0px; 179 | width: 12px; 180 | } 181 | .uiPanel-close:hover { 182 | background: #eee; 183 | } 184 | 185 | .uiPanel-body { 186 | overflow: auto; 187 | } 188 | 189 | 190 | .uiInfoPanel { 191 | background: rgba(0, 0, 0, 0.75); 192 | color: white; 193 | border-radius: 8px; 194 | padding: 1px; 195 | } 196 | .uiInfoPanel .uiPanel-titleBar { 197 | background: rgba(74, 74, 74, 0.75); 198 | background: -moz-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); 199 | background: -webkit-linear-gradient(top, rgba(84, 84, 84, 0.75), rgba(54, 54, 54, 0.75), rgba(64, 64, 64, 0.75)); 200 | border-radius: 8px 8px 0 0; 201 | padding: 1px 0 2px 0; 202 | border-bottom: 0; 203 | } 204 | .uiInfoPanel .uiPanel-close { 205 | border-radius: 6px; 206 | height: 13px; 207 | width: 13px; 208 | background: #ccc; 209 | left: 3px; 210 | top: 1px; 211 | color: #333; 212 | text-shadow: #222 0 0 1px; 213 | line-height: 11px; 214 | border: 0; 215 | padding: 0; 216 | } 217 | .uiInfoPanel .uiPanel-close:hover { 218 | background: #eee; 219 | } 220 | 221 | .uiInfoPanel .uiPanel-body { 222 | background: transparent; 223 | padding: 20px; 224 | border-radius: 0 0 8px 8px; 225 | border: 1px solid #222; 226 | } 227 | 228 | .uiMenuPanel { 229 | border: 1px solid #668dc6; 230 | position: absolute; 231 | background: #96c6eb; 232 | color: white; 233 | } 234 | 235 | .uiMenuPanel LI { 236 | list-style: none; 237 | border-bottom: 1px solid #668dc6; 238 | } 239 | 240 | .uiMenuPanel LI:hover { 241 | background: #2575b7; 242 | } 243 | 244 | .uiMenuPanel LI:last-child { 245 | border-bottom: 0; 246 | } 247 | 248 | .uiMenuPanel-label { 249 | white-space: nowrap; 250 | padding: 2px 10px 2px 10px; 251 | cursor: pointer; 252 | } 253 | 254 | .disabled .uiMenuPanel-label { 255 | cursor: auto; 256 | color: #888; 257 | } 258 | 259 | .uiSelectMenuPanel .uiMenuPanel-label { 260 | margin-left: 1em; 261 | padding-left: 4px; 262 | } 263 | 264 | .uiSelectMenuPanel .uiMenuPanel-item.selected .uiMenuPanel-label:before { 265 | content: "\2713"; 266 | width: 12px; 267 | margin-left: -12px; 268 | display: inline-block; 269 | } 270 | 271 | .uiTable TABLE { 272 | border-collapse: collapse; 273 | } 274 | 275 | .uiTable-body { 276 | overflow-y: scroll; 277 | overflow-x: auto; 278 | } 279 | 280 | .uiTable-headers { 281 | overflow-x: hidden; 282 | } 283 | 284 | .uiTable-body TD { 285 | white-space: nowrap; 286 | } 287 | 288 | .uiTable-body .uiTable-header-row TH, 289 | .uiTable-body .uiTable-header-row TH DIV { 290 | padding-top: 0; 291 | padding-bottom: 0; 292 | } 293 | 294 | .uiTable-body .uiTable-header-cell > DIV { 295 | height: 0; 296 | overflow: hidden; 297 | } 298 | 299 | .uiTable-headercell-menu { 300 | float: right; 301 | } 302 | 303 | .uiTable-tools { 304 | padding: 3px 4px; 305 | height: 14px; 306 | } 307 | 308 | .uiTable-header-row { 309 | background: #ddd; 310 | background: -moz-linear-gradient(top, #eee, #ccc); 311 | background: -webkit-linear-gradient(top, #eee, #ccc); 312 | } 313 | 314 | .uiTable-headercell-text { 315 | margin-right: 20px; 316 | } 317 | 318 | .uiTable-headercell-menu { 319 | display: none; 320 | } 321 | 322 | .uiTable-header-row TH { 323 | border-right: 1px solid #bbb; 324 | padding: 0; 325 | text-align: left; 326 | } 327 | 328 | .uiTable-header-row TH > DIV { 329 | padding: 3px 4px; 330 | border-right: 1px solid #eee; 331 | } 332 | 333 | .uiTable-headerEndCap > DIV { 334 | width: 19px; 335 | } 336 | 337 | .uiTable-header-row .uiTable-sort { 338 | background: #ccc; 339 | background: -moz-linear-gradient(top, #bebebe, #ccc); 340 | background: -webkit-linear-gradient(top, #bebebe, #ccc); 341 | } 342 | .uiTable-header-row TH.uiTable-sort > DIV { 343 | border-right: 1px solid #ccc; 344 | } 345 | 346 | .uiTable-sort .uiTable-headercell-menu { 347 | display: block; 348 | } 349 | 350 | .uiTable TABLE TD { 351 | border-right: 1px solid transparent; 352 | padding: 3px 4px; 353 | } 354 | 355 | .uiTable-body TABLE TR:nth-child(even) { 356 | background: #f3f3f3; 357 | } 358 | 359 | .uiTable-body TABLE TR.selected { 360 | color: white; 361 | background: #6060f1; 362 | } 363 | 364 | DIV.uiJsonPretty-object { font-size: 1.26em; font-family: monospace; } 365 | UL.uiJsonPretty-object, 366 | UL.uiJsonPretty-array { margin: 0; padding: 0 0 0 2em; list-style: none; } 367 | UL.uiJsonPretty-object LI, 368 | UL.uiJsonPretty-array LI { padding: 0; margin: 0; } 369 | .expando > SPAN.uiJsonPretty-name:before { content: "\25bc\a0"; color: #555; position: relative; top: 2px; } 370 | .expando.uiJsonPretty-minimised > SPAN.uiJsonPretty-name:before { content: "\25ba\a0"; top: 0; } 371 | .uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before, 372 | .expando .uiJsonPretty-minimised > UL SPAN.uiJsonPretty-name:before { content: ""; } 373 | SPAN.uiJsonPretty-string, 374 | SPAN.uiJsonPretty-string A { color: green; } 375 | SPAN.uiJsonPretty-string A { text-decoration: underline;} 376 | SPAN.uiJsonPretty-number { color: blue; } 377 | SPAN.uiJsonPretty-null { color: red; } 378 | SPAN.uiJsonPretty-boolean { color: purple; } 379 | .expando > .uiJsonPretty-name { cursor: pointer; } 380 | .expando > .uiJsonPretty-name:hover { text-decoration: underline; } 381 | .uiJsonPretty-minimised { white-space: nowrap; overflow: hidden; } 382 | .uiJsonPretty-minimised > UL { opacity: 0.6; } 383 | .uiJsonPretty-minimised .uiJsonPretty-minimised > UL { opacity: 1; } 384 | .uiJsonPretty-minimised UL, .uiJsonPretty-minimised LI { display: inline; padding: 0; } 385 | 386 | 387 | .uiJsonPanel SPAN.uiJsonPretty-string { color: #6F6; } 388 | .uiJsonPanel SPAN.uiJsonPretty-number { color: #66F; } 389 | .uiJsonPanel SPAN.uiJsonPretty-null { color: #F66; } 390 | .uiJsonPanel SPAN.uiJsonPretty-boolean { color: #F6F; } 391 | 392 | .uiPanelForm-field { 393 | display: block; 394 | padding: 2px 0; 395 | clear: both; 396 | } 397 | 398 | .uiPanelForm-label { 399 | float: left; 400 | width: 200px; 401 | padding: 3px 7px; 402 | text-align: right; 403 | } 404 | 405 | .uiSidebarSection-head { 406 | background-color: #b9cfff; 407 | background-image: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAcCAMAAABifa5OAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAMAUExURQUCFf///wICAgMDAwQEBAUFBQYGBgcHBwgICAkJCQoKCgsLCwwMDA0NDQ4ODg8PDxAQEBERERISEhMTExQUFBUVFRYWFhcXFxgYGBkZGRoaGhsbGxwcHB0dHR4eHh8fHyAgICEhISIiIiMjIyQkJCUlJSYmJicnJygoKCkpKSoqKisrKywsLC0tLS4uLi8vLzAwMDExMTIyMjMzMzQ0NDU1NTY2Njc3Nzg4ODk5OTo6Ojs7Ozw8PD09PT4+Pj8/P0BAQEFBQUJCQkNDQ0REREVFRUZGRkdHR0hISElJSUpKSktLS0xMTE1NTU5OTk9PT1BQUFFRUVJSUlNTU1RUVFVVVVZWVldXV1hYWFlZWVpaWltbW1xcXF1dXV5eXl9fX2BgYGFhYWJiYmNjY2RkZGVlZWZmZmdnZ2hoaGlpaWpqamtra2xsbG1tbW5ubm9vb3BwcHFxcXJycnNzc3R0dHV1dXZ2dnd3d3h4eHl5eXp6ent7e3x8fH19fX5+fn9/f4CAgIGBgYKCgoODg4SEhIWFhYaGhoeHh4iIiImJiYqKiouLi4yMjI2NjY6Ojo+Pj5CQkJGRkZKSkpOTk5SUlJWVlZaWlpeXl5iYmJmZmZqampubm5ycnJ2dnZ6enp+fn6CgoKGhoaKioqOjo6SkpKWlpaampqenp6ioqKmpqaqqqqurq6ysrK2tra6urq+vr7CwsLGxsbKysrOzs7S0tLW1tba2tre3t7i4uLm5ubq6uru7u7y8vL29vb6+vr+/v8DAwMHBwcLCwsPDw8TExMXFxcbGxsfHx8jIyMnJycrKysvLy8zMzM3Nzc7Ozs/Pz9DQ0NHR0dLS0tPT09TU1NXV1dbW1tfX19jY2NnZ2dra2tvb29zc3N3d3d7e3t/f3+Dg4OHh4eLi4uPj4+Tk5OXl5ebm5ufn5+jo6Onp6erq6uvr6+zs7O3t7e7u7u/v7/Dw8PHx8fLy8vPz8/T09PX19fb29vf39/j4+Pn5+fr6+vv7+/z8/P39/f7+/v///2Oyy/cAAAACdFJOU/8A5bcwSgAAADxJREFUeNq8zzkOACAMA8Hd/3+agiuRcIsrRopIjArOoLK1QAMNNBCRPkhLyzkn35Bjfd7JR1Nr09NoDACnvgDl1zlzoQAAAABJRU5ErkJggg=='); 408 | background-repeat: no-repeat; 409 | background-position: 2px 5px; 410 | margin-bottom: 1px; 411 | padding: 3px 3px 3px 17px; 412 | cursor: pointer; 413 | } 414 | 415 | .shown > .uiSidebarSection-head { 416 | background-position: 2px -13px; 417 | } 418 | 419 | .uiSidebarSection-body { 420 | margin-bottom: 3px; 421 | display: none; 422 | } 423 | 424 | .uiSidebarSection-help { 425 | text-shadow: #228 1px 1px 2px; 426 | color: blue; 427 | cursor: pointer; 428 | } 429 | 430 | .uiSidebarSection-help:hover { 431 | text-decoration: underline; 432 | } 433 | 434 | .uiQueryFilter { 435 | width: 350px; 436 | padding: 5px; 437 | background: #d8e7ff; 438 | background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); 439 | background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); 440 | } 441 | 442 | .uiQueryFilter DIV.uiQueryFilter-section { 443 | margin-bottom: 5px; 444 | } 445 | 446 | .uiQueryFilter HEADER { 447 | display: block; 448 | font-variant: small-caps; 449 | font-weight: bold; 450 | margin: 5px 0; 451 | } 452 | 453 | .uiQueryFilter-aliases SELECT { 454 | width: 100%; 455 | } 456 | 457 | .uiQueryFilter-booble { 458 | cursor: pointer; 459 | background: #e8f1ff; 460 | border: 1px solid #e8f1ff; 461 | border-radius: 5px; 462 | padding: 1px 4px; 463 | margin-bottom: 1px; 464 | overflow: hidden; 465 | white-space: nowrap; 466 | } 467 | 468 | .uiQueryFilter-booble.selected { 469 | background: #dae3f0; 470 | border-top: 1px solid #c8d4e6; 471 | border-left: 1px solid #c8d4e6; 472 | border-bottom: 1px solid #ffffff; 473 | border-right: 1px solid #ffffff; 474 | } 475 | 476 | .uiQueryFilter-filterName { 477 | background-color: #cbdfff; 478 | margin-bottom: 4px; 479 | padding: 3px; 480 | cursor: pointer; 481 | } 482 | 483 | .uiQueryFilter-filters INPUT { 484 | width: 300px; 485 | } 486 | 487 | .uiQueryFilter-subMultiFields { 488 | padding-left: 10px; 489 | } 490 | 491 | .uiQueryFilter-rangeHintFrom, 492 | .uiQueryFilter-rangeHintTo { 493 | margin: 0; 494 | opacity: 0.75; 495 | } 496 | .uiBrowser-filter { 497 | float: left; 498 | } 499 | 500 | .uiBrowser-table { 501 | margin-left: 365px; 502 | } 503 | 504 | .uiAnyRequest-request { 505 | float: left; 506 | width: 350px; 507 | padding: 5px; 508 | background: #d8e7ff; 509 | background: -moz-linear-gradient(left, #d8e7ff, #e8f1ff); 510 | background: -webkit-linear-gradient(left, #d8e7ff, #e8f1ff); 511 | } 512 | 513 | .uiAnyRequest-request INPUT[type=text], 514 | .uiAnyRequest-request TEXTAREA { 515 | width: 340px; 516 | } 517 | 518 | .anyRequest INPUT[name=path] { 519 | width: 259px; 520 | } 521 | 522 | .uiAnyRequest-out { 523 | margin-left: 365px; 524 | } 525 | 526 | .uiAnyRequest-out P { 527 | margin-top: 0; 528 | } 529 | 530 | .uiAnyRequest-jsonErr { 531 | color: red; 532 | } 533 | 534 | .uiAnyRequest-history { 535 | margin: 0; 536 | padding: 0; 537 | list-style: none; 538 | max-height: 100px; 539 | overflow-x: hidden; 540 | overflow-y: auto; 541 | } 542 | 543 | .uiNodesView TH, 544 | .uiNodesView TD { 545 | vertical-align: top; 546 | padding: 2px 20px; 547 | } 548 | 549 | .uiNodesView TH.close, 550 | .uiNodesView TD.close { 551 | color: #888; 552 | background: #f2f2f2; 553 | } 554 | 555 | .uiNodesView .uiMenuButton .uiButton-content { 556 | padding-right: 3px; 557 | border-radius: 8px; 558 | height: 14px; 559 | } 560 | 561 | .uiNodesView .uiMenuButton.active .uiButton-content, 562 | .uiNodesView .uiMenuButton:active .uiButton-content { 563 | border-bottom-right-radius: 0px; 564 | border-bottom-left-radius: 0px; 565 | } 566 | 567 | .uiNodesView .uiMenuButton .uiButton-label { 568 | padding: 0px 17px 0px 7px; 569 | } 570 | 571 | .uiNodesView-hasAlias { 572 | text-align: center; 573 | } 574 | .uiNodesView-hasAlias.max { 575 | border-top-right-radius: 8px; 576 | border-bottom-right-radius: 8px; 577 | } 578 | .uiNodesView-hasAlias.min { 579 | border-top-left-radius: 8px; 580 | border-bottom-left-radius: 8px; 581 | } 582 | .uiNodesView-hasAlias-remove { 583 | float: right; 584 | font-weight: bold; 585 | cursor: pointer; 586 | } 587 | 588 | .uiNodesView TD.uiNodesView-icon { 589 | padding: 20px 0px 15px 20px; 590 | } 591 | 592 | .uiNodesView-node:nth-child(odd) { 593 | background: #eee; 594 | } 595 | 596 | .uiNodesView-routing { 597 | position: relative; 598 | min-width: 90px; 599 | } 600 | 601 | .uiNodesView-nullReplica, 602 | .uiNodesView-replica { 603 | box-sizing: border-box; 604 | cursor: pointer; 605 | float: left; 606 | height: 40px; 607 | width: 35px; 608 | margin: 4px; 609 | border: 2px solid #444; 610 | padding: 2px; 611 | font-size: 32px; 612 | line-height: 32px; 613 | text-align: center; 614 | letter-spacing: -5px; 615 | text-indent: -7px; 616 | } 617 | 618 | .uiNodesView-replica.primary { 619 | border-width: 4px; 620 | line-height: 29px; 621 | } 622 | 623 | .uiNodesView-nullReplica { 624 | border-color: transparent; 625 | } 626 | 627 | .uiNodesView-replica.state-UNASSIGNED { background: #eeeeee; color: #999; border-color: #666; float: none; display: inline-block; } 628 | .uiNodesView-replica.state-INITIALIZING { background: #dddc88; } 629 | .uiNodesView-replica.state-STARTED { background: #99dd88; } 630 | .uiNodesView-replica.state-RELOCATING { background: #dc88dd; } 631 | 632 | 633 | .uiClusterConnect-uri { 634 | width: 280px; 635 | } 636 | 637 | .uiStructuredQuery { 638 | padding: 10px; 639 | } 640 | 641 | .uiStructuredQuery-out { 642 | min-height: 30px; 643 | } 644 | 645 | .uiFilterBrowser-row * { 646 | margin-right: 0.4em; 647 | } 648 | 649 | .uiFilterBrowser-row BUTTON { 650 | height: 22px; 651 | position: relative; 652 | top: 1px; 653 | } 654 | 655 | .uiHeader { 656 | padding: 3px 10px; 657 | } 658 | 659 | .uiHeader-name, .uiHeader-status { 660 | font-size: 1.2em; 661 | font-weight: bold; 662 | padding: 0 10px; 663 | } 664 | 665 | 666 | .uiApp-header { 667 | background: #eee; 668 | position: fixed; 669 | width: 100%; 670 | z-index: 9; 671 | } 672 | 673 | .uiApp-header H1 { 674 | margin: -2px 0 -4px 0; 675 | float: left; 676 | padding-right: 25px; 677 | } 678 | 679 | .uiApp-headerMenu { 680 | border-bottom: 1px solid #bbb; 681 | padding: 0px 3px; 682 | height: 22px; 683 | } 684 | 685 | .uiApp-headerMenu .active { 686 | background: white; 687 | border-bottom-color: white; 688 | } 689 | 690 | .uiApp-headerMenuItem { 691 | border: 1px solid #bbb; 692 | padding: 4px 8px 1px ; 693 | margin: 2px 1px 0; 694 | height: 14px; 695 | cursor: pointer; 696 | } 697 | 698 | .uiApp-body { 699 | padding: 51px 0px 0px 0px; 700 | } 701 | 702 | .uiApp-headerNewMenuItem { 703 | color: blue; 704 | } 705 | -------------------------------------------------------------------------------- /ql/parse/parser.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Dec 15, 2016 3 | 4 | @author: qs 5 | ''' 6 | 7 | from ql.parse import lexer 8 | from ql.parse import ASTNode 9 | from enum import Enum 10 | 11 | class AutoNumber(Enum): 12 | def __new__(cls): 13 | value = len(cls.__members__) + 1 14 | obj = object.__new__(cls) 15 | obj._value_ = value 16 | return obj 17 | 18 | 19 | 20 | class TK(AutoNumber): 21 | TOK_IDENTIFIER = () 22 | TOK_VALUE = () 23 | TOK_DQ_VALUE = () 24 | TOK_DOT = () 25 | TOK_LIST = () 26 | TOK_DICT = () 27 | TOK_TUPLE = () 28 | TOK_KEY_VALUE = () 29 | 30 | TOK_CORE_TYPE = () 31 | TOK_TABLE_NAME=() 32 | 33 | TOK_CREATE_TABLE = () 34 | TOK_QUERY = () 35 | TOK_INSERT_INTO = () 36 | TOK_BULK_INTO = () 37 | TOK_UPSERT_INTO = () 38 | TOK_UPDATE = () 39 | TOK_DELETE = () 40 | 41 | TOK_COLUMN_DEFINE = () 42 | TOK_COLUMN_OPTIONS = () 43 | TOK_META_DEFINE = () 44 | TOK_META_OPTIONS = () 45 | TOK_TABLE_COLUMNS = () 46 | TOK_TABLE_METAS = () 47 | TOK_TABLE_OPTIONS = () 48 | 49 | TOK_FUNCTION = () 50 | TOK_EXPRESSION = () 51 | TOK_COMPARE = () 52 | TOK_IN = () 53 | TOK_REVERSED = () 54 | TOK_COMPOUND = () 55 | TOK_EXPRESSION_LEFT = () 56 | TOK_EXPRESSION_RIGHT = () 57 | 58 | TOK_SELECT = () 59 | TOK_SELEXPR = () 60 | TOK_FROM = () 61 | TOK_WHERE = () 62 | TOK_LIMIT = () 63 | TOK_ORDERBY = () 64 | TOK_GROUPBY = () 65 | TOK_SORT = () 66 | TOK_SORT_MODE = () 67 | 68 | TOK_INSERT_COLUMNS = () 69 | TOK_INSERT_ROW = () 70 | TOK_INSERT_ROWS = () 71 | 72 | TOK_SET_COLUMNS_CLAUSE = () 73 | 74 | TOK_EXPLAIN = () 75 | 76 | TOK_DESC_TABLE = () 77 | TOK_SHOW_TABLES = () 78 | TOK_DROP_TABLE = () 79 | 80 | tokens = lexer.tokens 81 | 82 | precedence = ( 83 | ('left','OR'), 84 | ('left','AND'), 85 | ('left','NOT')) 86 | 87 | 88 | def token_list(plist): 89 | retval = [] 90 | if len(plist) == 2: 91 | retval = [plist[1]] 92 | else: 93 | if isinstance(plist[3],list): 94 | retval = [plist[1]] + plist[3] 95 | else: 96 | retval = [plist[1],plist[3]] 97 | return retval 98 | 99 | 100 | def p_EXECUTE_STATEMENT(p): 101 | '''EXECUTE_STATEMENT : STATEMENT''' 102 | p[0] = p[1] 103 | 104 | 105 | def p_EXPLAIN_STATEMENT(p): 106 | '''STATEMENT : EXPLAIN STATEMENT''' 107 | p[0] = ASTNode.Node(TK.TOK_EXPLAIN,None,[p[2]]) 108 | 109 | 110 | def p_STATEMENT(p): 111 | '''STATEMENT : TOK_CREATE_TABLE 112 | | TOK_INSERT_INTO 113 | | TOK_QUERY 114 | | TOK_BULK_INTO 115 | | TOK_UPDATE 116 | | TOK_UPSERT_INTO 117 | | TOK_DELETE 118 | | TOK_SHOW_TABLES 119 | | TOK_DESC_TABLE 120 | | TOK_DROP_TABLE''' 121 | p[0] = p[1] 122 | 123 | 124 | 125 | '''======================================base define========================================================''' 126 | 127 | 128 | def p_TOK_OPTIONS_OBJECT(p): 129 | '''TOK_OPTIONS_OBJECT : "(" KV_ELEMENTS_EXPR ")"''' 130 | p[0] = ASTNode.Node(TK.TOK_DICT,None,p[2]) 131 | 132 | 133 | def p_TOK_DICT_OBJECT(p): 134 | '''TOK_DICT_OBJECT : "{" KV_ELEMENTS_EXPR "}"''' 135 | p[0] = ASTNode.Node(TK.TOK_DICT,None,p[2]) 136 | 137 | 138 | def p_TOK_LIST_OBJECT(p): 139 | '''TOK_LIST_OBJECT : "[" VALUES_EXPR "]" 140 | | "[" "]"''' 141 | if len(p) == 4: 142 | p[0] = ASTNode.Node(TK.TOK_LIST,None,p[2]) 143 | else: 144 | p[0] = ASTNode.Node(TK.TOK_LIST,None,None) 145 | 146 | 147 | def p_TOK_TUPLE_OBJECT(p): 148 | '''TOK_TUPLE_OBJECT : "(" VALUES_EXPR ")" 149 | | "(" ")"''' 150 | if len(p) == 4: 151 | p[0] = ASTNode.Node(TK.TOK_TUPLE,None,p[2]) 152 | else: 153 | p[0] = ASTNode.Node(TK.TOK_TUPLE,None,None) 154 | 155 | 156 | def p_KV_ELEMENTS_EXPR(p): 157 | '''KV_ELEMENTS_EXPR : TOK_KEY_VALUE 158 | | TOK_KEY_VALUE COMMA TOK_KEY_VALUE 159 | | TOK_KEY_VALUE COMMA KV_ELEMENTS_EXPR''' 160 | p[0] = token_list(p) 161 | 162 | 163 | def p_VALUES_EXPR(p): 164 | '''VALUES_EXPR : VALUE_EXPR 165 | | VALUE_EXPR COMMA VALUE_EXPR 166 | | VALUE_EXPR COMMA VALUES_EXPR''' 167 | p[0] = token_list(p) 168 | 169 | 170 | def p_TOK_KEY_VALUE(p): 171 | '''TOK_KEY_VALUE : TOK_EXPRESSION''' 172 | if p[1].get_value() != '=': 173 | pass 174 | else: 175 | p[0] = p[1] 176 | p[0].set_type(TK.TOK_KEY_VALUE) 177 | 178 | 179 | 180 | def p_LEFT_RESERVED_VALUES_EXPR(p): 181 | '''LEFT_RESERVED_VALUES_EXPR : FROM 182 | | TO''' 183 | p[0] = ASTNode.Node(TK.TOK_VALUE,p[1],None) 184 | 185 | 186 | 187 | def p_LEFT_VALUE_EXPR(p): 188 | '''LEFT_VALUE_EXPR : VALUE_EXPR 189 | | TOK_FUNCTION_EXPR 190 | | LEFT_RESERVED_VALUES_EXPR''' 191 | p[0] = p[1] 192 | 193 | def p_LEFT_VALUES_EXPR(p): 194 | '''LEFT_VALUES_EXPR : LEFT_VALUE_EXPR 195 | | LEFT_VALUE_EXPR COMMA LEFT_VALUE_EXPR 196 | | LEFT_VALUE_EXPR COMMA LEFT_VALUES_EXPR''' 197 | p[0] = token_list(p) 198 | 199 | 200 | def p_RIGHT_VALUE_EXPR(p): 201 | '''RIGHT_VALUE_EXPR : VALUE_EXPR''' 202 | p[0] = p[1] 203 | 204 | 205 | def p_RIGHT_VALUES_EXPR(p): 206 | '''RIGHT_VALUES_EXPR : VALUE_EXPR 207 | | RIGHT_VALUE_EXPR COMMA RIGHT_VALUE_EXPR 208 | | RIGHT_VALUE_EXPR COMMA RIGHT_VALUES_EXPR''' 209 | p[0] = token_list(p) 210 | 211 | 212 | 213 | def p_VALUE_EXPR(p): 214 | '''VALUE_EXPR : TOK_DOT 215 | | TOK_VALUE 216 | | TOK_DICT_OBJECT 217 | | TOK_LIST_OBJECT''' 218 | p[0] = p[1] 219 | 220 | 221 | def p_TOK_DOT(p): 222 | '''TOK_DOT : TOK_VALUE "." TOK_VALUE 223 | | TOK_VALUE "." TOK_DOT''' 224 | p[0] = ASTNode.Node(TK.TOK_DOT,p[2],[p[1],(p[3])]) 225 | 226 | 227 | def p_TOK_DQ_VALUE(p): 228 | '''TOK_VALUE : DQUOTE_STRING''' 229 | p[0] = ASTNode.Node(TK.TOK_VALUE,p[1],[ASTNode.Node(TK.TOK_DQ_VALUE,None,None)]) 230 | 231 | 232 | def p_TOK_VALUE(p): 233 | '''TOK_VALUE : WORD 234 | | QUOTE_STRING 235 | | NUMBER 236 | | "*"''' 237 | p[0] = ASTNode.Node(TK.TOK_VALUE,p[1],None) 238 | 239 | 240 | 241 | def p_TOK_WILDCARD_VALUE(p): 242 | '''TOK_VALUE : WORD "*"''' 243 | p[0] = ASTNode.Node(TK.TOK_VALUE,p[1] + p[2],None) 244 | 245 | 246 | '''=======================================operator define==============================================''' 247 | 248 | 249 | def p_EXPRESSIONS_REVERSED_EXPR(p): 250 | '''EXPRESSION_EXPR : NOT EXPRESSION_EXPR''' 251 | p[0] = ASTNode.Node(TK.TOK_REVERSED,p[1].lower(),[p[2]]) 252 | 253 | 254 | def p_EXPRESSIONS_GROUP_EXPR(p): 255 | '''EXPRESSION_EXPR : "(" EXPRESSION_EXPR ")"''' 256 | p[0] = p[2] 257 | 258 | 259 | def p_EXPRESSION_OPERATOR_EXPR(p): 260 | '''EXPRESSION_EXPR : EXPRESSION_EXPR OR EXPRESSION_EXPR 261 | | EXPRESSION_EXPR AND EXPRESSION_EXPR''' 262 | p[0] = ASTNode.Node(TK.TOK_COMPOUND,p[2].lower(),[p[1],p[3]]) 263 | 264 | 265 | 266 | def p_EXPRESSION_EXPR(p): 267 | '''EXPRESSION_EXPR : TOK_EXPRESSION 268 | | TOK_FUNCTION_EXPR 269 | | TOK_IN_EXPR''' 270 | p[0] = p[1] 271 | 272 | 273 | def p_TOK_EXPRESSION_LEFT(p): 274 | '''TOK_EXPRESSION_LEFT : LEFT_VALUES_EXPR''' 275 | p[0] = ASTNode.Node(TK.TOK_EXPRESSION_LEFT,None,p[1]) 276 | 277 | def p_TOK_EXPRESSION_RIGHT(p): 278 | '''TOK_EXPRESSION_RIGHT : RIGHT_VALUE_EXPR''' 279 | p[0] = ASTNode.Node(TK.TOK_EXPRESSION_RIGHT,None,[p[1]]) 280 | 281 | def p_TOK_EXPRESSION(p): 282 | '''TOK_EXPRESSION : TOK_EXPRESSION_LEFT COMPARE_TYPE_EXPR TOK_EXPRESSION_RIGHT''' 283 | if p[2] == '!=': 284 | expression = ASTNode.Node(TK.TOK_COMPARE,'=',[p[1],p[3]]) 285 | p[0] = ASTNode.Node(TK.TOK_REVERSED,'NOT'.lower(),[expression]) 286 | else: 287 | p[0] = ASTNode.Node(TK.TOK_COMPARE,p[2],[p[1],p[3]]) 288 | 289 | 290 | def p_COMPARE_TYPE_EXPR(p): 291 | '''COMPARE_TYPE_EXPR : COMPARE_TYPE 292 | | LIKE''' 293 | p[0] = p[1] 294 | 295 | 296 | def p_TOK_FUNCTION_EXPR(p): 297 | '''TOK_FUNCTION_EXPR : TOK_BEWTEEN 298 | | TOK_FUNCTION 299 | | TOK_ISNULL''' 300 | p[0] = p[1] 301 | 302 | 303 | def p_TOK_FUNCTION(p): 304 | '''TOK_FUNCTION : VALUE_EXPR TOK_TUPLE_OBJECT''' 305 | p[0] = ASTNode.Node(TK.TOK_FUNCTION,p[1].get_value(),p[2].get_children()) 306 | 307 | 308 | def p_TOK_BEWTEEN(p): 309 | '''TOK_BEWTEEN : VALUE_EXPR BETWEEN RIGHT_VALUE_EXPR AND RIGHT_VALUE_EXPR''' 310 | p[0] = ASTNode.Node(TK.TOK_FUNCTION,p[2],[p[1],p[3],p[5]]) 311 | 312 | 313 | def p_TOK_ISNULL(p): 314 | '''TOK_ISNULL : VALUE_EXPR IS NULL 315 | | VALUE_EXPR IS NOT NULL''' 316 | if len(p) == 4: 317 | p[0] = ASTNode.Node(TK.TOK_FUNCTION,'ISNULL',[p[1]]) 318 | else: 319 | expression = ASTNode.Node(TK.TOK_FUNCTION,'ISNULL',[p[1]]) 320 | p[0] = ASTNode.Node(TK.TOK_REVERSED,'NOT'.lower(),[expression]) 321 | 322 | 323 | def p_TOK_IN_EXPR(p): 324 | '''TOK_IN_EXPR : TOK_EXPRESSION_LEFT IN TOK_TUPLE_OBJECT''' 325 | p[0] = p[0] = ASTNode.Node(TK.TOK_IN,p[2],[p[1],p[3]]) 326 | 327 | '''==========================================table define===========================================''' 328 | 329 | 330 | def p_TOK_CREATE_TABLE_WITH_OPTIONS(p): 331 | '''TOK_CREATE_TABLE : TOK_CREATE_TABLE WITH OPTION TOK_TABLE_OPTIONS''' 332 | p[0] = p[1] 333 | p[0].append_children(p[4]) 334 | 335 | 336 | def p_TOK_CREATE_TABLE_WITH_META(p): 337 | '''TOK_CREATE_TABLE : TOK_CREATE_TABLE WITH META TOK_TABLE_METAS''' 338 | p[0] = p[1] 339 | p[0].append_children(p[4]) 340 | 341 | 342 | def p_TOK_CREATE_TABLE(p): 343 | '''TOK_CREATE_TABLE : CREATE TABLE TOK_TABLE_NAME TOK_TABLE_COLS''' 344 | p[0] = ASTNode.Node(TK.TOK_CREATE_TABLE,None,[p[3],p[4]]) 345 | p[0].get_value() 346 | 347 | 348 | def p_TOK_META_OPTIONS(p): 349 | '''TOK_META_OPTIONS : TOK_OPTIONS_OBJECT''' 350 | p[0] = ASTNode.Node(TK.TOK_META_OPTIONS,None,[p[1]]) 351 | 352 | 353 | def p_TOK_META_DEFINE(p): 354 | '''TOK_META_DEF : WORD TOK_META_OPTIONS''' 355 | p[0] = ASTNode.Node(TK.TOK_META_DEFINE,p[1],[p[2]]) 356 | 357 | 358 | def p_TOK_METAS_DEFINE(p): 359 | '''TOK_METAS_DEF : TOK_META_DEF 360 | | TOK_META_DEF COMMA TOK_META_DEF 361 | | TOK_META_DEF COMMA TOK_METAS_DEF''' 362 | p[0] = token_list(p) 363 | 364 | 365 | def p_TOK_TABLE_METAS(p): 366 | '''TOK_TABLE_METAS : "(" ")" 367 | | "(" TOK_METAS_DEF ")"''' 368 | if len(p) == 3: 369 | p[0] = ASTNode.Node(TK.TOK_TABLE_METAS,None,None) 370 | else: 371 | p[0] = ASTNode.Node(TK.TOK_TABLE_METAS,None,p[2]) 372 | 373 | 374 | def p_TOK_TABLE_OPTIONS(p): 375 | '''TOK_TABLE_OPTIONS : TOK_OPTIONS_OBJECT''' 376 | p[0] = ASTNode.Node(TK.TOK_TABLE_OPTIONS,None,[p[1]]) 377 | 378 | 379 | def p_TOK_TABLE_NAME(p): 380 | '''TOK_TABLE_NAME : VALUE_EXPR''' 381 | p[0] = ASTNode.Node(TK.TOK_TABLE_NAME,None,[p[1]]) 382 | 383 | 384 | def p_TOK_TABLE_COLS(p): 385 | '''TOK_TABLE_COLS : "(" ")" 386 | | "(" TOK_COLUMNS_DEFINE ")"''' 387 | if len(p) == 3: 388 | p[0] = ASTNode.Node(TK.TOK_TABLE_COLUMNS,None,None) 389 | else: 390 | p[0] = ASTNode.Node(TK.TOK_TABLE_COLUMNS,None,p[2]) 391 | 392 | 393 | def p_TOK_COLUMNS_DEFINE(p): 394 | '''TOK_COLUMNS_DEFINE : TOK_COLUMN_DEFINE 395 | | TOK_COLUMN_DEFINE COMMA TOK_COLUMN_DEFINE 396 | | TOK_COLUMN_DEFINE COMMA TOK_COLUMNS_DEFINE''' 397 | p[0] = token_list(p) 398 | 399 | 400 | def p_COLUMN_TYPE(p): 401 | '''COLUMN_TYPE : WORD''' 402 | p[0] = ASTNode.Node(TK.TOK_CORE_TYPE,p[1],None) 403 | 404 | 405 | def p_TOK_COLUMN_OBJECT_DEFINE(p): 406 | '''TOK_COLUMN_DEFINE : TOK_COLUMN_DEFINE AS TOK_TABLE_COLS''' 407 | p[0] = p[1] 408 | p[0].append_children(p[3]) 409 | 410 | 411 | def p_p_TOK_COLUMN_OPTIONS(p): 412 | '''TOK_COLUMN_OPTIONS : TOK_OPTIONS_OBJECT''' 413 | p[0] = ASTNode.Node(TK.TOK_COLUMN_OPTIONS,None,[p[1]]) 414 | 415 | 416 | def p_TOK_COLUMN_DEFINE(p): 417 | '''TOK_COLUMN_DEFINE : WORD COLUMN_TYPE 418 | | WORD COLUMN_TYPE TOK_COLUMN_OPTIONS''' 419 | if len(p) == 3: 420 | p[0] = ASTNode.Node(TK.TOK_COLUMN_DEFINE,p[1],[p[2]]) 421 | else: 422 | p[0] = ASTNode.Node(TK.TOK_COLUMN_DEFINE,p[1],[p[2],p[3]]) 423 | 424 | 425 | 426 | 427 | 428 | '''=================================query define========================================''' 429 | 430 | 431 | def p_TOK_QUERY_WITH_ORDERBY(p): 432 | '''TOK_QUERY : TOK_QUERY ORDER BY TOK_ORDERBY''' 433 | p[0] = p[1] 434 | p[0].append_children(p[4]) 435 | 436 | 437 | def p_TOK_QUERY_WITH_EXPRESSIONS(p): 438 | '''TOK_QUERY : TOK_QUERY WHERE TOK_WHERE''' 439 | p[0] = p[1] 440 | p[0].append_children(p[3]) 441 | 442 | def p_TOK_QUERY_WITH_LIMITS(p): 443 | '''TOK_QUERY : TOK_QUERY LIMIT TOK_LIMIT''' 444 | p[0] = p[1] 445 | p[0].append_children(p[3]) 446 | 447 | 448 | def p_TOK_QUERY(p): 449 | '''TOK_QUERY : SELECT TOK_SELECT FROM TOK_FROM''' 450 | p[0] = ASTNode.Node(TK.TOK_QUERY,None,[p[2],p[4]]) 451 | 452 | 453 | def p_TOK_FROM(p): 454 | '''TOK_FROM : TOK_TABLE_NAME''' 455 | p[0] = ASTNode.Node(TK.TOK_FROM,None,[p[1]]) 456 | 457 | def p_TOK_FROM_WITH_ROUTING(p): 458 | '''TOK_FROM : TOK_TABLE_NAME "@" TOK_VALUE''' 459 | p[0] = ASTNode.Node(TK.TOK_FROM,None,[p[1],p[3]]) 460 | 461 | 462 | def p_TOK_WHRER(p): 463 | '''TOK_WHERE : EXPRESSION_EXPR''' 464 | p[0] = ASTNode.Node(TK.TOK_WHERE,None,[p[1]]) 465 | 466 | 467 | def p_TOK_SELECT(p): 468 | '''TOK_SELECT : TOK_SELEXPRS''' 469 | p[0] = ASTNode.Node(TK.TOK_SELECT,None,p[1]) 470 | 471 | 472 | def p_TOK_SELEXPR(p): 473 | '''TOK_SELEXPR : LEFT_VALUE_EXPR 474 | | LEFT_VALUE_EXPR AS VALUE_EXPR''' 475 | if len(p) == 2: 476 | p[0] = ASTNode.Node(TK.TOK_SELEXPR,None,[p[1]]) 477 | else: 478 | p[0] = ASTNode.Node(TK.TOK_SELEXPR,None,[p[1],p[3]]) 479 | 480 | def p_TOK_SELEXPRS(p): 481 | '''TOK_SELEXPRS : TOK_SELEXPR 482 | | TOK_SELEXPR COMMA TOK_SELEXPR 483 | | TOK_SELEXPR COMMA TOK_SELEXPRS''' 484 | p[0] = token_list(p) 485 | 486 | 487 | def p_TOK_LIMIT(p): 488 | '''TOK_LIMIT : LIMITS_EXPR''' 489 | p[0] = ASTNode.Node(TK.TOK_LIMIT,None,p[1]) 490 | 491 | 492 | def p_LIMIT_EXPR(p): 493 | '''LIMIT_EXPR : NUMBER''' 494 | p[0] = ASTNode.Node(TK.TOK_VALUE,p[1],None) 495 | 496 | 497 | def p_LIMITS_EXPR(p): 498 | '''LIMITS_EXPR : LIMIT_EXPR 499 | | LIMIT_EXPR COMMA LIMIT_EXPR''' 500 | if len(p) == 2: 501 | p[0] = [p[1]] 502 | else: 503 | p[0] = [p[1],p[3]] 504 | 505 | 506 | def p_TOK_ORDERBY(p): 507 | '''TOK_ORDERBY : TOK_SORTS''' 508 | p[0] = ASTNode.Node(TK.TOK_ORDERBY,None,p[1]) 509 | 510 | 511 | def p_TOK_SORTS(p): 512 | '''TOK_SORTS : TOK_SORT 513 | | TOK_SORT COMMA TOK_SORT 514 | | TOK_SORT COMMA TOK_SORTS''' 515 | p[0] = token_list(p) 516 | 517 | 518 | def p_SORT_MODE(p): 519 | '''SORT_MODE : ASC 520 | | DESC''' 521 | p[0] = ASTNode.Node(TK.TOK_SORT_MODE,p[1],None) 522 | 523 | 524 | def p_TOK_SORT(p): 525 | '''TOK_SORT : LEFT_VALUE_EXPR 526 | | LEFT_VALUE_EXPR SORT_MODE''' 527 | if len(p) == 2: 528 | p[0] = ASTNode.Node(TK.TOK_SORT,None,[p[1]]) 529 | else: 530 | p[0] = ASTNode.Node(TK.TOK_SORT,None,[p[1],p[2]]) 531 | 532 | 533 | 534 | 535 | '''=================================Aggregations define========================================''' 536 | 537 | def p_TOK_QUERY_WITH_GROUPBY(p): 538 | '''TOK_QUERY : TOK_QUERY GROUP BY TOK_GROUPBY''' 539 | p[0] = p[1] 540 | p[0].append_children(p[4]) 541 | 542 | 543 | def p_TOK_GROUPBY(p): 544 | '''TOK_GROUPBY : LEFT_VALUES_EXPR''' 545 | p[0] = ASTNode.Node(TK.TOK_GROUPBY,None,p[1]) 546 | 547 | 548 | 549 | '''=================================Load data define========================================''' 550 | 551 | def p_TOK_INSERT_INTO(p): 552 | '''TOK_INSERT_INTO : INSERT INTO TOK_TABLE_NAME TOK_INSERT_COLUMNS VALUES TOK_VALUE_ROW''' 553 | p[0] = ASTNode.Node(TK.TOK_INSERT_INTO,None,[p[3]] + [p[4]] + [p[6]]) 554 | 555 | 556 | 557 | def p_TOK_INSERT_COLUMNS(p): 558 | '''TOK_INSERT_COLUMNS : TOK_TUPLE_OBJECT''' 559 | p[0] = ASTNode.Node(TK.TOK_INSERT_COLUMNS,None,p[1].get_children()) 560 | 561 | 562 | def p_TOK_INSERT_ROW(p): 563 | '''TOK_VALUE_ROW : "(" RIGHT_VALUES_EXPR ")" ''' 564 | p[0] = ASTNode.Node(TK.TOK_INSERT_ROW,None,p[2]) 565 | 566 | 567 | def p_INSERT_ROWS_EXPR(p): 568 | '''INSERT_ROWS_EXPR : TOK_VALUE_ROW 569 | | TOK_VALUE_ROW COMMA TOK_VALUE_ROW 570 | | TOK_VALUE_ROW COMMA INSERT_ROWS_EXPR''' 571 | p[0] = token_list(p) 572 | 573 | 574 | def p_TOK_BULK_INTO(p): 575 | '''TOK_BULK_INTO : BULK INTO TOK_TABLE_NAME TOK_INSERT_COLUMNS VALUES INSERT_ROWS_EXPR''' 576 | 577 | rows = ASTNode.Node(TK.TOK_INSERT_ROWS,None,p[6]) 578 | 579 | p[0] = ASTNode.Node(TK.TOK_BULK_INTO,None,[p[3]] + [p[4]] + [rows]) 580 | 581 | 582 | def p_TOK_UPDATE(p): 583 | '''TOK_UPDATE : UPDATE TOK_TABLE_NAME SET TOK_SET_COLUMNS_CLAUSE WHERE TOK_WHERE''' 584 | p[0] = ASTNode.Node(TK.TOK_UPDATE,None,[p[2]] + [p[4]] + [p[6]]) 585 | 586 | 587 | def p_TOK_SET_COLUMNS(p): 588 | '''TOK_SET_COLUMNS_CLAUSE : KV_ELEMENTS_EXPR''' 589 | p[0] = ASTNode.Node(TK.TOK_SET_COLUMNS_CLAUSE,None,p[1]) 590 | 591 | 592 | 593 | def p_TOK_UPSERT_INTO(p): 594 | '''TOK_UPSERT_INTO : UPSERT TOK_TABLE_NAME SET TOK_SET_COLUMNS_CLAUSE WHERE TOK_WHERE''' 595 | p[0] = ASTNode.Node(TK.TOK_UPSERT_INTO,None,[p[2]] + [p[4]] + [p[6]]) 596 | 597 | 598 | def p_TOK_DELETE(p): 599 | '''TOK_DELETE : DELETE FROM TOK_TABLE_NAME WHERE TOK_WHERE''' 600 | p[0] = ASTNode.Node(TK.TOK_DELETE,None,[p[3]] + [p[5]]) 601 | 602 | 603 | '''=================================show========================================''' 604 | 605 | 606 | def p_SHOW_TABLES(p): 607 | '''TOK_SHOW_TABLES : SHOW TABLES''' 608 | p[0] = ASTNode.Node(TK.TOK_SHOW_TABLES,None,None) 609 | 610 | 611 | '''=================================desc========================================''' 612 | def p_DESC_TABLE(p): 613 | '''TOK_DESC_TABLE : DESC TOK_TABLE_NAME''' 614 | p[0] = ASTNode.Node(TK.TOK_DESC_TABLE,None,[p[2]]) 615 | 616 | 617 | '''=================================drop========================================''' 618 | def p_DROP_TABLE(p): 619 | '''TOK_DROP_TABLE : DROP TABLE TOK_TABLE_NAME''' 620 | p[0] = ASTNode.Node(TK.TOK_DROP_TABLE,None,[p[3]]) 621 | 622 | 623 | 624 | def p_error(p): 625 | raise Exception("Illegal syntax") 626 | -------------------------------------------------------------------------------- /head/vendor.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | /* FONT PATH 6 | * -------------------------- */ 7 | @font-face { 8 | font-family: 'FontAwesome'; 9 | src: url('fonts/fontawesome-webfont.eot?v=4.0.3'); 10 | src: url('fonts/fontawesome-webfont.eot?#iefix&v=4.0.3') format('embedded-opentype'), url('fonts/fontawesome-webfont.woff?v=4.0.3') format('woff'), url('fonts/fontawesome-webfont.ttf?v=4.0.3') format('truetype'), url('fonts/fontawesome-webfont.svg?v=4.0.3#fontawesomeregular') format('svg'); 11 | font-weight: normal; 12 | font-style: normal; 13 | } 14 | .fa { 15 | display: inline-block; 16 | font-family: FontAwesome; 17 | font-style: normal; 18 | font-weight: normal; 19 | line-height: 1; 20 | -webkit-font-smoothing: antialiased; 21 | -moz-osx-font-smoothing: grayscale; 22 | } 23 | /* makes the font 33% larger relative to the icon container */ 24 | .fa-lg { 25 | font-size: 1.3333333333333333em; 26 | line-height: 0.75em; 27 | vertical-align: -15%; 28 | } 29 | .fa-2x { 30 | font-size: 2em; 31 | } 32 | .fa-3x { 33 | font-size: 3em; 34 | } 35 | .fa-4x { 36 | font-size: 4em; 37 | } 38 | .fa-5x { 39 | font-size: 5em; 40 | } 41 | .fa-fw { 42 | width: 1.2857142857142858em; 43 | text-align: center; 44 | } 45 | .fa-ul { 46 | padding-left: 0; 47 | margin-left: 2.142857142857143em; 48 | list-style-type: none; 49 | } 50 | .fa-ul > li { 51 | position: relative; 52 | } 53 | .fa-li { 54 | position: absolute; 55 | left: -2.142857142857143em; 56 | width: 2.142857142857143em; 57 | top: 0.14285714285714285em; 58 | text-align: center; 59 | } 60 | .fa-li.fa-lg { 61 | left: -1.8571428571428572em; 62 | } 63 | .fa-border { 64 | padding: .2em .25em .15em; 65 | border: solid 0.08em #eeeeee; 66 | border-radius: .1em; 67 | } 68 | .pull-right { 69 | float: right; 70 | } 71 | .pull-left { 72 | float: left; 73 | } 74 | .fa.pull-left { 75 | margin-right: .3em; 76 | } 77 | .fa.pull-right { 78 | margin-left: .3em; 79 | } 80 | .fa-spin { 81 | -webkit-animation: spin 2s infinite linear; 82 | -moz-animation: spin 2s infinite linear; 83 | -o-animation: spin 2s infinite linear; 84 | animation: spin 2s infinite linear; 85 | } 86 | @-moz-keyframes spin { 87 | 0% { 88 | -moz-transform: rotate(0deg); 89 | } 90 | 100% { 91 | -moz-transform: rotate(359deg); 92 | } 93 | } 94 | @-webkit-keyframes spin { 95 | 0% { 96 | -webkit-transform: rotate(0deg); 97 | } 98 | 100% { 99 | -webkit-transform: rotate(359deg); 100 | } 101 | } 102 | @-o-keyframes spin { 103 | 0% { 104 | -o-transform: rotate(0deg); 105 | } 106 | 100% { 107 | -o-transform: rotate(359deg); 108 | } 109 | } 110 | @-ms-keyframes spin { 111 | 0% { 112 | -ms-transform: rotate(0deg); 113 | } 114 | 100% { 115 | -ms-transform: rotate(359deg); 116 | } 117 | } 118 | @keyframes spin { 119 | 0% { 120 | transform: rotate(0deg); 121 | } 122 | 100% { 123 | transform: rotate(359deg); 124 | } 125 | } 126 | .fa-rotate-90 { 127 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 128 | -webkit-transform: rotate(90deg); 129 | -moz-transform: rotate(90deg); 130 | -ms-transform: rotate(90deg); 131 | -o-transform: rotate(90deg); 132 | transform: rotate(90deg); 133 | } 134 | .fa-rotate-180 { 135 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 136 | -webkit-transform: rotate(180deg); 137 | -moz-transform: rotate(180deg); 138 | -ms-transform: rotate(180deg); 139 | -o-transform: rotate(180deg); 140 | transform: rotate(180deg); 141 | } 142 | .fa-rotate-270 { 143 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 144 | -webkit-transform: rotate(270deg); 145 | -moz-transform: rotate(270deg); 146 | -ms-transform: rotate(270deg); 147 | -o-transform: rotate(270deg); 148 | transform: rotate(270deg); 149 | } 150 | .fa-flip-horizontal { 151 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1); 152 | -webkit-transform: scale(-1, 1); 153 | -moz-transform: scale(-1, 1); 154 | -ms-transform: scale(-1, 1); 155 | -o-transform: scale(-1, 1); 156 | transform: scale(-1, 1); 157 | } 158 | .fa-flip-vertical { 159 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1); 160 | -webkit-transform: scale(1, -1); 161 | -moz-transform: scale(1, -1); 162 | -ms-transform: scale(1, -1); 163 | -o-transform: scale(1, -1); 164 | transform: scale(1, -1); 165 | } 166 | .fa-stack { 167 | position: relative; 168 | display: inline-block; 169 | width: 2em; 170 | height: 2em; 171 | line-height: 2em; 172 | vertical-align: middle; 173 | } 174 | .fa-stack-1x, 175 | .fa-stack-2x { 176 | position: absolute; 177 | left: 0; 178 | width: 100%; 179 | text-align: center; 180 | } 181 | .fa-stack-1x { 182 | line-height: inherit; 183 | } 184 | .fa-stack-2x { 185 | font-size: 2em; 186 | } 187 | .fa-inverse { 188 | color: #ffffff; 189 | } 190 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 191 | readers do not read off random characters that represent icons */ 192 | .fa-glass:before { 193 | content: "\f000"; 194 | } 195 | .fa-music:before { 196 | content: "\f001"; 197 | } 198 | .fa-search:before { 199 | content: "\f002"; 200 | } 201 | .fa-envelope-o:before { 202 | content: "\f003"; 203 | } 204 | .fa-heart:before { 205 | content: "\f004"; 206 | } 207 | .fa-star:before { 208 | content: "\f005"; 209 | } 210 | .fa-star-o:before { 211 | content: "\f006"; 212 | } 213 | .fa-user:before { 214 | content: "\f007"; 215 | } 216 | .fa-film:before { 217 | content: "\f008"; 218 | } 219 | .fa-th-large:before { 220 | content: "\f009"; 221 | } 222 | .fa-th:before { 223 | content: "\f00a"; 224 | } 225 | .fa-th-list:before { 226 | content: "\f00b"; 227 | } 228 | .fa-check:before { 229 | content: "\f00c"; 230 | } 231 | .fa-times:before { 232 | content: "\f00d"; 233 | } 234 | .fa-search-plus:before { 235 | content: "\f00e"; 236 | } 237 | .fa-search-minus:before { 238 | content: "\f010"; 239 | } 240 | .fa-power-off:before { 241 | content: "\f011"; 242 | } 243 | .fa-signal:before { 244 | content: "\f012"; 245 | } 246 | .fa-gear:before, 247 | .fa-cog:before { 248 | content: "\f013"; 249 | } 250 | .fa-trash-o:before { 251 | content: "\f014"; 252 | } 253 | .fa-home:before { 254 | content: "\f015"; 255 | } 256 | .fa-file-o:before { 257 | content: "\f016"; 258 | } 259 | .fa-clock-o:before { 260 | content: "\f017"; 261 | } 262 | .fa-road:before { 263 | content: "\f018"; 264 | } 265 | .fa-download:before { 266 | content: "\f019"; 267 | } 268 | .fa-arrow-circle-o-down:before { 269 | content: "\f01a"; 270 | } 271 | .fa-arrow-circle-o-up:before { 272 | content: "\f01b"; 273 | } 274 | .fa-inbox:before { 275 | content: "\f01c"; 276 | } 277 | .fa-play-circle-o:before { 278 | content: "\f01d"; 279 | } 280 | .fa-rotate-right:before, 281 | .fa-repeat:before { 282 | content: "\f01e"; 283 | } 284 | .fa-refresh:before { 285 | content: "\f021"; 286 | } 287 | .fa-list-alt:before { 288 | content: "\f022"; 289 | } 290 | .fa-lock:before { 291 | content: "\f023"; 292 | } 293 | .fa-flag:before { 294 | content: "\f024"; 295 | } 296 | .fa-headphones:before { 297 | content: "\f025"; 298 | } 299 | .fa-volume-off:before { 300 | content: "\f026"; 301 | } 302 | .fa-volume-down:before { 303 | content: "\f027"; 304 | } 305 | .fa-volume-up:before { 306 | content: "\f028"; 307 | } 308 | .fa-qrcode:before { 309 | content: "\f029"; 310 | } 311 | .fa-barcode:before { 312 | content: "\f02a"; 313 | } 314 | .fa-tag:before { 315 | content: "\f02b"; 316 | } 317 | .fa-tags:before { 318 | content: "\f02c"; 319 | } 320 | .fa-book:before { 321 | content: "\f02d"; 322 | } 323 | .fa-bookmark:before { 324 | content: "\f02e"; 325 | } 326 | .fa-print:before { 327 | content: "\f02f"; 328 | } 329 | .fa-camera:before { 330 | content: "\f030"; 331 | } 332 | .fa-font:before { 333 | content: "\f031"; 334 | } 335 | .fa-bold:before { 336 | content: "\f032"; 337 | } 338 | .fa-italic:before { 339 | content: "\f033"; 340 | } 341 | .fa-text-height:before { 342 | content: "\f034"; 343 | } 344 | .fa-text-width:before { 345 | content: "\f035"; 346 | } 347 | .fa-align-left:before { 348 | content: "\f036"; 349 | } 350 | .fa-align-center:before { 351 | content: "\f037"; 352 | } 353 | .fa-align-right:before { 354 | content: "\f038"; 355 | } 356 | .fa-align-justify:before { 357 | content: "\f039"; 358 | } 359 | .fa-list:before { 360 | content: "\f03a"; 361 | } 362 | .fa-dedent:before, 363 | .fa-outdent:before { 364 | content: "\f03b"; 365 | } 366 | .fa-indent:before { 367 | content: "\f03c"; 368 | } 369 | .fa-video-camera:before { 370 | content: "\f03d"; 371 | } 372 | .fa-picture-o:before { 373 | content: "\f03e"; 374 | } 375 | .fa-pencil:before { 376 | content: "\f040"; 377 | } 378 | .fa-map-marker:before { 379 | content: "\f041"; 380 | } 381 | .fa-adjust:before { 382 | content: "\f042"; 383 | } 384 | .fa-tint:before { 385 | content: "\f043"; 386 | } 387 | .fa-edit:before, 388 | .fa-pencil-square-o:before { 389 | content: "\f044"; 390 | } 391 | .fa-share-square-o:before { 392 | content: "\f045"; 393 | } 394 | .fa-check-square-o:before { 395 | content: "\f046"; 396 | } 397 | .fa-arrows:before { 398 | content: "\f047"; 399 | } 400 | .fa-step-backward:before { 401 | content: "\f048"; 402 | } 403 | .fa-fast-backward:before { 404 | content: "\f049"; 405 | } 406 | .fa-backward:before { 407 | content: "\f04a"; 408 | } 409 | .fa-play:before { 410 | content: "\f04b"; 411 | } 412 | .fa-pause:before { 413 | content: "\f04c"; 414 | } 415 | .fa-stop:before { 416 | content: "\f04d"; 417 | } 418 | .fa-forward:before { 419 | content: "\f04e"; 420 | } 421 | .fa-fast-forward:before { 422 | content: "\f050"; 423 | } 424 | .fa-step-forward:before { 425 | content: "\f051"; 426 | } 427 | .fa-eject:before { 428 | content: "\f052"; 429 | } 430 | .fa-chevron-left:before { 431 | content: "\f053"; 432 | } 433 | .fa-chevron-right:before { 434 | content: "\f054"; 435 | } 436 | .fa-plus-circle:before { 437 | content: "\f055"; 438 | } 439 | .fa-minus-circle:before { 440 | content: "\f056"; 441 | } 442 | .fa-times-circle:before { 443 | content: "\f057"; 444 | } 445 | .fa-check-circle:before { 446 | content: "\f058"; 447 | } 448 | .fa-question-circle:before { 449 | content: "\f059"; 450 | } 451 | .fa-info-circle:before { 452 | content: "\f05a"; 453 | } 454 | .fa-crosshairs:before { 455 | content: "\f05b"; 456 | } 457 | .fa-times-circle-o:before { 458 | content: "\f05c"; 459 | } 460 | .fa-check-circle-o:before { 461 | content: "\f05d"; 462 | } 463 | .fa-ban:before { 464 | content: "\f05e"; 465 | } 466 | .fa-arrow-left:before { 467 | content: "\f060"; 468 | } 469 | .fa-arrow-right:before { 470 | content: "\f061"; 471 | } 472 | .fa-arrow-up:before { 473 | content: "\f062"; 474 | } 475 | .fa-arrow-down:before { 476 | content: "\f063"; 477 | } 478 | .fa-mail-forward:before, 479 | .fa-share:before { 480 | content: "\f064"; 481 | } 482 | .fa-expand:before { 483 | content: "\f065"; 484 | } 485 | .fa-compress:before { 486 | content: "\f066"; 487 | } 488 | .fa-plus:before { 489 | content: "\f067"; 490 | } 491 | .fa-minus:before { 492 | content: "\f068"; 493 | } 494 | .fa-asterisk:before { 495 | content: "\f069"; 496 | } 497 | .fa-exclamation-circle:before { 498 | content: "\f06a"; 499 | } 500 | .fa-gift:before { 501 | content: "\f06b"; 502 | } 503 | .fa-leaf:before { 504 | content: "\f06c"; 505 | } 506 | .fa-fire:before { 507 | content: "\f06d"; 508 | } 509 | .fa-eye:before { 510 | content: "\f06e"; 511 | } 512 | .fa-eye-slash:before { 513 | content: "\f070"; 514 | } 515 | .fa-warning:before, 516 | .fa-exclamation-triangle:before { 517 | content: "\f071"; 518 | } 519 | .fa-plane:before { 520 | content: "\f072"; 521 | } 522 | .fa-calendar:before { 523 | content: "\f073"; 524 | } 525 | .fa-random:before { 526 | content: "\f074"; 527 | } 528 | .fa-comment:before { 529 | content: "\f075"; 530 | } 531 | .fa-magnet:before { 532 | content: "\f076"; 533 | } 534 | .fa-chevron-up:before { 535 | content: "\f077"; 536 | } 537 | .fa-chevron-down:before { 538 | content: "\f078"; 539 | } 540 | .fa-retweet:before { 541 | content: "\f079"; 542 | } 543 | .fa-shopping-cart:before { 544 | content: "\f07a"; 545 | } 546 | .fa-folder:before { 547 | content: "\f07b"; 548 | } 549 | .fa-folder-open:before { 550 | content: "\f07c"; 551 | } 552 | .fa-arrows-v:before { 553 | content: "\f07d"; 554 | } 555 | .fa-arrows-h:before { 556 | content: "\f07e"; 557 | } 558 | .fa-bar-chart-o:before { 559 | content: "\f080"; 560 | } 561 | .fa-twitter-square:before { 562 | content: "\f081"; 563 | } 564 | .fa-facebook-square:before { 565 | content: "\f082"; 566 | } 567 | .fa-camera-retro:before { 568 | content: "\f083"; 569 | } 570 | .fa-key:before { 571 | content: "\f084"; 572 | } 573 | .fa-gears:before, 574 | .fa-cogs:before { 575 | content: "\f085"; 576 | } 577 | .fa-comments:before { 578 | content: "\f086"; 579 | } 580 | .fa-thumbs-o-up:before { 581 | content: "\f087"; 582 | } 583 | .fa-thumbs-o-down:before { 584 | content: "\f088"; 585 | } 586 | .fa-star-half:before { 587 | content: "\f089"; 588 | } 589 | .fa-heart-o:before { 590 | content: "\f08a"; 591 | } 592 | .fa-sign-out:before { 593 | content: "\f08b"; 594 | } 595 | .fa-linkedin-square:before { 596 | content: "\f08c"; 597 | } 598 | .fa-thumb-tack:before { 599 | content: "\f08d"; 600 | } 601 | .fa-external-link:before { 602 | content: "\f08e"; 603 | } 604 | .fa-sign-in:before { 605 | content: "\f090"; 606 | } 607 | .fa-trophy:before { 608 | content: "\f091"; 609 | } 610 | .fa-github-square:before { 611 | content: "\f092"; 612 | } 613 | .fa-upload:before { 614 | content: "\f093"; 615 | } 616 | .fa-lemon-o:before { 617 | content: "\f094"; 618 | } 619 | .fa-phone:before { 620 | content: "\f095"; 621 | } 622 | .fa-square-o:before { 623 | content: "\f096"; 624 | } 625 | .fa-bookmark-o:before { 626 | content: "\f097"; 627 | } 628 | .fa-phone-square:before { 629 | content: "\f098"; 630 | } 631 | .fa-twitter:before { 632 | content: "\f099"; 633 | } 634 | .fa-facebook:before { 635 | content: "\f09a"; 636 | } 637 | .fa-github:before { 638 | content: "\f09b"; 639 | } 640 | .fa-unlock:before { 641 | content: "\f09c"; 642 | } 643 | .fa-credit-card:before { 644 | content: "\f09d"; 645 | } 646 | .fa-rss:before { 647 | content: "\f09e"; 648 | } 649 | .fa-hdd-o:before { 650 | content: "\f0a0"; 651 | } 652 | .fa-bullhorn:before { 653 | content: "\f0a1"; 654 | } 655 | .fa-bell:before { 656 | content: "\f0f3"; 657 | } 658 | .fa-certificate:before { 659 | content: "\f0a3"; 660 | } 661 | .fa-hand-o-right:before { 662 | content: "\f0a4"; 663 | } 664 | .fa-hand-o-left:before { 665 | content: "\f0a5"; 666 | } 667 | .fa-hand-o-up:before { 668 | content: "\f0a6"; 669 | } 670 | .fa-hand-o-down:before { 671 | content: "\f0a7"; 672 | } 673 | .fa-arrow-circle-left:before { 674 | content: "\f0a8"; 675 | } 676 | .fa-arrow-circle-right:before { 677 | content: "\f0a9"; 678 | } 679 | .fa-arrow-circle-up:before { 680 | content: "\f0aa"; 681 | } 682 | .fa-arrow-circle-down:before { 683 | content: "\f0ab"; 684 | } 685 | .fa-globe:before { 686 | content: "\f0ac"; 687 | } 688 | .fa-wrench:before { 689 | content: "\f0ad"; 690 | } 691 | .fa-tasks:before { 692 | content: "\f0ae"; 693 | } 694 | .fa-filter:before { 695 | content: "\f0b0"; 696 | } 697 | .fa-briefcase:before { 698 | content: "\f0b1"; 699 | } 700 | .fa-arrows-alt:before { 701 | content: "\f0b2"; 702 | } 703 | .fa-group:before, 704 | .fa-users:before { 705 | content: "\f0c0"; 706 | } 707 | .fa-chain:before, 708 | .fa-link:before { 709 | content: "\f0c1"; 710 | } 711 | .fa-cloud:before { 712 | content: "\f0c2"; 713 | } 714 | .fa-flask:before { 715 | content: "\f0c3"; 716 | } 717 | .fa-cut:before, 718 | .fa-scissors:before { 719 | content: "\f0c4"; 720 | } 721 | .fa-copy:before, 722 | .fa-files-o:before { 723 | content: "\f0c5"; 724 | } 725 | .fa-paperclip:before { 726 | content: "\f0c6"; 727 | } 728 | .fa-save:before, 729 | .fa-floppy-o:before { 730 | content: "\f0c7"; 731 | } 732 | .fa-square:before { 733 | content: "\f0c8"; 734 | } 735 | .fa-bars:before { 736 | content: "\f0c9"; 737 | } 738 | .fa-list-ul:before { 739 | content: "\f0ca"; 740 | } 741 | .fa-list-ol:before { 742 | content: "\f0cb"; 743 | } 744 | .fa-strikethrough:before { 745 | content: "\f0cc"; 746 | } 747 | .fa-underline:before { 748 | content: "\f0cd"; 749 | } 750 | .fa-table:before { 751 | content: "\f0ce"; 752 | } 753 | .fa-magic:before { 754 | content: "\f0d0"; 755 | } 756 | .fa-truck:before { 757 | content: "\f0d1"; 758 | } 759 | .fa-pinterest:before { 760 | content: "\f0d2"; 761 | } 762 | .fa-pinterest-square:before { 763 | content: "\f0d3"; 764 | } 765 | .fa-google-plus-square:before { 766 | content: "\f0d4"; 767 | } 768 | .fa-google-plus:before { 769 | content: "\f0d5"; 770 | } 771 | .fa-money:before { 772 | content: "\f0d6"; 773 | } 774 | .fa-caret-down:before { 775 | content: "\f0d7"; 776 | } 777 | .fa-caret-up:before { 778 | content: "\f0d8"; 779 | } 780 | .fa-caret-left:before { 781 | content: "\f0d9"; 782 | } 783 | .fa-caret-right:before { 784 | content: "\f0da"; 785 | } 786 | .fa-columns:before { 787 | content: "\f0db"; 788 | } 789 | .fa-unsorted:before, 790 | .fa-sort:before { 791 | content: "\f0dc"; 792 | } 793 | .fa-sort-down:before, 794 | .fa-sort-asc:before { 795 | content: "\f0dd"; 796 | } 797 | .fa-sort-up:before, 798 | .fa-sort-desc:before { 799 | content: "\f0de"; 800 | } 801 | .fa-envelope:before { 802 | content: "\f0e0"; 803 | } 804 | .fa-linkedin:before { 805 | content: "\f0e1"; 806 | } 807 | .fa-rotate-left:before, 808 | .fa-undo:before { 809 | content: "\f0e2"; 810 | } 811 | .fa-legal:before, 812 | .fa-gavel:before { 813 | content: "\f0e3"; 814 | } 815 | .fa-dashboard:before, 816 | .fa-tachometer:before { 817 | content: "\f0e4"; 818 | } 819 | .fa-comment-o:before { 820 | content: "\f0e5"; 821 | } 822 | .fa-comments-o:before { 823 | content: "\f0e6"; 824 | } 825 | .fa-flash:before, 826 | .fa-bolt:before { 827 | content: "\f0e7"; 828 | } 829 | .fa-sitemap:before { 830 | content: "\f0e8"; 831 | } 832 | .fa-umbrella:before { 833 | content: "\f0e9"; 834 | } 835 | .fa-paste:before, 836 | .fa-clipboard:before { 837 | content: "\f0ea"; 838 | } 839 | .fa-lightbulb-o:before { 840 | content: "\f0eb"; 841 | } 842 | .fa-exchange:before { 843 | content: "\f0ec"; 844 | } 845 | .fa-cloud-download:before { 846 | content: "\f0ed"; 847 | } 848 | .fa-cloud-upload:before { 849 | content: "\f0ee"; 850 | } 851 | .fa-user-md:before { 852 | content: "\f0f0"; 853 | } 854 | .fa-stethoscope:before { 855 | content: "\f0f1"; 856 | } 857 | .fa-suitcase:before { 858 | content: "\f0f2"; 859 | } 860 | .fa-bell-o:before { 861 | content: "\f0a2"; 862 | } 863 | .fa-coffee:before { 864 | content: "\f0f4"; 865 | } 866 | .fa-cutlery:before { 867 | content: "\f0f5"; 868 | } 869 | .fa-file-text-o:before { 870 | content: "\f0f6"; 871 | } 872 | .fa-building-o:before { 873 | content: "\f0f7"; 874 | } 875 | .fa-hospital-o:before { 876 | content: "\f0f8"; 877 | } 878 | .fa-ambulance:before { 879 | content: "\f0f9"; 880 | } 881 | .fa-medkit:before { 882 | content: "\f0fa"; 883 | } 884 | .fa-fighter-jet:before { 885 | content: "\f0fb"; 886 | } 887 | .fa-beer:before { 888 | content: "\f0fc"; 889 | } 890 | .fa-h-square:before { 891 | content: "\f0fd"; 892 | } 893 | .fa-plus-square:before { 894 | content: "\f0fe"; 895 | } 896 | .fa-angle-double-left:before { 897 | content: "\f100"; 898 | } 899 | .fa-angle-double-right:before { 900 | content: "\f101"; 901 | } 902 | .fa-angle-double-up:before { 903 | content: "\f102"; 904 | } 905 | .fa-angle-double-down:before { 906 | content: "\f103"; 907 | } 908 | .fa-angle-left:before { 909 | content: "\f104"; 910 | } 911 | .fa-angle-right:before { 912 | content: "\f105"; 913 | } 914 | .fa-angle-up:before { 915 | content: "\f106"; 916 | } 917 | .fa-angle-down:before { 918 | content: "\f107"; 919 | } 920 | .fa-desktop:before { 921 | content: "\f108"; 922 | } 923 | .fa-laptop:before { 924 | content: "\f109"; 925 | } 926 | .fa-tablet:before { 927 | content: "\f10a"; 928 | } 929 | .fa-mobile-phone:before, 930 | .fa-mobile:before { 931 | content: "\f10b"; 932 | } 933 | .fa-circle-o:before { 934 | content: "\f10c"; 935 | } 936 | .fa-quote-left:before { 937 | content: "\f10d"; 938 | } 939 | .fa-quote-right:before { 940 | content: "\f10e"; 941 | } 942 | .fa-spinner:before { 943 | content: "\f110"; 944 | } 945 | .fa-circle:before { 946 | content: "\f111"; 947 | } 948 | .fa-mail-reply:before, 949 | .fa-reply:before { 950 | content: "\f112"; 951 | } 952 | .fa-github-alt:before { 953 | content: "\f113"; 954 | } 955 | .fa-folder-o:before { 956 | content: "\f114"; 957 | } 958 | .fa-folder-open-o:before { 959 | content: "\f115"; 960 | } 961 | .fa-smile-o:before { 962 | content: "\f118"; 963 | } 964 | .fa-frown-o:before { 965 | content: "\f119"; 966 | } 967 | .fa-meh-o:before { 968 | content: "\f11a"; 969 | } 970 | .fa-gamepad:before { 971 | content: "\f11b"; 972 | } 973 | .fa-keyboard-o:before { 974 | content: "\f11c"; 975 | } 976 | .fa-flag-o:before { 977 | content: "\f11d"; 978 | } 979 | .fa-flag-checkered:before { 980 | content: "\f11e"; 981 | } 982 | .fa-terminal:before { 983 | content: "\f120"; 984 | } 985 | .fa-code:before { 986 | content: "\f121"; 987 | } 988 | .fa-reply-all:before { 989 | content: "\f122"; 990 | } 991 | .fa-mail-reply-all:before { 992 | content: "\f122"; 993 | } 994 | .fa-star-half-empty:before, 995 | .fa-star-half-full:before, 996 | .fa-star-half-o:before { 997 | content: "\f123"; 998 | } 999 | .fa-location-arrow:before { 1000 | content: "\f124"; 1001 | } 1002 | .fa-crop:before { 1003 | content: "\f125"; 1004 | } 1005 | .fa-code-fork:before { 1006 | content: "\f126"; 1007 | } 1008 | .fa-unlink:before, 1009 | .fa-chain-broken:before { 1010 | content: "\f127"; 1011 | } 1012 | .fa-question:before { 1013 | content: "\f128"; 1014 | } 1015 | .fa-info:before { 1016 | content: "\f129"; 1017 | } 1018 | .fa-exclamation:before { 1019 | content: "\f12a"; 1020 | } 1021 | .fa-superscript:before { 1022 | content: "\f12b"; 1023 | } 1024 | .fa-subscript:before { 1025 | content: "\f12c"; 1026 | } 1027 | .fa-eraser:before { 1028 | content: "\f12d"; 1029 | } 1030 | .fa-puzzle-piece:before { 1031 | content: "\f12e"; 1032 | } 1033 | .fa-microphone:before { 1034 | content: "\f130"; 1035 | } 1036 | .fa-microphone-slash:before { 1037 | content: "\f131"; 1038 | } 1039 | .fa-shield:before { 1040 | content: "\f132"; 1041 | } 1042 | .fa-calendar-o:before { 1043 | content: "\f133"; 1044 | } 1045 | .fa-fire-extinguisher:before { 1046 | content: "\f134"; 1047 | } 1048 | .fa-rocket:before { 1049 | content: "\f135"; 1050 | } 1051 | .fa-maxcdn:before { 1052 | content: "\f136"; 1053 | } 1054 | .fa-chevron-circle-left:before { 1055 | content: "\f137"; 1056 | } 1057 | .fa-chevron-circle-right:before { 1058 | content: "\f138"; 1059 | } 1060 | .fa-chevron-circle-up:before { 1061 | content: "\f139"; 1062 | } 1063 | .fa-chevron-circle-down:before { 1064 | content: "\f13a"; 1065 | } 1066 | .fa-html5:before { 1067 | content: "\f13b"; 1068 | } 1069 | .fa-css3:before { 1070 | content: "\f13c"; 1071 | } 1072 | .fa-anchor:before { 1073 | content: "\f13d"; 1074 | } 1075 | .fa-unlock-alt:before { 1076 | content: "\f13e"; 1077 | } 1078 | .fa-bullseye:before { 1079 | content: "\f140"; 1080 | } 1081 | .fa-ellipsis-h:before { 1082 | content: "\f141"; 1083 | } 1084 | .fa-ellipsis-v:before { 1085 | content: "\f142"; 1086 | } 1087 | .fa-rss-square:before { 1088 | content: "\f143"; 1089 | } 1090 | .fa-play-circle:before { 1091 | content: "\f144"; 1092 | } 1093 | .fa-ticket:before { 1094 | content: "\f145"; 1095 | } 1096 | .fa-minus-square:before { 1097 | content: "\f146"; 1098 | } 1099 | .fa-minus-square-o:before { 1100 | content: "\f147"; 1101 | } 1102 | .fa-level-up:before { 1103 | content: "\f148"; 1104 | } 1105 | .fa-level-down:before { 1106 | content: "\f149"; 1107 | } 1108 | .fa-check-square:before { 1109 | content: "\f14a"; 1110 | } 1111 | .fa-pencil-square:before { 1112 | content: "\f14b"; 1113 | } 1114 | .fa-external-link-square:before { 1115 | content: "\f14c"; 1116 | } 1117 | .fa-share-square:before { 1118 | content: "\f14d"; 1119 | } 1120 | .fa-compass:before { 1121 | content: "\f14e"; 1122 | } 1123 | .fa-toggle-down:before, 1124 | .fa-caret-square-o-down:before { 1125 | content: "\f150"; 1126 | } 1127 | .fa-toggle-up:before, 1128 | .fa-caret-square-o-up:before { 1129 | content: "\f151"; 1130 | } 1131 | .fa-toggle-right:before, 1132 | .fa-caret-square-o-right:before { 1133 | content: "\f152"; 1134 | } 1135 | .fa-euro:before, 1136 | .fa-eur:before { 1137 | content: "\f153"; 1138 | } 1139 | .fa-gbp:before { 1140 | content: "\f154"; 1141 | } 1142 | .fa-dollar:before, 1143 | .fa-usd:before { 1144 | content: "\f155"; 1145 | } 1146 | .fa-rupee:before, 1147 | .fa-inr:before { 1148 | content: "\f156"; 1149 | } 1150 | .fa-cny:before, 1151 | .fa-rmb:before, 1152 | .fa-yen:before, 1153 | .fa-jpy:before { 1154 | content: "\f157"; 1155 | } 1156 | .fa-ruble:before, 1157 | .fa-rouble:before, 1158 | .fa-rub:before { 1159 | content: "\f158"; 1160 | } 1161 | .fa-won:before, 1162 | .fa-krw:before { 1163 | content: "\f159"; 1164 | } 1165 | .fa-bitcoin:before, 1166 | .fa-btc:before { 1167 | content: "\f15a"; 1168 | } 1169 | .fa-file:before { 1170 | content: "\f15b"; 1171 | } 1172 | .fa-file-text:before { 1173 | content: "\f15c"; 1174 | } 1175 | .fa-sort-alpha-asc:before { 1176 | content: "\f15d"; 1177 | } 1178 | .fa-sort-alpha-desc:before { 1179 | content: "\f15e"; 1180 | } 1181 | .fa-sort-amount-asc:before { 1182 | content: "\f160"; 1183 | } 1184 | .fa-sort-amount-desc:before { 1185 | content: "\f161"; 1186 | } 1187 | .fa-sort-numeric-asc:before { 1188 | content: "\f162"; 1189 | } 1190 | .fa-sort-numeric-desc:before { 1191 | content: "\f163"; 1192 | } 1193 | .fa-thumbs-up:before { 1194 | content: "\f164"; 1195 | } 1196 | .fa-thumbs-down:before { 1197 | content: "\f165"; 1198 | } 1199 | .fa-youtube-square:before { 1200 | content: "\f166"; 1201 | } 1202 | .fa-youtube:before { 1203 | content: "\f167"; 1204 | } 1205 | .fa-xing:before { 1206 | content: "\f168"; 1207 | } 1208 | .fa-xing-square:before { 1209 | content: "\f169"; 1210 | } 1211 | .fa-youtube-play:before { 1212 | content: "\f16a"; 1213 | } 1214 | .fa-dropbox:before { 1215 | content: "\f16b"; 1216 | } 1217 | .fa-stack-overflow:before { 1218 | content: "\f16c"; 1219 | } 1220 | .fa-instagram:before { 1221 | content: "\f16d"; 1222 | } 1223 | .fa-flickr:before { 1224 | content: "\f16e"; 1225 | } 1226 | .fa-adn:before { 1227 | content: "\f170"; 1228 | } 1229 | .fa-bitbucket:before { 1230 | content: "\f171"; 1231 | } 1232 | .fa-bitbucket-square:before { 1233 | content: "\f172"; 1234 | } 1235 | .fa-tumblr:before { 1236 | content: "\f173"; 1237 | } 1238 | .fa-tumblr-square:before { 1239 | content: "\f174"; 1240 | } 1241 | .fa-long-arrow-down:before { 1242 | content: "\f175"; 1243 | } 1244 | .fa-long-arrow-up:before { 1245 | content: "\f176"; 1246 | } 1247 | .fa-long-arrow-left:before { 1248 | content: "\f177"; 1249 | } 1250 | .fa-long-arrow-right:before { 1251 | content: "\f178"; 1252 | } 1253 | .fa-apple:before { 1254 | content: "\f179"; 1255 | } 1256 | .fa-windows:before { 1257 | content: "\f17a"; 1258 | } 1259 | .fa-android:before { 1260 | content: "\f17b"; 1261 | } 1262 | .fa-linux:before { 1263 | content: "\f17c"; 1264 | } 1265 | .fa-dribbble:before { 1266 | content: "\f17d"; 1267 | } 1268 | .fa-skype:before { 1269 | content: "\f17e"; 1270 | } 1271 | .fa-foursquare:before { 1272 | content: "\f180"; 1273 | } 1274 | .fa-trello:before { 1275 | content: "\f181"; 1276 | } 1277 | .fa-female:before { 1278 | content: "\f182"; 1279 | } 1280 | .fa-male:before { 1281 | content: "\f183"; 1282 | } 1283 | .fa-gittip:before { 1284 | content: "\f184"; 1285 | } 1286 | .fa-sun-o:before { 1287 | content: "\f185"; 1288 | } 1289 | .fa-moon-o:before { 1290 | content: "\f186"; 1291 | } 1292 | .fa-archive:before { 1293 | content: "\f187"; 1294 | } 1295 | .fa-bug:before { 1296 | content: "\f188"; 1297 | } 1298 | .fa-vk:before { 1299 | content: "\f189"; 1300 | } 1301 | .fa-weibo:before { 1302 | content: "\f18a"; 1303 | } 1304 | .fa-renren:before { 1305 | content: "\f18b"; 1306 | } 1307 | .fa-pagelines:before { 1308 | content: "\f18c"; 1309 | } 1310 | .fa-stack-exchange:before { 1311 | content: "\f18d"; 1312 | } 1313 | .fa-arrow-circle-o-right:before { 1314 | content: "\f18e"; 1315 | } 1316 | .fa-arrow-circle-o-left:before { 1317 | content: "\f190"; 1318 | } 1319 | .fa-toggle-left:before, 1320 | .fa-caret-square-o-left:before { 1321 | content: "\f191"; 1322 | } 1323 | .fa-dot-circle-o:before { 1324 | content: "\f192"; 1325 | } 1326 | .fa-wheelchair:before { 1327 | content: "\f193"; 1328 | } 1329 | .fa-vimeo-square:before { 1330 | content: "\f194"; 1331 | } 1332 | .fa-turkish-lira:before, 1333 | .fa-try:before { 1334 | content: "\f195"; 1335 | } 1336 | .fa-plus-square-o:before { 1337 | content: "\f196"; 1338 | } 1339 | --------------------------------------------------------------------------------