├── .gitignore ├── README.md ├── lib └── resty │ ├── elasticsearch.lua │ └── es │ ├── cat.lua │ ├── cluster.lua │ ├── indices.lua │ ├── nodes.lua │ ├── snapshot.lua │ └── utils.lua └── t └── search.t /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.swo 3 | *~ 4 | go 5 | t/servroot/ 6 | reindex 7 | *.t_ 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-resty-elasticsearch 2 | 3 | ElasticSearch client for for [OpenResty](http://openresty.org/) / [ngx_lua](https://github.com/openresty/lua-nginx-module). 4 | 5 | # Requirements 6 | 7 | [lua-resty-http](https://github.com/pintsized/lua-resty-http) 8 | 9 | # API 10 | 11 | * [new](#new) 12 | * [ping](#ping) 13 | * [info](#info) 14 | * [search](#search) 15 | 16 | ## Synopsis 17 | 18 | ``` lua 19 | lua_package_path "/path/to/lua-resty-http,lua-resty-elasticsearch/lib/?.lua;;"; 20 | 21 | server { 22 | location /test_es { 23 | content_by_lua ' 24 | local cjson = require "cjson" 25 | local elasticsearch = require "resty.elasticsearch" 26 | local es = elasticsearch:new({"http://localhost:9200"}) 27 | 28 | local data, err = es:info() 29 | if not data then 30 | ngx.say("info err: ", err) 31 | else 32 | ngx.say(cjson.encode(data)) 33 | end 34 | ngx.say("---------------------------") 35 | 36 | local body = {query={match_all={}}} 37 | local data, err = es:search({doc_type="products"}) 38 | if not data then 39 | ngx.say("search err: ", err) 40 | else 41 | ngx.say(cjson.encode(data)) 42 | end 43 | ngx.say("---------------------------") 44 | 45 | local data, err = es.cat:health() 46 | if not data then 47 | ngx.say("cat health err: ", err) 48 | else 49 | ngx.say(data) 50 | end 51 | '; 52 | } 53 | } 54 | ``` 55 | 56 | ## new 57 | 58 | `syntax: es = elasticsearch:new()` 59 | 60 | Creates the elasticsearch object. 61 | 62 | ## ping 63 | 64 | `syntax: ok, err = es:ping()` 65 | 66 | Returns True if the cluster is up, False otherwise. 67 | 68 | ## info 69 | 70 | `syntax: data, err = es:info()` 71 | 72 | Get the basic info from the current cluster. 73 | 74 | ## search 75 | 76 | `syntax: data, err = es:search{index="index", doc_type="user", body={query={match_all={}}}}` 77 | 78 | Execute a search query and get back search hits that match the query. 79 | 80 | # Copyright and License 81 | 82 | This module is licensed under the 2-clause BSD license. 83 | 84 | Copyright (c) 2015-2016, by bells 85 | 86 | All rights reserved. 87 | 88 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 89 | 90 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 91 | 92 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 93 | 94 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 95 | -------------------------------------------------------------------------------- /lib/resty/elasticsearch.lua: -------------------------------------------------------------------------------- 1 | local cjson = require "cjson" 2 | local http = require "resty.http" 3 | 4 | local es_utils = require "resty.es.utils" 5 | local es_cat = require "resty.es.cat" 6 | local es_cluster = require "resty.es.cluster" 7 | local es_indices = require "resty.es.indices" 8 | local es_nodes = require "resty.es.nodes" 9 | local es_snapshot = require "resty.es.snapshot" 10 | 11 | local str_find = string.find 12 | local str_sub = string.sub 13 | local math_random = math.random 14 | local make_path = es_utils.make_path 15 | local get_err_str = es_utils.get_err_str 16 | local deal_params = es_utils.deal_params 17 | 18 | 19 | local _M = { 20 | _VERSION = '0.01' 21 | } 22 | 23 | 24 | local mt = { __index = _M } 25 | 26 | 27 | function _M.new(self, hosts) 28 | if not hosts then 29 | hosts = {'http://localhost:9200'} 30 | end 31 | 32 | local es = { 33 | hosts_len = #hosts, 34 | hosts = hosts, 35 | } 36 | es.cat = es_cat:new(es) 37 | es.cluster = es_cluster:new(es) 38 | es.indices = es_indices:new(es) 39 | es.nodes = es_nodes:new(es) 40 | es.snapshot = es_snapshot:new(es) 41 | 42 | return setmetatable(es, mt) 43 | end 44 | 45 | 46 | function _M._perform_request(self, http_method, url, params, body) 47 | local temp_index = math_random(self.hosts_len) 48 | local host = self.hosts[temp_index] 49 | local http_c = http.new() 50 | 51 | url = host .. url 52 | if params then 53 | url = url .. ngx.encode_args(params) 54 | end 55 | if body then 56 | body = cjson.encode(body) 57 | else 58 | body = '' 59 | end 60 | 61 | local res, err = http_c:request_uri(url, {method = http_method, body = body}) 62 | if not res then 63 | return nil, err 64 | end 65 | http_c:set_keepalive() 66 | 67 | if not ((200 <= res.status) and (res.status < 300)) then 68 | return nil, get_err_str(res.status, res.body) 69 | end 70 | 71 | local response_body = res.body 72 | local temp_index, _, _ = str_find(res.headers['content-type'], ';') 73 | local mimetype = str_sub(res.headers['content-type'], 1, temp_index - 1) 74 | if mimetype == 'application/json' then 75 | response_body = cjson.decode(response_body) 76 | elseif mimetype == 'text/plain' then 77 | -- do nothing 78 | else 79 | return nil, 'Unknown mimetype, unable to deserialize: ' .. mimetype 80 | end 81 | 82 | return response_body, '' 83 | end 84 | 85 | 86 | ------------------------------------------------------------------------------ 87 | -- Get the basic info from the current cluster. 88 | -- 89 | ------------------------------------------------------------------------------ 90 | function _M.info(self, params) 91 | local _, query_params = deal_params(s_params) 92 | local data, err = self:_perform_request('GET', '/', query_params) 93 | 94 | return data, err 95 | end 96 | 97 | 98 | ------------------------------------------------------------------------------ 99 | -- Returns True if the cluster is up, False otherwise. 100 | -- 101 | ------------------------------------------------------------------------------ 102 | function _M.ping(self, params) 103 | local _, query_params = deal_params(s_params) 104 | local data, err = self:_perform_request('HEAD', '/', query_params) 105 | if not data then 106 | return false, err 107 | end 108 | return true, '' 109 | end 110 | 111 | 112 | ------------------------------------------------------------------------------ 113 | -- Execute a search query and get back search hits that match the query. 114 | -- 115 | ------------------------------------------------------------------------------ 116 | function _M.search(self, s_params) 117 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type', 'body') 118 | if basic_params.doc_type and not basic_params.index then 119 | basic_params.index = '_all' 120 | end 121 | 122 | local data, err = self:_perform_request( 123 | 'GET', make_path(basic_params.index, basic_params.doc_type, '_search'), 124 | query_params, basic_params.body 125 | ) 126 | 127 | return data, err 128 | end 129 | 130 | 131 | ------------------------------------------------------------------------------ 132 | -- A query that accepts a query template. 133 | -- 134 | ------------------------------------------------------------------------------ 135 | function _M.search_template(self, s_params) 136 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type', 'body') 137 | local data, err = self:_perform_request( 138 | 'GET', 139 | make_path(basic_params.index, basic_params.doc_type, '_search', 'template'), 140 | query_params, basic_params.body 141 | ) 142 | 143 | return data, err 144 | end 145 | 146 | 147 | function _M.search_shards(self, s_params) 148 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type') 149 | local data, err = self:_perform_request( 150 | 'GET', make_path(basic_params.index, basic_params.doc_type, '_search_shards'), 151 | query_params 152 | ) 153 | 154 | return data, err 155 | end 156 | 157 | 158 | function _M.explain(self, s_params) 159 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type', 'id', 'body') 160 | if not basic_params.index then 161 | return nil, 'the index parameter is a required argument.' 162 | end 163 | if not basic_params.doc_type then 164 | return nil, 'the doc_type parameter is a required argument.' 165 | end 166 | if not basic_params.id then 167 | return nil, 'the id parameter is a required argument.' 168 | end 169 | 170 | local data, err = self:_perform_request( 171 | 'GET', 172 | make_path(basic_params.index, basic_params.doc_type, basic_params.id, '_explain'), 173 | query_params, basic_params.body 174 | ) 175 | 176 | return data, err 177 | end 178 | 179 | 180 | function _M.delete(self, s_params) 181 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type', 'id') 182 | if not basic_params.index then 183 | return nil, 'the index parameter is a required argument.' 184 | end 185 | if not basic_params.doc_type then 186 | return nil, 'the doc_type parameter is a required argument.' 187 | end 188 | if not basic_params.id then 189 | return nil, 'the id parameter is a required argument.' 190 | end 191 | 192 | local data, err = self:_perform_request( 193 | 'DELETE', make_path(basic_params.index, basic_params.doc_type, basic_params.id), 194 | query_params 195 | ) 196 | 197 | return data, err 198 | end 199 | 200 | 201 | function _M.count(self, s_params) 202 | local basic_params, query_params = deal_params(s_params, 'index', 'doc_type', 'body') 203 | if basic_params.doc_type and not basic_params.index then 204 | basic_params.index = '_all' 205 | end 206 | 207 | local data, err = self:_perform_request( 208 | 'GET', make_path(basic_params.index, basic_params.doc_type, '_count'), 209 | query_params, basic_params.body 210 | ) 211 | 212 | return data, err 213 | end 214 | 215 | 216 | function _M.suggest(self, s_params) 217 | local basic_params, query_params = deal_params(s_params, 'index', 'body') 218 | if not basic_params.body then 219 | return nil, 'the body parameter is a required argument.' 220 | end 221 | 222 | local data, err = self:_perform_request( 223 | 'POST', make_path(basic_params.index, '_suggest'), 224 | query_params, basic_params.body 225 | ) 226 | 227 | return data, err 228 | end 229 | 230 | 231 | return _M 232 | -------------------------------------------------------------------------------- /lib/resty/es/cat.lua: -------------------------------------------------------------------------------- 1 | local es_utils = require "resty.es.utils" 2 | 3 | 4 | local deal_params = es_utils.deal_params 5 | 6 | 7 | local _M = { 8 | _VERSION = '0.01' 9 | } 10 | 11 | 12 | local mt = { __index = _M } 13 | 14 | function _M.new(self, client) 15 | return setmetatable({client = client}, mt) 16 | end 17 | 18 | 19 | function _M.aliases(self, c_params) 20 | end 21 | 22 | 23 | function _M.health(self, s_params) 24 | local _, query_params = deal_params(s_params) 25 | local data, err = self.client:_perform_request( 26 | 'GET', '/_cat/health', query_params 27 | ) 28 | 29 | return data, err 30 | end 31 | 32 | 33 | return _M 34 | -------------------------------------------------------------------------------- /lib/resty/es/cluster.lua: -------------------------------------------------------------------------------- 1 | local es_utils = require "resty.es.utils" 2 | 3 | 4 | local make_path = es_utils.make_path 5 | local deal_params = es_utils.deal_params 6 | 7 | 8 | local _M = { 9 | _VERSION = '0.01' 10 | } 11 | 12 | 13 | local mt = { __index = _M } 14 | 15 | 16 | function _M.new(self, client) 17 | return setmetatable({client = client}, mt) 18 | end 19 | 20 | 21 | function _M.health(self, s_params) 22 | local basic_params, query_params = deal_params(s_params, 'index') 23 | local data, err = self.client:_perform_request( 24 | 'GET', 25 | make_path('_cluster', 'health', basic_index), 26 | query_params 27 | ) 28 | 29 | return data, err 30 | end 31 | 32 | 33 | return _M 34 | -------------------------------------------------------------------------------- /lib/resty/es/indices.lua: -------------------------------------------------------------------------------- 1 | local es_utils = require "resty.es.utils" 2 | 3 | 4 | local make_path = es_utils.make_path 5 | local deal_params = es_utils.deal_params 6 | 7 | 8 | local _M = { 9 | _VERSION = '0.01' 10 | } 11 | 12 | 13 | local mt = { __index = _M } 14 | 15 | 16 | function _M.new(self, client) 17 | return setmetatable({client = client}, mt) 18 | end 19 | 20 | 21 | function _M.analyze(self, s_params) 22 | local basic_params, query_params = deal_params(s_params, 'index', 'body') 23 | local data, err = self.client:_perform_request( 24 | 'GET', 25 | make_path(basic_params.index, '_analyze'), 26 | query_params, basic_params.body 27 | ) 28 | 29 | return data, err 30 | end 31 | 32 | 33 | return _M 34 | -------------------------------------------------------------------------------- /lib/resty/es/nodes.lua: -------------------------------------------------------------------------------- 1 | local es_utils = require "resty.es.utils" 2 | 3 | 4 | local make_path = es_utils.make_path 5 | local deal_params = es_utils.deal_params 6 | 7 | 8 | local _M = { 9 | _VERSION = '0.01' 10 | } 11 | 12 | 13 | local mt = { __index = _M } 14 | 15 | 16 | function _M.new(self, client) 17 | return setmetatable({client = client}, mt) 18 | end 19 | 20 | 21 | function _M.info(self, s_params) 22 | local basic_params, query_params = deal_params(s_params, 'node_id', 'metric') 23 | local data, err = self.client:_perform_request( 24 | 'GET', 25 | make_path('_nodes', basic_params.node_id, basic_params.metric), 26 | query_params 27 | ) 28 | 29 | return data, err 30 | end 31 | 32 | 33 | return _M 34 | -------------------------------------------------------------------------------- /lib/resty/es/snapshot.lua: -------------------------------------------------------------------------------- 1 | local es_utils = require "resty.es.utils" 2 | 3 | 4 | local make_path = es_utils.make_path 5 | local deal_params = es_utils.deal_params 6 | 7 | 8 | local _M = { 9 | _VERSION = '0.01' 10 | } 11 | 12 | 13 | local mt = { __index = _M } 14 | 15 | 16 | function _M.new(self, client) 17 | return setmetatable({client = client}, mt) 18 | end 19 | 20 | 21 | function _M.create(self, s_params) 22 | local basic_params, query_params = deal_params(s_params, 'repository', 'snapshot', 'body') 23 | local data, err = self.client:_perform_request( 24 | 'PUT', 25 | make_path('_snapshot', basic_params.repository, basic_params.snapshot), 26 | query_params, basic_params.body 27 | ) 28 | 29 | return data, err 30 | end 31 | 32 | 33 | return _M 34 | -------------------------------------------------------------------------------- /lib/resty/es/utils.lua: -------------------------------------------------------------------------------- 1 | local cjson = require "cjson" 2 | 3 | local t_insert = table.insert 4 | local t_concat = table.concat 5 | local str_format = string.format 6 | local select = select 7 | 8 | 9 | local _M = { 10 | _VERSION = '0.01' 11 | } 12 | 13 | 14 | function _M.make_path(...) 15 | local t = {} 16 | local num_args = select('#', ...) 17 | for i = 1, num_args do 18 | local arg = select(i, ...) 19 | if arg ~= nil and arg ~= '' then 20 | t_insert(t, arg) 21 | end 22 | end 23 | return '/' .. t_concat(t, '/') 24 | end 25 | 26 | 27 | local function json_decode(str) 28 | local json_value = nil 29 | pcall(function (str) json_value = json.decode(str) end, str) 30 | return json_value 31 | end 32 | 33 | 34 | function _M.get_err_str(self, http_response_code, http_response_data) 35 | return str_format( 36 | '%s: http response code: %s. and es response error info: %s', 37 | http_resonse_code, http_response_data 38 | ) 39 | end 40 | 41 | 42 | function _M.deal_params(s_params, query_params_list) 43 | if s_params == nil then 44 | return {}, {} 45 | end 46 | end 47 | 48 | 49 | return _M 50 | -------------------------------------------------------------------------------- /t/search.t: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bells/lua-resty-elasticsearch/bf5d095c778f05c95b018cf5ada0a17befa25c06/t/search.t --------------------------------------------------------------------------------