├── .gitignore ├── LICENSE.md ├── MANIFEST.in ├── README.md ├── Vagrantfile ├── setup.py ├── superelasticsearch └── __init__.py ├── tests ├── __init__.py ├── test_data.dump └── test_superelasticsearch.py └── vagrant └── setup.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | __pycache__ 21 | 22 | # Installer logs 23 | pip-log.txt 24 | 25 | # Unit test / coverage reports 26 | .coverage 27 | .tox 28 | nosetests.xml 29 | 30 | # Translations 31 | *.mo 32 | 33 | # Mr Developer 34 | .mr.developer.cfg 35 | .project 36 | .pydevproject 37 | 38 | # Celery 39 | *.db 40 | 41 | # Vagrant 42 | .vagrant 43 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Wingify Software Pvt. Ltd. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md 2 | include LICENSE.md 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # superelasticsearch 2 | 3 | superelasticsearch is utility library that extends a particular version of 4 | [elasticsearch][es] library to provide some more utility functions on top of it 5 | to make using [Elasticsearch][es_server] even easier. 6 | 7 | ## Compatibility 8 | 9 | This compatibility table indicates compatibility of SuperElasticsearch with 10 | the versions of elasticsearch-py and Elasticsearch. This only means that we 11 | have tested SuperElasticsearch with these versions of Elasticsearch and 12 | the official Elasticsearch Python client. It may actually work with newer 13 | releases. If such is the case, please feel free to updated this table by 14 | opening a pull request. 15 | 16 | | Elasticsearch | elasticsearch-py | SuperElasticsearch | Release date | 17 | | -------------- | ---------------- | ------------------ | ------------ | 18 | | 1.7.2 | 1.7.0 | 0.1.0 | Oct 6, 2015 | 19 | 20 | ## Design & Usage 21 | 22 | superelasticsearch is nothing but a sub-class of the Elasticsearch Python 23 | client. It closely follows the API design as the Elasticsearch client library 24 | does. 25 | 26 | ## Additional Python APIs 27 | 28 | SuperElasticsearch provides a few additional APIs that are sugar coated to 29 | simplify using Elasticsearch in Python. These additional APIs are listed as 30 | follows: 31 | 32 | ### Iterated Search (or simpler Scroll API) 33 | 34 | Iterated search allows you to perform scroll API with ease and helps you reduce 35 | code, especially where you might want to use it in a loop. Iterated search 36 | returns a generator which can be iterated in a loop to get docs in returned by 37 | every Scroll API call. The best part is that the Scroll ID of every scroll is 38 | handled by the ``itersearch`` API. 39 | 40 | ``` 41 | from superelasticsearch import SuperElasticsearch 42 | 43 | client = SuperElasticsearch(hosts=['localhost:9200']) 44 | 45 | for doc in client.itersearch(index='test_index', doc_type'tweets', 46 | scroll='10m'): 47 | # do something with doc here 48 | pass 49 | 50 | ``` 51 | 52 | ### Simpler Bulk API 53 | 54 | Elasitcsearch's Bulk API is extremely helpful but has different semantics. 55 | Using Bulk API means manual handling of all the differences in naming of 56 | parameters, manual construction of the complex bulk body and all the errors 57 | and debugging challenges that come as extra work in the process. 58 | 59 | SuperElasticsearch provides a simpler Bulk API that enables you to use Bulk 60 | API in a non-bulk fashion. 61 | 62 | Example: 63 | 64 | ``` 65 | from superelasticsearch import SuperElasticsearch 66 | 67 | client = SuperElasticsearch(hosts=['localhost:9200']) 68 | bulk = client.bulk_operation() 69 | 70 | bulk.index(index='test_index_1', doc_type='test_doc_type', 71 | body=dict(key1='val1')) 72 | bulk.delete(index='test_index_2', doc_type='test_doc_type', 73 | id=123) 74 | bulk.update(index='test_index_3', doc_type='test_doc_type', 75 | id=456, body={ 76 | 'script': 'ctx._source.count += count', 77 | 'params': { 78 | 'count': 1 79 | } 80 | }) 81 | 82 | resp = bulk.execute() 83 | ``` 84 | 85 | SuperElasticsearch's Bulk Operations do all the book keeping of individual 86 | operations that you perform, properly serialize those operations to Bulk APIs 87 | requirements and executes the request. 88 | 89 | [es]: http://github.com/elasticsearch/elasticsearch-py 90 | [es_server]: http://elasticsearch.org 91 | 92 | ## License 93 | 94 | This project is licensed under MIT License. 95 | 96 | Copyright (c) 2013 Wingify Software Pvt. Ltd. 97 | 98 | See [LICENSE.md](LICENSE.md). 99 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! 5 | VAGRANTFILE_API_VERSION = "2" 6 | 7 | Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| 8 | # Provision using shell 9 | config.vm.provision "shell", path: "vagrant/setup.sh" 10 | 11 | config.vm.define "superelasticsearch", primary: true do |superelasticsearch| 12 | # Box configuration 13 | superelasticsearch.vm.box = "debian-7.3.0" 14 | superelasticsearch.vm.box_url = "http://puppet-vagrant-boxes.puppetlabs.com/debian-73-x64-virtualbox-nocm.box" 15 | 16 | # Synced folders 17 | superelasticsearch.vm.synced_folder ".", "/home/vagrant/superelasticsearch" 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | try: 2 | from setuptools import setup, find_packages 3 | except ImportError: 4 | from distribute_setup import use_setuptools 5 | use_setuptools() 6 | from setuptools import setup, find_packages 7 | 8 | __name__ = 'superelasticsearch' 9 | __version__ = '0.1.1' 10 | __author__ = 'Wingify Engineering' 11 | __author_email__ = 'dev@wingify.com' 12 | 13 | long_description = '''superelasticsearch is utility library that extends a 14 | particular version of elasticsearch library to provide some more utility 15 | functions on top of it to make using Elasticsearch even easier. 16 | 17 | SuperElasticsearch provides a few additional APIs that are sugar coated to 18 | simplify using Elasticsearch in Python. These additional APIs are listed as 19 | follows: 20 | 21 | 1. Iterated Search 22 | 2. Simple Bulk API 23 | 24 | Read more with examples here: https://github.com/wingify/superelasticsearch 25 | ''' 26 | 27 | setup( 28 | name=__name__, 29 | version=__version__, 30 | author=__author__, 31 | author_email=__author_email__, 32 | description=('An extended version of the official ' 33 | 'Elasticsearch Python client.'), 34 | long_description=long_description, 35 | packages=find_packages(exclude=['tests']), 36 | install_requires = [ 37 | 'elasticsearch', 38 | ], 39 | include_package_data = True, 40 | zip_safe = False, 41 | classifiers = [ 42 | 'Development Status :: 2 - Pre-Alpha', 43 | 'Intended Audience :: Developers', 44 | 'Operating System :: OS Independent', 45 | 'Programming Language :: Python', 46 | ], 47 | test_suite='nose.collector', 48 | tests_require=[ 49 | 'nose', 50 | 'mock', 51 | 'datadiff', 52 | ] 53 | ) 54 | -------------------------------------------------------------------------------- /superelasticsearch/__init__.py: -------------------------------------------------------------------------------- 1 | ''' 2 | superelasticsearch 3 | ~~~~~~~~~~~~~~~~~~ 4 | 5 | SuperElasticsearch - the client to make your life with Elasticsearch and 6 | Python easy. 7 | 8 | This library aims to make working with Elasticsearch in Python easier. The 9 | main of this library is to provide you with APIs that otherwise don't 10 | exist. These new APIs have been developed using existing Elasticsearch 11 | APIs. 12 | 13 | SuperElasticsearch essentially builds upon the official Python client for 14 | Elasticsearch by subclassing the main client class and extending it to 15 | provide more APIs that make working with Elasticsearch easier. This makes 16 | it easy for you to adopt this library as it supports everything that the 17 | official library supports, and it adds more APIs that work like the APIs 18 | of the official client. 19 | ''' 20 | 21 | __all__ = ['SuperElasticsearch'] 22 | 23 | from elasticsearch import Elasticsearch 24 | from elasticsearch import ElasticsearchException 25 | from elasticsearch.client.utils import query_params 26 | from elasticsearch.serializer import JSONSerializer 27 | 28 | # Use elasticsearch library's implementation of JSON serializer 29 | json = JSONSerializer() 30 | 31 | 32 | class SuperElasticsearch(Elasticsearch): 33 | ''' 34 | Subclass of :class:`elasticsearch.Elasticsearch` to provide some useful 35 | utilities. 36 | ''' 37 | 38 | def __init__(self, *args, **kwargs): 39 | super(SuperElasticsearch, self).__init__(*args, **kwargs) 40 | 41 | # presevery arguments and keyword arguments for bulk clients 42 | self._args = args 43 | self._kwargs = kwargs 44 | 45 | def itersearch(self, scroll, **kwargs): 46 | ''' 47 | Iterated search for making Scroll API really simple to use. 48 | 49 | Executes a search query of scroll type and returns an iterator for easy 50 | iteration over the result set and for making further calls to 51 | Elasticsearch for scrolling over the remaining results. 52 | 53 | :arg index: A comma-separated list of index names to search; use `_all` 54 | or empty string to perform the operation on all indices 55 | :arg doc_type: A comma-separated list of document types to search; 56 | leave empty to perform the operation on all types 57 | :arg body: The search definition using the Query DSL 58 | :arg chunked: False to get one document per iteration. False to get the 59 | all the documents returned in response to every scroll request. 60 | Defaults to False. 61 | :arg with_meta: True to return meta data of Scroll API requests with 62 | every iteration. Defaults to False. 63 | :arg _source: True or false to return the _source field or not, or a 64 | list of fields to return 65 | :arg _source_exclude: A list of fields to exclude from the returned 66 | _source field 67 | :arg _source_include: A list of fields to extract and return from the 68 | _source field 69 | :arg analyze_wildcard: Specify whether wildcard and prefix queries 70 | should be analyzed (default: false) 71 | :arg analyzer: The analyzer to use for the query string 72 | :arg default_operator: The default operator for query string query (AND 73 | or OR) (default: OR) 74 | :arg df: The field to use as default where no field prefix is given in 75 | the query string 76 | :arg explain: Specify whether to return detailed information about 77 | score computation as part of a hit 78 | :arg fields: A comma-separated list of fields to return as part of a hit 79 | :arg ignore_indices: When performed on multiple indices, allows to 80 | ignore `missing` ones (default: none) 81 | :arg indices_boost: Comma-separated list of index boosts 82 | :arg lenient: Specify whether format-based query failures (such as 83 | providing text to a numeric field) should be ignored 84 | :arg lowercase_expanded_terms: Specify whether query terms should be 85 | lowercased 86 | :arg from_: Starting offset (default: 0) 87 | :arg preference: Specify the node or shard the operation should be 88 | performed on (default: random) 89 | :arg q: Query in the Lucene query string syntax 90 | :arg routing: A comma-separated list of specific routing values 91 | :arg scroll: Specify how long a consistent view of the index should be 92 | maintained for scrolled search 93 | :arg size: Number of hits to return (default: 10) 94 | :arg sort: A comma-separated list of : pairs 95 | :arg source: The URL-encoded request definition using the Query DSL 96 | (instead of using request body) 97 | :arg stats: Specific 'tag' of the request for logging and statistical 98 | purposes 99 | :arg suggest_field: Specify which field to use for suggestions 100 | :arg suggest_mode: Specify suggest mode (default: missing) 101 | :arg suggest_size: How many suggestions to return in response 102 | :arg suggest_text: The source text for which the suggestions should be 103 | returned 104 | :arg timeout: Explicit operation timeout 105 | :arg version: Specify whether to return document version as part of a 106 | hit 107 | 108 | .. Usage:: 109 | from superelasticsearch import SuperElasticsearch 110 | es = SuperElasticsearch(hosts=['localhost:9200']) 111 | for doc in es.itersearch(index='tweets', doc_type='tweet', 112 | chunked=False): 113 | print doc['_id'] 114 | ''' 115 | 116 | # add scroll 117 | kwargs['scroll'] = scroll 118 | 119 | # prepare kwargs for search 120 | if 'chunked' in kwargs: 121 | chunked = kwargs.pop('chunked') 122 | else: 123 | chunked = True 124 | 125 | if 'with_meta' in kwargs: 126 | with_meta = kwargs.pop('with_meta') 127 | else: 128 | with_meta = False 129 | 130 | resp = self.search(**kwargs) 131 | total = resp['hits']['total'] 132 | scroll_id = resp['_scroll_id'] 133 | counter = 0 134 | 135 | while len(resp['hits']['hits']) > 0: 136 | # prepare meta 137 | meta = resp.copy() 138 | meta['hits'] = resp['hits'].copy() 139 | meta['hits'].pop('hits') 140 | 141 | # if expected chunked, then return chunks else return 142 | # every doc per iteration 143 | if chunked: 144 | if with_meta: 145 | yield resp['hits']['hits'], meta 146 | else: 147 | yield resp['hits']['hits'] 148 | else: 149 | for doc in resp['hits']['hits']: 150 | if with_meta: 151 | yield doc, meta 152 | else: 153 | yield doc 154 | 155 | # increment the counter 156 | counter += len(resp['hits']['hits']) 157 | 158 | # get the next set of results 159 | scroll_id = resp['_scroll_id'] 160 | resp = self.scroll(scroll_id=scroll_id, scroll=kwargs['scroll']) 161 | 162 | # check if all the documents were scrolled or not 163 | if counter != total: 164 | raise ElasticsearchException( 165 | 'Failed to get all the documents while scrolling. Total ' 166 | 'documents that matched the query: %s\n' 167 | 'Total documents that were retrieved while scrolling: %s\n' 168 | 'Last scroll_id with documents: %s.\n' 169 | 'Last scroll_id: %s ' % ( 170 | total, 171 | counter, 172 | scroll_id, 173 | resp['_scroll_id'])) 174 | 175 | # clear scroll 176 | self.clear_scroll(scroll_id=scroll_id) 177 | 178 | def bulk_operation(self, **kwargs): 179 | ''' 180 | Creates a new native client like instance for performing bulk 181 | operations. For every bulk operation, a new bulk operation instance 182 | must be created. 183 | 184 | .. Usage:: 185 | from superelasticsearch import SuperElasticsearch 186 | es = SuperElasticsearch(hosts=['localhost:9200']) 187 | bulk = es.bulk_operation(index='bulk_index') 188 | 189 | bulk.index( 190 | index='other_bulk_index', doc_type='docs', body=dict(key1=val1)) 191 | bulk.create(doc_type='docs', body=dict(key2=val2)) 192 | 193 | bulk.execute() 194 | 195 | :arg index: Default index for items which don't provide one 196 | :arg doc_type: Default document type for items which don't provide one 197 | :arg consistency: Explicit write consistency setting for the operation 198 | :arg refresh: Refresh the index after performing the operation 199 | :arg routing: Specific routing value 200 | :arg replication: Explicitly set the replication type (default: sync) 201 | :arg timeout: Explicit operation timeout 202 | :returns: an instance of :class:`BulkOperation` 203 | 204 | .. Note:: all the arguments passed at the time create a new bulk 205 | operation can be overridden when 206 | :meth:`BulkOperation.execute`: is called. 207 | ''' 208 | 209 | return BulkOperation(self, **kwargs) 210 | 211 | 212 | class _BulkAction(object): 213 | 214 | def __init__(self, type, params, body=None): 215 | if type not in BulkOperation.BULK_ACTIONS: 216 | raise Exception('%s action type is not a valid Elasticsearch bulk ' 217 | 'action type.' % type) 218 | 219 | if BulkOperation.BULK_ACTIONS.get(type) and body is None: 220 | raise Exception('%s action type expects a body as well to be a ' 221 | 'valid bulk operation.' % type) 222 | 223 | self.type = type 224 | self.params = params 225 | self.body = body 226 | 227 | @property 228 | def es_op(self): 229 | retval = '' 230 | 231 | retval += json.dumps({self.type: self.params}) 232 | if BulkOperation.BULK_ACTIONS.get(self.type): 233 | retval += '\n' + json.dumps(self.body) 234 | 235 | return retval 236 | 237 | 238 | class BulkOperation(object): 239 | ''' 240 | Simple bulk operations manager for Elasticsearch's Bulk API. Exposes API 241 | similar to non-bulk counterparts of all supported Bulk Operations, manages 242 | every call to make Bulk API request and executes it. Basically, it takes 243 | away the pain of writing different code because of difference between 244 | non-bulk APIs and bulk API. 245 | ''' 246 | 247 | # Map of valid Bulk Actions that Elasticsearch supports and which of those 248 | # actions expect a body that needs to be added in the line next to the line 249 | # of action specification 250 | BULK_ACTIONS = { 251 | 'index': True, 252 | 'create': True, 253 | 'update': True, 254 | 'delete': False, 255 | } 256 | 257 | @query_params('index', 'doc_type', 'consistency', 'refresh', 'routing', 258 | 'replication', 'timeout') 259 | def __init__(self, client, params=None, **kwargs): 260 | ''' 261 | API for performing easy bulk operations in Elasticsearch. 262 | 263 | :arg client: instance of official Elasticsearch Python client. 264 | :arg index: Default index for items which don't provide one 265 | :arg doc_type: Default document type for items which don't provide one 266 | :arg consistency: Explicit write consistency setting for the operation 267 | :arg refresh: Refresh the index after performing the operation 268 | :arg routing: Specific routing value 269 | :arg replication: Explicitly set the replication type (default: sync) 270 | :arg timeout: Explicit operation timeout 271 | 272 | .. Note:: all the arguments passed at the time create a new bulk 273 | operation can be overridden when 274 | :meth:`BulkOperation.execute`: is called. 275 | ''' 276 | 277 | self._client = client 278 | self._params = params 279 | self._actions = [] 280 | 281 | @query_params('index', 'doc_type', 'consistency', 'refresh', 'routing', 282 | 'replication', 'timeout') 283 | def execute(self, params=None, **kwargs): 284 | ''' 285 | Executes all recorded actions using Elasticsearch's Bulk Query. 286 | 287 | .. Note:: The arguments passed at the time of creating a bulk client 288 | will be overridden with the arguments passed to this method. 289 | 290 | :arg index: Default index for items which don't provide one 291 | :arg doc_type: Default document type for items which don't provide one 292 | :arg consistency: Explicit write consistency setting for the operation 293 | :arg refresh: Refresh the index after performing the operation 294 | :arg routing: Specific routing value 295 | :arg replication: Explicitly set the replication type (default: sync) 296 | :arg timeout: Explicit operation timeout 297 | ''' 298 | 299 | # TO DO: check if percolate, timeout and replication parameters are 300 | # allowed for bulk index operation 301 | 302 | bulk_body = '' 303 | for action in self._actions: 304 | bulk_body += action.es_op + '\n' 305 | 306 | bulk_kwargs = {} 307 | bulk_kwargs.update(self._params) 308 | bulk_kwargs.update(params) 309 | 310 | resp = self._client.bulk(body=bulk_body, **bulk_kwargs) 311 | self._actions = [] 312 | return resp 313 | 314 | @query_params('index', 'doc_type', 'consistency', 'parent', 'refresh', 315 | 'routing', 'timestamp', 'ttl', 316 | 'version', 'version_type') 317 | def _index_or_create(self, action_type, body, id=None, params=None): 318 | ''' 319 | Implementation of Bulk Index and Bulk Create operations. 320 | 321 | :arg action_type: The type of action i.e. **create** or **index** 322 | :arg index: The name of the index 323 | :arg doc_type: The type of the document 324 | :arg body: The document 325 | :arg id: Document ID 326 | :arg consistency: Explicit write consistency setting for the operation 327 | :arg parent: ID of the parent document 328 | :arg refresh: Refresh the index after performing the operation 329 | :arg routing: Specific routing value 330 | :arg timestamp: Explicit timestamp for the document 331 | :arg ttl: Expiration time for the document 332 | :arg version: Explicit version number for concurrency control 333 | :arg version_type: Specific version type 334 | ''' 335 | 336 | # TO DO: check if percolate, timeout and replication parameters are 337 | # allowed for bulk index operation 338 | 339 | bulk_params = {} 340 | 341 | if params.get('index') is not None: 342 | bulk_params['_index'] = params['index'] 343 | params.pop('index') 344 | if params.get('doc_type') is not None: 345 | bulk_params['_type'] = params['doc_type'] 346 | params.pop('doc_type') 347 | if id is not None: 348 | bulk_params.update(_id=id) 349 | 350 | bulk_params.update(params) 351 | 352 | self._actions.append(_BulkAction(type=action_type, params=bulk_params, 353 | body=body)) 354 | 355 | def index(self, body, id=None, **kwargs): 356 | ''' 357 | Implementation of Bulk Index operation. 358 | 359 | :arg action_type: The type of action i.e. **create** or **index** 360 | :arg index: The name of the index 361 | :arg doc_type: The type of the document 362 | :arg body: The document 363 | :arg id: Document ID 364 | :arg consistency: Explicit write consistency setting for the operation 365 | :arg parent: ID of the parent document 366 | :arg refresh: Refresh the index after performing the operation 367 | :arg routing: Specific routing value 368 | :arg timestamp: Explicit timestamp for the document 369 | :arg ttl: Expiration time for the document 370 | :arg version: Explicit version number for concurrency control 371 | :arg version_type: Specific version type 372 | ''' 373 | 374 | self._index_or_create('index', body, id, **kwargs) 375 | 376 | def create(self, body, id=None, **kwargs): 377 | ''' 378 | Implementation of Bulk Create operation. 379 | 380 | :arg action_type: The type of action i.e. **create** or **index** 381 | :arg index: The name of the index 382 | :arg doc_type: The type of the document 383 | :arg body: The document 384 | :arg id: Document ID 385 | :arg consistency: Explicit write consistency setting for the operation 386 | :arg parent: ID of the parent document 387 | :arg refresh: Refresh the index after performing the operation 388 | :arg routing: Specific routing value 389 | :arg timestamp: Explicit timestamp for the document 390 | :arg ttl: Expiration time for the document 391 | :arg version: Explicit version number for concurrency control 392 | :arg version_type: Specific version type 393 | ''' 394 | 395 | self._index_or_create('create', body, id, **kwargs) 396 | 397 | @query_params('index', 'doc_type', 'consistency', 'parent', 'replication', 398 | 'routing', 'ttl', 'version', 'version_type') 399 | def update(self, id, body, params=None, **kwargs): 400 | ''' 401 | Implementation of Bulk Update operation. 402 | 403 | :arg index: The name of the index 404 | :arg doc_type: The type of the document 405 | :arg body: The document 406 | :arg id: Document ID 407 | :arg consistency: Explicit write consistency setting for the operation 408 | :arg parent: ID of the parent document 409 | :arg replication: Specific replication type (default: sync) 410 | :arg routing: Specific routing value 411 | :arg ttl: Expiration time for the document 412 | :arg version: Explicit version number for concurrency control 413 | :arg version_type: Specific version type 414 | ''' 415 | 416 | bulk_params = {} 417 | 418 | if params.get('index') is not None: 419 | bulk_params['_index'] = params['index'] 420 | params.pop('index') 421 | if params.get('doc_type') is not None: 422 | bulk_params['_type'] = params['doc_type'] 423 | params.pop('doc_type') 424 | bulk_params.update(_id=id) 425 | 426 | bulk_params.update(params) 427 | 428 | self._actions.append(_BulkAction(type='update', params=bulk_params, 429 | body=body)) 430 | 431 | @query_params('index', 'doc_type', 'consistency', 'parent', 'replication', 432 | 'routing', 'version', 'version_type') 433 | def delete(self, id, params=None, **kwargs): 434 | ''' 435 | Implementation of Bulk Delete operation. 436 | 437 | :arg index: The name of the index 438 | :arg doc_type: The type of the document 439 | :arg id: Document ID 440 | :arg consistency: Explicit write consistency setting for the operation 441 | :arg parent: ID of the parent document 442 | :arg replication: Explicitly set the replication type (default: sync) 443 | :arg routing: Specific routing value 444 | :arg version: Explicit version number for concurrency control 445 | :arg version_type: Specific version type 446 | ''' 447 | 448 | bulk_params = {} 449 | 450 | if params.get('index') is not None: 451 | bulk_params['_index'] = params['index'] 452 | params.pop('index') 453 | if params.get('doc_type') is not None: 454 | bulk_params['_type'] = params['doc_type'] 455 | params.pop('doc_type') 456 | bulk_params.update(_id=id) 457 | 458 | bulk_params.update(params) 459 | 460 | self._actions.append(_BulkAction(type='delete', params=bulk_params)) 461 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wingify/superelasticsearch/3e6d5d7661d95e1e9207b84ae05310e313f8fd09/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_data.dump: -------------------------------------------------------------------------------- 1 | [{"city": "Cornwall", "region": "Ontario", "geo": "-43.288, -19.54089", "zipcode": "11847", "country": "Nepal"}, {"city": "Merrickville-Wolford", "region": "Ontario", "geo": "-51.22427, -4.77618", "zipcode": "90114", "country": "Sweden"}, {"city": "Rochester", "region": "Minnesota", "geo": "45.11669, 9.94903", "zipcode": "58638", "country": "Brunei"}, {"city": "Tuktoyaktuk", "region": "Northwest Territories", "geo": "-62.11111, -39.18021", "zipcode": "37397", "country": "Morocco"}, {"city": "Redcliffe", "region": "Queensland", "geo": "-74.25122, -130.85289", "zipcode": "28993", "country": "Macedonia, the former Yugoslav Republic of"}, {"city": "Lieferinge", "region": "OV", "geo": "-6.47139, -19.33476", "zipcode": "14683", "country": "Finland"}, {"city": "Evansville", "region": "IN", "geo": "-35.08725, -39.38893", "zipcode": "66299", "country": "Marshall Islands"}, {"city": "Gore", "region": "South Island", "geo": "-69.73514, -178.68319", "zipcode": "96613", "country": "Cambodia"}, {"city": "Newport", "region": "Hampshire", "geo": "-32.39768, 26.24806", "zipcode": "89413", "country": "Argentina"}, {"city": "Limoges", "region": "Limousin", "geo": "39.38555, 15.99181", "zipcode": "62677", "country": "Oman"}, {"city": "San Rafael", "region": "C", "geo": "51.40419, -41.74895", "zipcode": "76066", "country": "Finland"}, {"city": "Albacete", "region": "CM", "geo": "84.27531, -144.20373", "zipcode": "89335", "country": "Venezuela"}, {"city": "Vigo", "region": "GA", "geo": "11.31613, 80.93322", "zipcode": "52310", "country": "Uruguay"}, {"city": "Doetinchem", "region": "Gl", "geo": "-84.41909, -53.56341", "zipcode": "17831", "country": "Botswana"}, {"city": "Reims", "region": "Champagne-Ardenne", "geo": "31.84884, -87.89532", "zipcode": "60637", "country": "Lebanon"}, {"city": "Wanganui", "region": "NI", "geo": "71.01593, -160.96877", "zipcode": "14228", "country": "Pitcairn Islands"}, {"city": "Dieppe", "region": "New Brunswick", "geo": "57.91587, -167.63302", "zipcode": "49672", "country": "Jordan"}, {"city": "Ca\u00c3\u00b1as", "region": "Guanacaste", "geo": "-13.53889, 119.8551", "zipcode": "29710", "country": "Bangladesh"}, {"city": "Paradise", "region": "NL", "geo": "-24.12782, 123.84568", "zipcode": "49093", "country": "Indonesia"}, {"city": "Iqaluit", "region": "Nunavut", "geo": "-75.96247, -24.23463", "zipcode": "45907", "country": "Holy See (Vatican City State)"}, {"city": "Bremerhaven", "region": "Bremen", "geo": "-59.03963, -86.76454", "zipcode": "92656", "country": "San Marino"}, {"city": "Humbeek", "region": "VB", "geo": "22.27852, -24.37118", "zipcode": "11578", "country": "Bhutan"}, {"city": "Helena", "region": "MT", "geo": "19.65425, -15.87204", "zipcode": "13527", "country": "Martinique"}, {"city": "Meppel", "region": "Drenthe", "geo": "-56.04592, -57.68843", "zipcode": "49571", "country": "Djibouti"}, {"city": "Tilff", "region": "LU", "geo": "-53.98855, 86.043", "zipcode": "26212", "country": "Mauritania"}, {"city": "Heredia", "region": "H", "geo": "16.0723, -102.51426", "zipcode": "03981", "country": "Trinidad and Tobago"}, {"city": "Betim", "region": "MG", "geo": "-8.45499, 52.43311", "zipcode": "07914", "country": "Jordan"}, {"city": "Nicoya", "region": "G", "geo": "44.93882, -131.44111", "zipcode": "12164", "country": "Afghanistan"}, {"city": "W\u00c3\u00b6rgl", "region": "Tyrol", "geo": "-85.63689, -14.31159", "zipcode": "25127", "country": "New Zealand"}, {"city": "Enns", "region": "Upper Austria", "geo": "-40.77174, -25.89614", "zipcode": "36573", "country": "Brunei"}, {"city": "Columbia", "region": "MD", "geo": "-73.47126, -40.84174", "zipcode": "16417", "country": "French Polynesia"}, {"city": "Bad V\u00c3\u00b6slau", "region": "Lower Austria", "geo": "8.55364, 81.99529", "zipcode": "67411", "country": "Dominican Republic"}, {"city": "Tampa", "region": "Florida", "geo": "39.72549, -89.56226", "zipcode": "80214", "country": "Jordan"}, {"city": "Invercargill", "region": "SI", "geo": "-73.25243, 17.79139", "zipcode": "55387", "country": "C\u00c3\u00b4te D'Ivoire (Ivory Coast)"}, {"city": "L\u00ef\u00bf\u00bdrrach", "region": "BW", "geo": "-24.6091, -21.69468", "zipcode": "06446", "country": "Yemen"}, {"city": "Sioux City", "region": "Iowa", "geo": "-72.38934, 25.98969", "zipcode": "65272", "country": "Togo"}, {"city": "Santander", "region": "CA", "geo": "-29.36126, -28.02583", "zipcode": "79617", "country": "Mozambique"}, {"city": "Munich", "region": "Bavaria", "geo": "52.80678, -1.4017", "zipcode": "70252", "country": "Austria"}, {"city": "Rocca Santo Stefano", "region": "Lazio", "geo": "-34.56097, 168.55805", "zipcode": "54305", "country": "Jamaica"}, {"city": "An\u00c3\u00a1polis", "region": "GO", "geo": "-28.11636, 137.45202", "zipcode": "01455", "country": "Italy"}, {"city": "Guysborough", "region": "Nova Scotia", "geo": "2.94348, 157.95021", "zipcode": "04924", "country": "Taiwan"}, {"city": "Gjoa Haven", "region": "NU", "geo": "8.15029, -59.03003", "zipcode": "19114", "country": "Trinidad and Tobago"}, {"city": "V\u00c3\u00b6lkermarkt", "region": "Kt", "geo": "-39.49701, 26.10787", "zipcode": "96377", "country": "Uruguay"}, {"city": "Racine", "region": "WI", "geo": "-9.28992, 130.93655", "zipcode": "97863", "country": "Fiji"}, {"city": "La Salle", "region": "Valle d'Aosta", "geo": "73.6987, 33.31381", "zipcode": "45822", "country": "Liberia"}, {"city": "Lugo", "region": "Galicia", "geo": "41.62765, 8.27613", "zipcode": "32232", "country": "Korea, North"}, {"city": "Kapfenberg", "region": "Styria", "geo": "2.15839, -16.33498", "zipcode": "68585", "country": "Benin"}, {"city": "Orbais", "region": "WB", "geo": "-15.21623, -45.43381", "zipcode": "81989", "country": "Bulgaria"}, {"city": "Lochristi", "region": "OV", "geo": "-81.87595, 100.49344", "zipcode": "36776", "country": "South Georgia and The South Sandwich Islands"}, {"city": "Emmen", "region": "Dr", "geo": "-41.51719, -96.60258", "zipcode": "85785", "country": "Turks and Caicos Islands"}, {"city": "Gaithersburg", "region": "MD", "geo": "-2.75742, 106.82755", "zipcode": "41130", "country": "Kazakhstan"}, {"city": "Uberaba", "region": "MG", "geo": "-68.04566, 57.42606", "zipcode": "03968", "country": "Samoa"}, {"city": "Aurora", "region": "CO", "geo": "-41.8208, -164.69431", "zipcode": "57449", "country": "Yemen"}, {"city": "Liberia", "region": "G", "geo": "67.0443, -156.13923", "zipcode": "99282", "country": "Malawi"}, {"city": "Arviat", "region": "NU", "geo": "35.43465, -74.47334", "zipcode": "87485", "country": "Tuvalu"}, {"city": "Toernich", "region": "Luxemburg", "geo": "50.9291, -43.18105", "zipcode": "04698", "country": "Sri Lanka"}, {"city": "Castelvecchio di Rocca Barbena", "region": "LI", "geo": "26.42481, 31.80587", "zipcode": "33718", "country": "Bermuda"}, {"city": "Grand Rapids", "region": "Michigan", "geo": "-6.10599, 130.41445", "zipcode": "78600", "country": "Thailand"}, {"city": "Canoas", "region": "Rio Grande do Sul", "geo": "-76.73782, 162.81519", "zipcode": "46371", "country": "China"}, {"city": "Portland", "region": "ME", "geo": "-83.14113, -61.14195", "zipcode": "96075", "country": "French Guiana"}, {"city": "Burgos", "region": "CL", "geo": "46.78425, 161.83685", "zipcode": "36956", "country": "Turkmenistan"}, {"city": "Heinsch", "region": "LX", "geo": "-6.21536, -178.9469", "zipcode": "74406", "country": "Mayotte"}, {"city": "Sale", "region": "VI", "geo": "50.43631, -115.66501", "zipcode": "61598", "country": "Norway"}, {"city": "Springfield", "region": "Massachusetts", "geo": "59.66155, -174.48336", "zipcode": "05478", "country": "Belize"}, {"city": "Chapec\u00c3\u00b3", "region": "Santa Catarina", "geo": "49.27313, 98.48848", "zipcode": "21460", "country": "Zambia"}, {"city": "Lac-Serent", "region": "Quebec", "geo": "-1.89433, 47.57071", "zipcode": "93953", "country": "Micronesia"}, {"city": "Armadale", "region": "WA", "geo": "34.17045, 178.39673", "zipcode": "61940", "country": "Senegal"}, {"city": "Calco", "region": "LO", "geo": "-24.87779, -64.58997", "zipcode": "53293", "country": "Saint Martin"}, {"city": "Rekem", "region": "L.", "geo": "-20.00198, 145.96646", "zipcode": "74625", "country": "Jersey"}, {"city": "Orp-Jauche", "region": "Waals-Brabant", "geo": "-45.90567, -84.97884", "zipcode": "33410", "country": "Antigua and Barbuda"}, {"city": "Timaru", "region": "SI", "geo": "19.62464, -129.412", "zipcode": "62778", "country": "Uzbekistan"}, {"city": "Daly", "region": "Manitoba", "geo": "-31.46127, -86.62404", "zipcode": "81677", "country": "Libya"}, {"city": "Anthisnes", "region": "Luik", "geo": "76.26049, 23.75787", "zipcode": "32680", "country": "Cape Verde"}, {"city": "Santa Maria", "region": "Rio Grande do Sul", "geo": "-9.85322, -47.83384", "zipcode": "31722", "country": "Bolivia"}, {"city": "Ferri\u00ef\u00bf\u00bdres", "region": "LU", "geo": "39.78501, 171.78794", "zipcode": "73457", "country": "Saint Martin"}, {"city": "Harrisburg", "region": "Pennsylvania", "geo": "63.19945, -149.39175", "zipcode": "33018", "country": "Virgin Islands, United States"}, {"city": "Gasteiz", "region": "PV", "geo": "2.70742, 156.71416", "zipcode": "00597", "country": "Burundi"}, {"city": "Orosei", "region": "SA", "geo": "35.62604, -120.51083", "zipcode": "46853", "country": "Cayman Islands"}, {"city": "Barrie", "region": "ON", "geo": "22.83431, -171.15062", "zipcode": "39851", "country": "Switzerland"}, {"city": "Gro\u00c3\u009fpetersdorf", "region": "Burgenland", "geo": "-15.18838, -30.64618", "zipcode": "34890", "country": "Romania"}, {"city": "Brive-la-Gaillarde", "region": "Li", "geo": "78.72734, 24.90858", "zipcode": "99244", "country": "Guinea"}, {"city": "Lourdes", "region": "Manitoba", "geo": "-25.20945, 22.37952", "zipcode": "43365", "country": "Turkmenistan"}, {"city": "Estevan", "region": "SK", "geo": "22.08503, 0.18745", "zipcode": "10639", "country": "Qatar"}, {"city": "Cantley", "region": "Quebec", "geo": "67.47055, 1.60019", "zipcode": "21091", "country": "Seychelles"}, {"city": "Carapicu\u00c3\u00adba", "region": "S\u00c3\u00a3o Paulo", "geo": "73.95488, -110.30436", "zipcode": "29605", "country": "Uruguay"}, {"city": "Devonport", "region": "TA", "geo": "-36.08497, -125.62478", "zipcode": "77121", "country": "French Guiana"}, {"city": "North Shore", "region": "North Island", "geo": "4.67658, -56.82054", "zipcode": "23041", "country": "Gabon"}, {"city": "Pamplona", "region": "Navarra", "geo": "82.63961, -107.75439", "zipcode": "98378", "country": "Nauru"}, {"city": "Saint-Oyen", "region": "VD", "geo": "-64.32973, 106.97775", "zipcode": "08088", "country": "Saint Helena, Ascension and Tristan da Cunha"}, {"city": "Sant'Elpidio a Mare", "region": "Marche", "geo": "87.67492, 103.1823", "zipcode": "01693", "country": "New Zealand"}, {"city": "Toledo", "region": "Ohio", "geo": "-37.63824, 173.21332", "zipcode": "47122", "country": "Grenada"}, {"city": "Cagnes-sur-Mer", "region": "Provence-Alpes-C\u00c3\u00b4te d'Azur", "geo": "-29.71109, 30.3691", "zipcode": "15888", "country": "Korea, South"}, {"city": "Henderson", "region": "Nevada", "geo": "14.49132, 104.23211", "zipcode": "41396", "country": "Saint Martin"}, {"city": "Cambridge Bay", "region": "Nunavut", "geo": "-61.45193, 138.44957", "zipcode": "49369", "country": "Lithuania"}, {"city": "Lac Ste. Anne", "region": "Alberta", "geo": "4.50997, -36.23855", "zip": "78922", "country": "Chad"}, {"city": "Norfolk", "region": "VA", "geo": "-32.8141, -95.11215", "zip": "97556", "country": "Liberia"}, {"city": "Almere", "region": "Fl", "geo": "22.31471, -5.0771", "zip": "49038", "country": "Paraguay"}, {"city": "Hamilton", "region": "North Island", "geo": "-13.60609, 12.74015", "zip": "41596", "country": "Costa Rica"}, {"city": "Klosterneuburg", "region": "N\u00c3", "geo": "25.03109, 138.03223", "zip": "29655", "country": "Canada"}, {"city": "Darwin", "region": "Northern Territory", "geo": "-32.07988, -99.03488", "zip": "88545", "country": "Namibia"}, {"city": "Gifhorn", "region": "NI", "geo": "-28.86466, -61.69297", "zip": "18847", "country": "Canada"}, {"city": "Cortil-Noirmont", "region": "WB", "geo": "30.75562, -13.6778", "zip": "81355", "country": "United Arab Emirates"}, {"city": "Greymouth", "region": "South Island", "geo": "62.26017, -28.30744", "zip": "79312", "country": "Uzbekistan"}, {"city": "Alacant", "region": "Comunitat Valenciana", "geo": "-86.16001, -57.19054", "zip": "45830", "country": "Guyana"}, {"city": "Stonewall", "region": "MB", "geo": "1.73256, 2.93403", "zip": "61908", "country": "Northern Mariana Islands"}, {"city": "Whittlesey", "region": "CA", "geo": "77.07768, 17.22972", "zip": "09912", "country": "Portugal"}, {"city": "Hoogeveen", "region": "Drenthe", "geo": "26.13736, 127.72445", "zip": "46425", "country": "Macao"}, {"city": "Forres", "region": "MO", "geo": "83.36018, 42.30881", "zip": "89135", "country": "Christmas Island"}, {"city": "Opheylissem", "region": "WB", "geo": "83.28651, -164.78335", "zip": "84725", "country": "Bouvet Island"}, {"city": "Maracana\u00c3\u00ba", "region": "Cear\u00c3\u00a1", "geo": "-4.19385, 170.5779", "zip": "87044", "country": "Martinique"}, {"city": "Wanaka", "region": "SI", "geo": "-65.29844, -115.70789", "zip": "62544", "country": "Cuba"}, {"city": "Bicester", "region": "OX", "geo": "83.06654, -168.65588", "zip": "10484", "country": "Bouvet Island"}, {"city": "Hard", "region": "Vorarlberg", "geo": "-30.68413, -105.61029", "zip": "61431", "country": "Antarctica"}, {"city": "Hoeilaart", "region": "Vlaams-Brabant", "geo": "85.60268, 66.89173", "zip": "07758", "country": "Brunei"}, {"city": "Hamburg", "region": "Hamburg", "geo": "-80.53699, -95.66337", "zip": "71719", "country": "Tuvalu"}, {"city": "Poitiers", "region": "Poitou-Charentes", "geo": "47.13948, 105.89324", "zip": "30622", "country": "South Sudan"}, {"city": "Hoorn", "region": "Noord Holland", "geo": "13.13645, -121.06457", "zip": "42364", "country": "Suriname"}, {"city": "Palmerston", "region": "NT", "geo": "-12.36891, 12.34982", "zip": "22367", "country": "Cape Verde"}, {"city": "Tallahassee", "region": "Florida", "geo": "-73.83279, -152.43251", "zip": "75860", "country": "Colombia"}, {"city": "Canberra", "region": "Australian Capital Territories", "geo": "-70.61643, -68.74387", "zip": "50665", "country": "Micronesia"}, {"city": "Roosendaal", "region": "N.", "geo": "81.66834, 78.01773", "zip": "76771", "country": "Mexico"}, {"city": "Parla", "region": "MA", "geo": "-39.71636, 144.60654", "zip": "09804", "country": "Liechtenstein"}, {"city": "Melilla", "region": "ME", "geo": "59.2427, 89.74648", "zip": "72109", "country": "Montenegro"}, {"city": "Dorgali", "region": "SA", "geo": "-43.74721, -102.31465", "zip": "10088", "country": "Turks and Caicos Islands"}, {"city": "Campinas", "region": "SP", "geo": "-52.47202, 106.32287", "zip": "53792", "country": "Macao"}, {"city": "Aparecida de Goi\u00c3\u00a2nia", "region": "GO", "geo": "-84.79425, 160.86889", "zip": "87097", "country": "Sint Maarten"}, {"city": "Murcia", "region": "MU", "geo": "-55.28494, 112.73217", "zip": "02552", "country": "Northern Mariana Islands"}, {"city": "Sant'Eufemia a Maiella", "region": "AB", "geo": "1.86943, 115.82415", "zip": "26198", "country": "Cura\u00c3\u00a7ao"}, {"city": "S\u00c3\u00a3o Jo\u00c3\u00a3o de Meriti", "region": "RJ", "geo": "-2.00021, -125.19272", "zip": "55717", "country": "Timor-Leste"}, {"city": "Bouge", "region": "NA", "geo": "-36.04747, 34.78675", "zip": "73374", "country": "Namibia"}, {"city": "Paulatuk", "region": "NT", "geo": "65.49966, -25.82693", "zip": "90617", "country": "Marshall Islands"}, {"city": "Marneffe", "region": "LU", "geo": "51.10926, 126.76222", "zip": "00555", "country": "Ukraine"}, {"city": "Freising", "region": "Bavaria", "geo": "-23.46781, -12.63567", "zip": "36942", "country": "Christmas Island"}, {"city": "Oudergem", "region": "Brussels Hoofdstedelijk Gewest", "geo": "37.65569, 108.73469", "zip": "25043", "country": "Burundi"}, {"city": "Salem", "region": "OR", "geo": "33.34153, 83.94696", "zip": "40293", "country": "Puerto Rico"}, {"city": "Poggio San Marcello", "region": "MA", "geo": "48.63375, 130.95024", "zip": "22423", "country": "Slovenia"}, {"city": "Aalbeke", "region": "West-Vlaanderen", "geo": "-74.22454, -101.66157", "zip": "07571", "country": "Heard Island and Mcdonald Islands"}, {"city": "Rangiora", "region": "SI", "geo": "22.223, -127.64866", "zip": "38985", "country": "Saint Kitts and Nevis"}, {"city": "Idaho Falls", "region": "Idaho", "geo": "24.63897, -27.33398", "zip": "62286", "country": "Slovakia"}, {"city": "San Marcello Pistoiese", "region": "TO", "geo": "15.45306, 143.23317", "zip": "64126", "country": "Haiti"}, {"city": "Newtonmore", "region": "IN", "geo": "16.10967, 43.06549", "zip": "74090", "country": "Switzerland"}, {"city": "Mariakerke", "region": "OV", "geo": "-79.47833, 39.20145", "zip": "16635", "country": "El Salvador"}, {"city": "Whitehorse", "region": "YK", "geo": "-51.0896, 63.62421", "zip": "24197", "country": "France"}, {"city": "Reading", "region": "PA", "geo": "-17.24588, 179.95802", "zip": "88886", "country": "Brazil"}, {"city": "Charleville-M\u00c3\u00a9zi\u00c3\u00a8res", "region": "Ch", "geo": "57.71915, -47.17432", "zip": "47818", "country": "Rwanda"}, {"city": "Columbus", "region": "OH", "geo": "77.16257, 79.43027", "zip": "15496", "country": "Panama"}, {"city": "Siquirres", "region": "L", "geo": "37.94798, -130.45992", "zip": "18710", "country": "Israel"}, {"city": "Alajuela", "region": "A", "geo": "60.51596, 147.46337", "zip": "05516", "country": "Albania"}, {"city": "Mondolfo", "region": "MA", "geo": "72.96391, -167.08776", "zip": "82150", "country": "Turkey"}, {"city": "Palma de Mallorca", "region": "BA", "geo": "-75.78426, -172.16392", "zip": "54426", "country": "Paraguay"}, {"city": "Renlies", "region": "HE", "geo": "-38.73302, -149.24354", "zip": "55726", "country": "Israel"}, {"city": "Fochabers", "region": "MO", "geo": "71.18275, -66.0332", "zip": "87920", "country": "Tajikistan"}, {"city": "Deutschkreutz", "region": "Bg", "geo": "-46.07257, 34.69353", "zip": "93983", "country": "Korea, South"}, {"city": "Liverpool", "region": "LA", "geo": "37.10383, 168.93541", "zip": "95687", "country": "Solomon Islands"}, {"city": "Upper Hutt", "region": "NI", "geo": "-11.37968, -179.74894", "zip": "09793", "country": "Antarctica"}, {"city": "Maranguape", "region": "Cear\u00c3\u00a1", "geo": "50.74375, 110.96882", "zip": "41905", "country": "French Southern Territories"}, {"city": "Martigues", "region": "Pr", "geo": "-16.27414, 105.21028", "zip": "24375", "country": "Nauru"}, {"city": "Matera", "region": "BA", "geo": "35.97397, 54.40655", "zip": "14608", "country": "Kiribati"}, {"city": "Stratford", "region": "PE", "geo": "19.02251, 67.94877", "zip": "17870", "country": "Saint Barth\u00c3\u00a9lemy"}, {"city": "St. Veit an der Glan", "region": "Carinthia", "geo": "62.80685, -168.03343", "zip": "19189", "country": "Albania"}, {"city": "Orange", "region": "New South Wales", "geo": "71.52419, 142.24244", "zip": "56169", "country": "Anguilla"}, {"city": "Palencia", "region": "Castilla y Le\u00c3\u00b3n", "geo": "-86.24938, -80.34679", "zip": "80642", "country": "Netherlands"}, {"city": "Dollard-des-Ormeaux", "region": "QC", "geo": "-35.42858, 78.1669", "zip": "39774", "country": "Swaziland"}, {"city": "Monte Vidon Corrado", "region": "MA", "geo": "22.99103, -91.17718", "zip": "01588", "country": "Iraq"}, {"city": "Vernon", "region": "Ha", "geo": "73.69542, 41.56254", "zip": "25531", "country": "Bosnia and Herzegovina"}, {"city": "Clare", "region": "New Brunswick", "geo": "-2.99196, 156.13927", "zip": "13351", "country": "United States Minor Outlying Islands"}, {"city": "Lutsel K'e", "region": "Northwest Territories", "geo": "29.51125, 0.07065", "zip": "27136", "country": "Portugal"}, {"city": "Prince Albert", "region": "Saskatchewan", "geo": "-54.3483, -125.71011", "zip": "95007", "country": "Venezuela"}, {"city": "Temuka", "region": "South Island", "geo": "-22.39293, 1.76413", "zip": "67085", "country": "Peru"}, {"city": "San Jos\u00c3\u00a9 de Alajuela", "region": "A", "geo": "-87.33637, 86.11469", "zip": "34521", "country": "Bolivia"}, {"city": "Pickering", "region": "Ontario", "geo": "77.16393, -93.40458", "zip": "81556", "country": "Chad"}, {"city": "Oss", "region": "N.", "geo": "60.4434, 59.00084", "zip": "45399", "country": "Mongolia"}, {"city": "Grave", "region": "Noord Brabant", "geo": "-2.74407, 64.97835", "zip": "87111", "country": "Congo, the Democratic Republic of the"}, {"city": "Balclutha", "region": "SI", "geo": "0.76829, 164.39869", "zip": "68276", "country": "Tajikistan"}, {"city": "Waitakere", "region": "North Island", "geo": "-67.41676, 76.17011", "zip": "74847", "country": "New Caledonia"}, {"city": "Oldenzaal", "region": "Overijssel", "geo": "24.99873, -134.39489", "zip": "19573", "country": "Azerbaijan"}, {"city": "Aklavik", "region": "NT", "geo": "83.67881, 168.14903", "zip": "67373", "country": "United Arab Emirates"}, {"city": "Salisbury", "region": "Wiltshire", "geo": "17.09789, 108.37549", "zip": "04925", "country": "Christmas Island"}, {"city": "Christchurch", "region": "South Island", "geo": "61.87511, -164.47008", "zip": "37463", "country": "Tunisia"}, {"city": "Coutisse", "region": "Namen", "geo": "15.96866, -127.21841", "zip": "99291", "country": "Belgium"}, {"city": "Oviedo", "region": "Principado de Asturias", "geo": "-41.93095, 38.62659", "zip": "70089", "country": "Tanzania"}, {"city": "Dole", "region": "Franche-Comt\u00c3\u00a9", "geo": "-54.83406, 167.94571", "zip": "43060", "country": "Micronesia"}, {"city": "Soissons", "region": "Picardie", "geo": "-46.55545, -165.4171", "zip": "27788", "country": "Sudan"}, {"city": "Nurallao", "region": "SA", "geo": "-49.01734, 173.44454", "zip": "46325", "country": "Hungary"}, {"city": "Selva di Cadore", "region": "Veneto", "geo": "9.84011, 63.2361", "zip": "93016", "country": "Myanmar"}, {"city": "Motueka", "region": "South Island", "geo": "-79.85918, 6.28564", "zip": "70190", "country": "Niger"}, {"city": "Assen", "region": "Drenthe", "geo": "6.05407, 5.70389", "zip": "76787", "country": "Cocos (Keeling) Islands"}, {"city": "Gu\u00c3\u00a1piles", "region": "L", "geo": "1.84182, -90.56054", "zip": "18053", "country": "El Salvador"}, {"city": "Maple Creek", "region": "SK", "geo": "60.81464, -86.98608", "zip": "65749", "country": "Lebanon"}, {"city": "New Radnor", "region": "RA", "geo": "49.33745, 179.22146", "zip": "55276", "country": "El Salvador"}, {"city": "Gruitrode", "region": "Limburg", "geo": "64.44464, -15.08107", "zip": "57724", "country": "Argentina"}, {"city": "Benalla", "region": "VI", "geo": "-43.87215, -40.59366", "zip": "81886", "country": "Lesotho"}, {"city": "Rockingham", "region": "WA", "geo": "8.87126, 118.63538", "zip": "56683", "country": "Lithuania"}, {"city": "Dokkum", "region": "Fr", "geo": "-88.9512, 161.72938", "zip": "13925", "country": "Bangladesh"}, {"city": "Lake Cowichan", "region": "BC", "geo": "70.95081, -22.6725", "zip": "24121", "country": "Greece"}, {"city": "Guardia Perticara", "region": "BA", "geo": "-34.33544, -127.50601", "zip": "65304", "country": "Ethiopia"}, {"city": "Warrnambool", "region": "VI", "geo": "-67.14499, 51.66567", "zip": "83986", "country": "Hungary"}, {"city": "Sint-Lambrechts-Woluwe", "region": "BU", "geo": "64.30927, -3.90945", "zip": "22180", "country": "Guyana"}, {"city": "Colmar", "region": "Al", "geo": "-54.79101, 3.39722", "zip": "15717", "country": "Cape Verde"}, {"city": "Virginia Beach", "region": "VA", "geo": "-83.76345, -113.48689", "zip": "74398", "country": "Cyprus"}, {"city": "Saint-Herblain", "region": "Pays de la Loire", "geo": "-69.36336, -60.13143", "zip": "39391", "country": "Saint Pierre and Miquelon"}, {"city": "Nelson", "region": "SI", "geo": "-50.29544, 102.09771", "zip": "50233", "country": "Tokelau"}, {"city": "Bayreuth", "region": "BY", "geo": "16.7407, -152.72928", "zip": "75265", "country": "Lithuania"}, {"city": "Huntley", "region": "AB", "geo": "-15.21781, 128.11523", "zip": "87301", "country": "Sierra Leone"}, {"city": "Donstiennes", "region": "HE", "geo": "46.62898, -4.50273", "zip": "92222", "country": "Somalia"}, {"city": "Paisley", "region": "RF", "geo": "73.64325, 25.44136", "zip": "23174", "country": "Burundi"}, {"city": "South Portland", "region": "Maine", "geo": "89.01932, -25.90105", "zip": "76743", "country": "Virgin Islands, British"}, {"city": "Zaventem", "region": "Vlaams-Brabant", "geo": "-8.29612, 13.75063", "zip": "65559", "country": "Sao Tome and Principe"}, {"city": "Vienna", "region": "Wi", "geo": "-8.16598, -8.3251", "zip": "74033", "country": "Sri Lanka"}, {"city": "Tilburg", "region": "Noord Brabant", "geo": "13.21074, -12.5562", "zip": "86818", "country": "Samoa"}, {"city": "Gander", "region": "Newfoundland and Labrador", "geo": "25.99831, -136.44554", "zip": "42540", "country": "Philippines"}, {"city": "Casper", "region": "Wyoming", "geo": "9.69044, 52.71568", "zip": "00718", "country": "Kuwait"}, {"city": "Richmond", "region": "Quebec", "geo": "87.5455, 135.76343", "zip": "02341", "country": "Canada"}, {"city": "Meridian", "region": "Idaho", "geo": "57.17166, -115.83982", "zip": "96120", "country": "United Arab Emirates"}, {"city": "Kuurne", "region": "WV", "geo": "-27.82809, -1.67491", "zip": "60291", "country": "Hong Kong"}, {"city": "Louisville", "region": "Kentucky", "geo": "69.63751, 75.33196", "zip": "36007", "country": "Albania"}, {"city": "Kufstein", "region": "Ti", "geo": "5.52108, -120.50904", "zip": "73299", "country": "Madagascar"}, {"city": "Cagliari", "region": "Sardegna", "geo": "-68.41083, 81.65625", "zip": "26386", "country": "Sweden"}, {"city": "Lebach", "region": "Schleswig-Holstein", "geo": "-78.84882, -58.70022", "zip": "21169", "country": "Sao Tome and Principe"}, {"city": "Machynlleth", "region": "Montgomeryshire", "geo": "27.10815, -105.46145", "zip": "82604", "country": "Isle of Man"}, {"city": "Campochiaro", "region": "MO", "geo": "83.68906, 133.67838", "zip": "20124", "country": "Korea, South"}, {"city": "Le Petit-Quevilly", "region": "Haute-Normandie", "geo": "-6.29368, 3.64033", "zip": "63456", "country": "Isle of Man"}, {"city": "Wrigley", "region": "NT", "geo": "10.16848, 88.682", "zip": "43402", "country": "Benin"}, {"city": "Levin", "region": "North Island", "geo": "-24.0374, 19.14542", "zip": "72008", "country": "Western Sahara"}, {"city": "Divin\u00c3\u00b3polis", "region": "MG", "geo": "74.81846, 52.56676", "zip": "02637", "country": "Brazil"}, {"city": "Paranagu\u00c3\u00a1", "region": "PR", "geo": "-60.29727, 46.45486", "zip": "23625", "country": "Saint Lucia"}, {"city": "Maple Ridge", "region": "British Columbia", "geo": "53.15686, -19.05966", "zip": "35388", "country": "South Georgia and The South Sandwich Islands"}, {"city": "Baddeck", "region": "Nova Scotia", "geo": "85.30298, 36.18331", "zip": "61609", "country": "Aruba"}, {"city": "Aschersleben", "region": "ST", "geo": "-26.48387, -24.99117", "zip": "07402", "country": "Canada"}, {"city": "Belford Roxo", "region": "RJ", "geo": "-84.46277, -112.8278", "zip": "16880", "country": "Eritrea"}, {"city": "Tranent", "region": "East Lothian", "geo": "30.56466, 84.89027", "zip": "31782", "country": "Russian Federation"}, {"city": "San Pablo", "region": "Heredia", "geo": "-59.65368, -87.71177", "zip": "50809", "country": "Congo (Brazzaville)"}, {"city": "Wellington", "region": "North Island", "geo": "-29.96649, 6.2459", "zip": "76170", "country": "New Caledonia"}, {"city": "Lansing", "region": "Michigan", "geo": "-17.66012, -18.47095", "zip": "52816", "country": "Iraq"}, {"city": "Roio del Sangro", "region": "Abruzzo", "geo": "45.87639, -153.81179", "zip": "25515", "country": "Australia"}, {"city": "Mosciano Sant'Angelo", "region": "AB", "geo": "32.40055, 109.43553", "zip": "00278", "country": "British Indian Ocean Territory"}, {"city": "Empoli", "region": "Toscana", "geo": "-48.13877, 164.14796", "zip": "57305", "country": "Iran"}, {"city": "Burntisland", "region": "Fife", "geo": "18.82634, 52.60484", "zip": "96080", "country": "Indonesia"}, {"city": "Montauban", "region": "Midi-Pyr\u00c3\u00a9n\u00c3\u00a9es", "geo": "-4.8315, -10.71973", "zip": "85373", "country": "Bonaire, Sint Eustatius and Saba"}, {"city": "Flin Flon", "region": "MB", "geo": "25.66112, -19.81503", "zip": "07551", "country": "Nigeria"}, {"city": "Newquay", "region": "CO", "geo": "68.72008, 7.66988", "zip": "05891", "country": "Lesotho"}, {"city": "Moulins", "region": "Au", "geo": "50.70167, 38.41142", "zip": "86908", "country": "Netherlands"}, {"city": "Narbonne", "region": "Languedoc-Roussillon", "geo": "-86.42155, 41.55483", "zip": "31265", "country": "South Sudan"}, {"city": "Destelbergen", "region": "OV", "geo": "-31.82636, -162.76622", "zip": "27763", "country": "Bonaire, Sint Eustatius and Saba"}, {"city": "Sch\u00ef\u00bf\u00bdnebeck", "region": "ST", "geo": "51.79637, 141.35484", "zip": "08753", "country": "Finland"}, {"city": "Fortaleza", "region": "CE", "geo": "-16.64383, 60.55572", "zip": "20457", "country": "Comoros"}, {"city": "Clermont-Ferrand", "region": "Au", "geo": "-32.1123, 28.76064", "zip": "99348", "country": "Sao Tome and Principe"}, {"city": "Fort Worth", "region": "TX", "geo": "30.96355, -167.7131", "zip": "21977", "country": "C\u00c3\u00b4te D'Ivoire (Ivory Coast)"}, {"city": "Sobral", "region": "CE", "geo": "-18.93607, -7.40215", "zip": "48171", "country": "Tonga"}, {"city": "Stralsund", "region": "MV", "geo": "25.41689, -3.57508", "zip": "69678", "country": "Colombia"}, {"city": "Chattanooga", "region": "TN", "geo": "-7.24786, 57.6533", "zip": "98509", "country": "Faroe Islands"}, {"city": "Wolfurt", "region": "Vorarlberg", "geo": "-71.4821, 80.06086", "zip": "34383", "country": "Egypt"}, {"city": "Cheyenne", "region": "WY", "geo": "-11.14914, -113.44887", "zip": "13708", "country": "Gabon"}, {"city": "Orl\u00c3\u00a9ans", "region": "Ce", "geo": "-4.05175, 129.86414", "zip": "13994", "country": "Botswana"}, {"city": "Huntsville", "region": "AL", "geo": "34.88144, 101.27545", "zip": "12176", "country": "Antigua and Barbuda"}, {"city": "Castle Douglas", "region": "Kirkcudbrightshire", "geo": "9.16128, 101.61513", "zip": "56869", "country": "Western Sahara"}, {"city": "Pereto", "region": "Abruzzo", "geo": "-16.12143, 131.55242", "zip": "35971", "country": "Jersey"}, {"city": "Wetzlar", "region": "HE", "geo": "42.5739, -66.18936", "zip": "78172", "country": "Svalbard and Jan Mayen Islands"}, {"city": "Colorado Springs", "region": "CO", "geo": "-55.93042, -42.22991", "zip": "72782", "country": "Saint Barth\u00c3\u00a9lemy"}, {"city": "San Nicol\u00c3\u00a1s", "region": "C", "geo": "14.70572, -85.49833", "zip": "16023", "country": "United States Minor Outlying Islands"}, {"city": "Bungay", "region": "SF", "geo": "49.9458, -37.54999", "zip": "58235", "country": "Macedonia, the former Yugoslav Republic of"}, {"city": "Pointe-au-Pic", "region": "Quebec", "geo": "-50.87479, 173.44312", "zip": "18307", "country": "Liechtenstein"}, {"city": "Honolulu", "region": "Hawaii", "geo": "-85.86309, 110.17572", "zip": "87454", "country": "Monaco"}, {"city": "Bellevue", "region": "WA", "geo": "81.17006, -130.02836", "zip": "23815", "country": "Reunion"}, {"city": "Heusden", "region": "Noord Brabant", "geo": "-80.70541, 80.87185", "zip": "40194", "country": "Tanzania"}, {"city": "Logro\u00c3\u00b1o", "region": "La Rioja", "geo": "-42.76257, -136.15768", "zip": "53782", "country": "Slovenia"}, {"city": "Tournefeuille", "region": "Midi-Pyr\u00c3\u00a9n\u00c3\u00a9es", "geo": "33.5025, 146.0914", "zip": "65729", "country": "Denmark"}, {"city": "Cedar Rapids", "region": "IA", "geo": "66.25535, -144.20837", "zip": "17066", "country": "Saint Helena, Ascension and Tristan da Cunha"}, {"city": "Blenheim", "region": "SI", "geo": "10.24026, 149.3389", "zip": "93302", "country": "Oman"}, {"city": "Imst", "region": "Ti", "geo": "11.86462, 111.49793", "zip": "11085", "country": "Holy See (Vatican City State)"}, {"city": "Zwettl-Nieder\u00c3\u00b6sterreich", "region": "Lower Austria", "geo": "-89.36773, -150.93216", "zip": "28672", "country": "Saint Kitts and Nevis"}, {"city": "Wageningen", "region": "Gelderland", "geo": "44.17591, 62.66347", "zip": "42753", "country": "Moldova"}, {"city": "Middelburg", "region": "Zeeland", "geo": "21.57958, -65.26338", "zip": "14086", "country": "Latvia"}, {"city": "Houdemont", "region": "LX", "geo": "31.20164, -25.8605", "zip": "50056", "country": "Northern Mariana Islands"}, {"city": "Traun", "region": "Upper Austria", "geo": "-78.36228, 137.7868", "zip": "53193", "country": "China"}, {"city": "Essex", "region": "VT", "geo": "-45.37231, -110.14542", "zip": "71349", "country": "Iceland"}, {"city": "Charlottetown", "region": "Prince Edward Island", "geo": "-64.19227, 140.0622", "zip": "50795", "country": "Ireland"}, {"city": "Terneuzen", "region": "Zl", "geo": "-61.40185, -117.27887", "zip": "93634", "country": "Burkina Faso"}, {"city": "Leeuwarden", "region": "Friesland", "geo": "-87.88751, -146.30891", "zip": "63604", "country": "Antarctica"}, {"city": "Wilmington", "region": "Delaware", "geo": "48.13623, -25.07537", "zip": "30749", "country": "Sweden"}, {"city": "Hattiesburg", "region": "Mississippi", "geo": "-27.54206, 106.03544", "zip": "65097", "country": "Virgin Islands, British"}, {"city": "Camaragibe", "region": "PE", "geo": "-4.27824, 94.21942", "zip": "31127", "country": "Argentina"}, {"city": "Istres", "region": "Pr", "geo": "-9.16215, 15.47364", "zip": "41748", "country": "Viet Nam"}, {"city": "Ravenstein", "region": "Noord Brabant", "geo": "57.41277, 63.57006", "zip": "02990", "country": "Nauru"}, {"city": "Cobourg", "region": "ON", "geo": "85.79334, -59.37316", "zip": "86849", "country": "Zambia"}, {"city": "Jette", "region": "BU", "geo": "31.63325, -36.82462", "zip": "37777", "country": "Korea, North"}, {"city": "Donosti", "region": "PV", "geo": "-74.50893, -19.40378", "zip": "92834", "country": "Aruba"}, {"city": "Wyoming", "region": "WY", "geo": "-46.15404, 95.00145", "zip": "39920", "country": "Azerbaijan"}, {"city": "Salzburg", "region": "Salzburg", "geo": "-41.84679, 73.49255", "zip": "62074", "country": "Samoa"}, {"city": "Savannah", "region": "Georgia", "geo": "-69.46914, 142.56138", "zip": "70344", "country": "Andorra"}, {"city": "Lauro de Freitas", "region": "Bahia", "geo": "-80.61575, 70.78896", "zip": "84296", "country": "Gibraltar"}, {"city": "Friedrichsdorf", "region": "HE", "geo": "-87.13759, 46.85427", "zip": "32224", "country": "Honduras"}, {"city": "Tulsa", "region": "Oklahoma", "geo": "-25.74528, -78.06259", "zip": "59822", "country": "Malawi"}, {"city": "Rio Verde", "region": "Goi\u00c3\u00a1s", "geo": "-19.79922, 91.11966", "zip": "95508", "country": "Germany"}, {"city": "Bonneville", "region": "NA", "geo": "63.3219, 44.52897", "zip": "59827", "country": "Western Sahara"}, {"city": "Carmen", "region": "Cartago", "geo": "38.26569, 162.52705", "zip": "20499", "country": "Canada"}, {"city": "Hartford", "region": "CT", "geo": "-63.19219, 31.02381", "zip": "30826", "country": "Bangladesh"}, {"city": "Waiheke Island", "region": "NI", "geo": "53.52898, -17.7973", "zip": "27427", "country": "Liechtenstein"}, {"city": "Dornbirn", "region": "Vorarlberg", "geo": "49.07309, -161.19193", "zip": "22384", "country": "Saint Kitts and Nevis"}, {"city": "Calle Blancos", "region": "San Jos\u00c3\u00a9", "geo": "51.57873, -145.08843", "zip": "38144", "country": "Viet Nam"}, {"city": "N\u00c3\u00aemes", "region": "La", "geo": "-47.54175, 114.71096", "zip": "54487", "country": "Cyprus"}, {"city": "Waiuku", "region": "NI", "geo": "-59.55129, 92.39425", "zip": "36183", "country": "Guam"}, {"city": "Marseille", "region": "Pr", "geo": "-51.56859, -23.88422", "zip": "33541", "country": "Uzbekistan"}, {"city": "Pangnirtung", "region": "Nunavut", "geo": "64.56543, -146.63965", "zip": "62371", "country": "Switzerland"}, {"city": "Milnathort", "region": "KR", "geo": "-42.46331, 24.60476", "zip": "73728", "country": "Gabon"}, {"city": "Tumbler Ridge", "region": "British Columbia", "geo": "-0.67697, -158.57802", "zip": "38001", "country": "Sao Tome and Principe"}, {"city": "Wernigerode", "region": "ST", "geo": "-61.64855, -79.10461", "zip": "09095", "country": "Korea, North"}, {"city": "Molfetta", "region": "PU", "geo": "54.41926, -31.60921", "zip": "16618", "country": "Botswana"}, {"city": "Bocchigliero", "region": "CA", "geo": "14.09251, 114.99189", "zip": "49655", "country": "Mauritania"}, {"city": "Rio de Janeiro", "region": "Rio de Janeiro", "geo": "6.3157, 152.82964", "zip": "47034", "country": "Uzbekistan"}, {"city": "Stokrooie", "region": "Limburg", "geo": "28.79249, 71.18181", "zip": "45187", "country": "Uzbekistan"}, {"city": "Carunchio", "region": "Abruzzo", "geo": "76.25144, 25.23636", "zip": "47950", "country": "Mayotte"}, {"city": "Fremantle", "region": "Western Australia", "geo": "-87.73442, -60.64919", "zip": "93509", "country": "Timor-Leste"}, {"city": "Villafalletto", "region": "Piemonte", "geo": "26.84811, 114.01307", "zip": "21381", "country": "Fiji"}, {"city": "Lelystad", "region": "Fl", "geo": "-16.90483, -82.45978", "zip": "19520", "country": "New Zealand"}, {"city": "Salvador", "region": "BA", "geo": "84.50091, 139.15355", "zip": "68971", "country": "Belgium"}, {"city": "Colombo", "region": "Paran\u00c3\u00a1", "geo": "-74.63368, -76.63968", "zip": "19802", "country": "Sierra Leone"}, {"city": "Ashburton", "region": "SI", "geo": "65.14748, 98.7714", "zip": "74459", "country": "Czech Republic"}, {"city": "Neunkirchen", "region": "Schleswig-Holstein", "geo": "-81.55232, -34.88717", "zip": "16334", "country": "France"}, {"city": "Castres", "region": "Mi", "geo": "-25.10061, -126.6593", "zip": "82125", "country": "Jordan"}, {"city": "Bayeux", "region": "PB", "geo": "76.60763, -28.40164", "zip": "06802", "country": "Norfolk Island"}, {"city": "Nijmegen", "region": "Gelderland", "geo": "43.1463, -142.56385", "zip": "46286", "country": "Pakistan"}, {"city": "Trier", "region": "Rhineland-Palatinate", "geo": "-70.54269, -168.37819", "zip": "88945", "country": "Reunion"}, {"city": "San Rafael Abajo", "region": "SJ", "geo": "-13.95331, 41.89773", "zip": "05180", "country": "Norfolk Island"}, {"city": "Whyalla", "region": "SA", "geo": "-28.66491, 56.26404", "zip": "09795", "country": "Belgium"}, {"city": "Lint", "region": "Antwerpen", "geo": "-53.30332, -161.22202", "zip": "79006", "country": "Nepal"}, {"city": "Las Palmas", "region": "Canarias", "geo": "-14.82304, 55.83823", "zip": "61155", "country": "Isle of Man"}, {"city": "Annapolis Royal", "region": "Nova Scotia", "geo": "-86.28868, 71.14088", "zip": "58330", "country": "Denmark"}, {"city": "Pike Creek", "region": "DE", "geo": "83.6046, 9.25326", "zip": "97889", "country": "Myanmar"}, {"city": "Itzehoe", "region": "Schleswig-Holstein", "geo": "39.31079, 56.49864", "zip": "38889", "country": "Christmas Island"}, {"city": "Saint-L\u00ef\u00bf\u00bdonard", "region": "NB", "geo": "-7.26278, -74.69402", "zip": "59453", "country": "Cambodia"}, {"city": "Helensburgh", "region": "Dunbartonshire", "geo": "51.61505, 94.98548", "zip": "97099", "country": "Kazakhstan"}, {"city": "Ollolai", "region": "Sardegna", "geo": "20.89234, 158.28848", "zip": "64016", "country": "Chile"}, {"city": "Mechelen-aan-de-Maas", "region": "L.", "geo": "-30.85238, 108.1607", "zip": "69526", "country": "Northern Mariana Islands"}, {"city": "Albury", "region": "NS", "geo": "8.02665, 77.12138", "zip": "99021", "country": "Heard Island and Mcdonald Islands"}, {"city": "Caucaia", "region": "Cear\u00c3\u00a1", "geo": "83.34794, -131.37798", "zip": "69194", "country": "Azerbaijan"}, {"city": "Schwaz", "region": "Ti", "geo": "28.88995, -74.7578", "zip": "07038", "country": "Chile"}, {"city": "King's Lynn", "region": "NF", "geo": "-6.62821, 55.88241", "zip": "66998", "country": "Bonaire, Sint Eustatius and Saba"}, {"city": "Missoula", "region": "MT", "geo": "84.07004, 44.3607", "zip": "56565", "country": "Heard Island and Mcdonald Islands"}, {"city": "Isle-aux-Coudres", "region": "QC", "geo": "-13.97097, 142.16298", "zip": "88240", "country": "Seychelles"}, {"city": "Tucson", "region": "Arizona", "geo": "33.27333, 146.97767", "zip": "58997", "country": "Guadeloupe"}, {"city": "Finkenstein am Faaker See", "region": "Kt", "geo": "-19.98845, 77.39954", "zip": "24332", "country": "Uganda"}, {"city": "Auxerre", "region": "Bo", "geo": "-46.15096, -33.28964", "zip": "50286", "country": "Rwanda"}, {"city": "Sint-Michiels", "region": "WV", "geo": "89.28661, 115.5427", "zip": "04465", "country": "Western Sahara"}, {"city": "Montague", "region": "Prince Edward Island", "geo": "-31.47366, -166.10636", "zip": "45595", "country": "Belarus"}, {"city": "Rock Springs", "region": "Wyoming", "geo": "66.13554, -168.02715", "zip": "68072", "country": "Guyana"}, {"city": "Utrecht", "region": "Utrecht", "geo": "-13.8942, -134.54508", "zip": "21535", "country": "Grenada"}, {"city": "Bridgeport", "region": "Connecticut", "geo": "29.99212, -43.43605", "zip": "46568", "country": "Singapore"}, {"city": "Senftenberg", "region": "BB", "geo": "-65.11975, 58.29633", "zip": "93319", "country": "Antarctica"}, {"city": "Thionville", "region": "Lorraine", "geo": "-67.1455, -88.47263", "zip": "10236", "country": "Hungary"}, {"city": "Caen", "region": "Basse-Normandie", "geo": "43.72995, 85.11116", "zip": "62890", "country": "Egypt"}, {"city": "Barranca", "region": "Puntarenas", "geo": "-69.17537, 73.76697", "zip": "12191", "country": "Niger"}, {"city": "Llanwrtwd Wells", "region": "Brecknockshire", "geo": "84.06612, -173.1166", "zip": "57658", "country": "Monaco"}, {"city": "Deline", "region": "Northwest Territories", "geo": "14.29237, -9.77156", "zip": "20586", "country": "Grenada"}, {"city": "Beigem", "region": "Vlaams-Brabant", "geo": "-31.25727, 26.81089", "zip": "05125", "country": "Saint Lucia"}, {"city": "Jemeppe-sur-Sambre", "region": "Namen", "geo": "-27.46125, -31.88008", "zip": "47118", "country": "Brunei"}, {"city": "Workum", "region": "Friesland", "geo": "-86.86553, -150.53437", "zip": "33979", "country": "Tokelau"}, {"city": "Oban", "region": "AR", "geo": "72.35184, -143.84273", "zip": "20204", "country": "French Guiana"}, {"city": "Berlin", "region": "Berlin", "geo": "72.30285, -94.51402", "zip": "27734", "country": "Myanmar"}, {"city": "Reggio nell'Emilia", "region": "Emilia-Romagna", "geo": "-65.27961, -93.88212", "zip": "15976", "country": "Barbados"}, {"city": "Schwedt", "region": "Brandenburg", "geo": "47.34018, 171.2859", "zip": "96142", "country": "Saint Pierre and Miquelon"}, {"city": "Springdale", "region": "Arkansas", "geo": "-11.97356, 79.95898", "zip": "81130", "country": "Armenia"}, {"city": "Alphen aan den Rijn", "region": "Z.", "geo": "19.69341, -2.81685", "zip": "66216", "country": "French Polynesia"}, {"city": "Gravata\u00c3\u00ad", "region": "RS", "geo": "44.76224, -146.23483", "zip": "52064", "country": "Belize"}, {"city": "Bonnyville", "region": "Alberta", "geo": "16.80182, 66.0417", "zip": "58804", "country": "Bosnia and Herzegovina"}, {"city": "Scalloway", "region": "SH", "geo": "-47.74287, 119.61532", "zip": "82136", "country": "Australia"}, {"city": "Baden-Baden", "region": "Baden", "geo": "-56.36164, 97.2337", "zip": "86399", "country": "United States"}, {"city": "Prestatyn", "region": "Flintshire", "geo": "-20.49377, -151.04714", "zip": "84375", "country": "Sweden"}, {"city": "Itapipoca", "region": "Cear\u00c3\u00a1", "geo": "-23.24579, 117.45365", "zip": "54406", "country": "Serbia"}, {"city": "Gresham", "region": "OR", "geo": "62.61253, 26.89404", "zip": "17592", "country": "Mongolia"}, {"city": "Feldkirch", "region": "Vorarlberg", "geo": "-78.52495, 58.76637", "zip": "62732", "country": "Bahamas"}, {"city": "Tauranga", "region": "North Island", "geo": "43.14078, -47.23634", "zip": "56747", "country": "Eritrea"}, {"city": "M\u00ef\u00bf\u00bdnster", "region": "North Rhine-Westphalia", "geo": "-61.47994, -60.5453", "zip": "42535", "country": "Sint Maarten"}, {"city": "Little Rock", "region": "Arkansas", "geo": "18.6587, 101.27705", "zip": "50321", "country": "Guadeloupe"}, {"city": "Stade", "region": "NI", "geo": "-15.26854, 13.53202", "zip": "78219", "country": "Sri Lanka"}, {"city": "Ceuta", "region": "Ceuta", "geo": "70.36354, -156.30253", "zip": "23584", "country": "Uruguay"}, {"city": "Spijkenisse", "region": "Z.", "geo": "83.71545, -143.51247", "zip": "39105", "country": "Ethiopia"}, {"city": "Pizzoferrato", "region": "AB", "geo": "-43.2209, 71.44894", "zip": "07133", "country": "Lebanon"}, {"city": "Victor Harbor", "region": "South Australia", "geo": "-22.545, 6.78983", "zip": "85418", "country": "Isle of Man"}, {"city": "Castelnovo del Friuli", "region": "FV", "geo": "-42.77133, 132.50681", "zip": "24046", "country": "Albania"}, {"city": "Auburn", "region": "ME", "geo": "-1.93338, 8.55223", "zip": "28556", "country": "Suriname"}, {"city": "Strasbourg", "region": "Alsace", "geo": "36.55478, 20.9479", "zip": "18915", "country": "Tajikistan"}, {"city": "Ayr", "region": "AY", "geo": "-31.22679, -43.06897", "zip": "06066", "country": "New Zealand"}, {"city": "Port Lincoln", "region": "SA", "geo": "29.11236, -141.94859", "zip": "12827", "country": "Pitcairn Islands"}, {"city": "Lustenau", "region": "Vorarlberg", "geo": "36.74941, -143.37747", "zip": "05990", "country": "Timor-Leste"}, {"city": "Montpelier", "region": "Vermont", "geo": "48.56702, 144.21031", "zip": "77505", "country": "Central African Republic"}, {"city": "Woerden", "region": "Utrecht", "geo": "55.53332, 116.20612", "zip": "44463", "country": "Belgium"}, {"city": "G\u00ef\u00bf\u00bdppingen", "region": "BW", "geo": "-59.83802, -146.12448", "zip": "86536", "country": "Ghana"}, {"city": "Prenzlau", "region": "Brandenburg", "geo": "72.57871, -176.49188", "zip": "60985", "country": "Hungary"}, {"city": "Compi\u00c3\u00a8gne", "region": "Pi", "geo": "-12.02547, 132.2065", "zip": "66945", "country": "Samoa"}, {"city": "Sint-Gillis", "region": "Brussels Hoofdstedelijk Gewest", "geo": "61.22544, 119.74884", "zip": "39412", "country": "Nigeria"}, {"city": "Cleveland", "region": "OH", "geo": "-6.97571, 35.41304", "zip": "79367", "country": "Equatorial Guinea"}, {"city": "Mulhouse", "region": "Alsace", "geo": "-62.0946, -65.68207", "zip": "47257", "country": "United Arab Emirates"}, {"city": "Enna", "region": "Sicilia", "geo": "-88.66365, 38.47573", "zip": "39386", "country": "Latvia"}, {"city": "Rockville", "region": "MD", "geo": "72.55973, 9.52057", "zip": "35304", "country": "Monaco"}, {"city": "Townsville", "region": "Queensland", "geo": "-58.11992, 60.18702", "zip": "62979", "country": "Romania"}, {"city": "Roux-Miroir", "region": "Waals-Brabant", "geo": "-58.99119, -179.41927", "zip": "76933", "country": "Venezuela"}, {"city": "Stony Plain", "region": "Alberta", "geo": "-52.59685, 37.78234", "zip": "30627", "country": "Marshall Islands"}, {"city": "Neusiedl am See", "region": "Burgenland", "geo": "-21.10218, -161.23232", "zip": "60726", "country": "Suriname"}, {"city": "Devizes", "region": "WI", "geo": "80.80031, -1.21436", "zip": "41581", "country": "Burundi"}, {"city": "Sankt Wendel", "region": "Schleswig-Holstein", "geo": "-13.88607, -6.63466", "zip": "32667", "country": "Saint Kitts and Nevis"}, {"city": "Lloydminster", "region": "SK", "geo": "-26.07589, -172.66908", "zip": "03601", "country": "Mayotte"}, {"city": "Biggleswade", "region": "BD", "geo": "62.85062, -176.91822", "zip": "04602", "country": "French Southern Territories"}, {"city": "Jauchelette", "region": "Waals-Brabant", "geo": "-45.42912, -13.14783", "zip": "96736", "country": "Liberia"}, {"city": "Saint-Di\u00c3\u00a9-des-Vosges", "region": "Lo", "geo": "-71.27761, 140.28563", "zip": "80962", "country": "Chad"}, {"city": "Feira de Santana", "region": "Bahia", "geo": "26.1552, -79.54527", "zip": "64476", "country": "C\u00c3\u00b4te D'Ivoire (Ivory Coast)"}, {"city": "Zirl", "region": "Tyrol", "geo": "-81.96543, 166.28208", "zip": "38394", "country": "\u00c3\u0085land Islands"}, {"city": "Alen\u00c3\u00a7on", "region": "Ba", "geo": "-42.89185, -133.93052", "zip": "41234", "country": "British Indian Ocean Territory"}, {"city": "Bear", "region": "Delaware", "geo": "-72.17821, 104.20137", "zip": "50692", "country": "British Indian Ocean Territory"}, {"city": "Ananindeua", "region": "PA", "geo": "9.6693, -159.94105", "zip": "22123", "country": "Greenland"}, {"city": "Joinville", "region": "SC", "geo": "43.40572, 48.39194", "zip": "58247", "country": "Estonia"}, {"city": "Sevilla", "region": "Andaluc\u00c3\u00ada", "geo": "40.24224, -56.09571", "zip": "86261", "country": "Fiji"}, {"city": "Sens", "region": "Bourgogne", "geo": "-63.15781, 64.02219", "zip": "73030", "country": "Tuvalu"}, {"city": "Yellowhead County", "region": "Alberta", "geo": "-75.72557, -11.21103", "zip": "52048", "country": "Gambia"}, {"city": "Ingolstadt", "region": "Bavaria", "geo": "-47.43668, -74.85715", "zip": "44691", "country": "\u00c3\u0085land Islands"}, {"city": "Kapolei", "region": "HI", "geo": "19.98509, 2.48113", "zip": "44499", "country": "Bahamas"}, {"city": "B\u00c3\u00a8gles", "region": "Aquitaine", "geo": "-22.42392, 35.78992", "zip": "16693", "country": "Indonesia"}, {"city": "Vieuxville", "region": "Luik", "geo": "-46.78037, -102.19901", "zip": "83625", "country": "Costa Rica"}, {"city": "Reus", "region": "CA", "geo": "-60.72858, 87.77322", "zip": "11812", "country": "Montserrat"}, {"city": "Altm\u00c3\u00bcnster", "region": "O\u00c3", "geo": "19.01338, 89.56207", "zip": "74918", "country": "Cape Verde"}, {"city": "Lithgow", "region": "New South Wales", "geo": "-17.74431, -147.53516", "zip": "67506", "country": "Canada"}, {"city": "Huissen", "region": "Gl", "geo": "89.37498, 142.31579", "zip": "64819", "country": "Holy See (Vatican City State)"}, {"city": "Montreal", "region": "Quebec", "geo": "78.88892, -164.7737", "zip": "19837", "country": "Sint Maarten"}, {"city": "Stirling", "region": "ST", "geo": "7.67708, 127.17737", "zip": "51104", "country": "Moldova"}, {"city": "Zandhoven", "region": "AN", "geo": "-58.06453, 176.87092", "zip": "10968", "country": "Ukraine"}, {"city": "Huntly", "region": "NI", "geo": "22.77296, 89.2588", "zip": "28755", "country": "Grenada"}, {"city": "Namen", "region": "NA", "geo": "-1.83995, -125.15321", "zip": "78431", "country": "Antigua and Barbuda"}, {"city": "Wetaskiwin", "region": "AB", "geo": "-53.29944, -45.43621", "zip": "34005", "country": "Egypt"}, {"city": "Voitsberg", "region": "Styria", "geo": "-86.98395, -92.95586", "zip": "41667", "country": "Heard Island and Mcdonald Islands"}, {"city": "Breda", "region": "N.", "geo": "72.37232, 91.28514", "zip": "37554", "country": "Austria"}, {"city": "Beert", "region": "Vlaams-Brabant", "geo": "85.34879, 3.95122", "zip": "86596", "country": "Canada"}, {"city": "Cabo de Santo Agostinho", "region": "Pernambuco", "geo": "-58.39986, -145.27137", "zip": "12556", "country": "Northern Mariana Islands"}, {"city": "Hay River", "region": "Northwest Territories", "geo": "-86.21275, 0.89498", "zip": "32268", "country": "Comoros"}, {"city": "Hoofddorp", "region": "N.", "geo": "8.57457, 45.73779", "zip": "94592", "country": "Congo (Brazzaville)"}, {"city": "Bonavista", "region": "Newfoundland and Labrador", "geo": "-2.70635, -61.81956", "zip": "58406", "country": "Burundi"}, {"city": "Rennes", "region": "Br", "geo": "73.18327, -152.17271", "zip": "22847", "country": "Sint Maarten"}, {"city": "Castiglione Messer Raimondo", "region": "Abruzzo", "geo": "-61.39379, 130.28437", "zip": "42676", "country": "American Samoa"}, {"city": "Saint-Louis", "region": "Alsace", "geo": "-59.15189, -11.73541", "zip": "96796", "country": "Zimbabwe"}, {"city": "Falisolle", "region": "NA", "geo": "4.27723, 82.74804", "zip": "07774", "country": "Greenland"}, {"city": "Castanhal", "region": "Par\u00c3\u00a1", "geo": "-83.19714, 0.15204", "zip": "86353", "country": "Virgin Islands, British"}, {"city": "Great Yarmouth", "region": "Norfolk", "geo": "-75.21837, 28.50647", "zip": "66360", "country": "Saudi Arabia"}, {"city": "Oamaru", "region": "South Island", "geo": "32.39231, 62.99802", "zip": "35259", "country": "Indonesia"}, {"city": "Vitry-sur-Seine", "region": "\u00c3\u008ele-de-France", "geo": "-49.56778, -84.72645", "zip": "95409", "country": "Ecuador"}, {"city": "Spittal an der Drau", "region": "Kt", "geo": "44.86595, -133.52865", "zip": "19809", "country": "Malta"}, {"city": "Rosoux-Crenwick", "region": "Luik", "geo": "87.31445, 143.16306", "zip": "97784", "country": "Romania"}, {"city": "Macklin", "region": "Saskatchewan", "geo": "-53.56066, -31.88769", "zip": "91352", "country": "Norfolk Island"}, {"city": "K\u00c3\u00b6flach", "region": "St", "geo": "-72.14382, 111.9082", "zip": "22087", "country": "Reunion"}, {"city": "M\u00c3\u00b6dling", "region": "N\u00c3", "geo": "82.11366, 24.19458", "zip": "41175", "country": "Wallis and Futuna"}, {"city": "Cagli", "region": "Marche", "geo": "12.26983, -95.84379", "zip": "06729", "country": "Jordan"}, {"city": "Verrebroek", "region": "OV", "geo": "26.62954, -39.84656", "zip": "47434", "country": "Guinea"}, {"city": "Memphis", "region": "Tennessee", "geo": "15.61477, 118.242", "zip": "94196", "country": "Nigeria"}, {"city": "Pordenone", "region": "Friuli-Venezia Giulia", "geo": "76.23959, 2.76271", "zip": "40172", "country": "Liberia"}, {"city": "Griesheim", "region": "HE", "geo": "50.69428, 65.39198", "zip": "71865", "country": "C\u00c3\u00b4te D'Ivoire (Ivory Coast)"}, {"city": "Sankt Johann im Pongau", "region": "Salzburg", "geo": "63.07153, -102.18878", "zip": "44543", "country": "Norway"}, {"city": "Newcastle", "region": "NS", "geo": "-3.60992, -47.17386", "zip": "54016", "country": "Poland"}, {"city": "Horsham", "region": "Victoria", "geo": "-65.19477, 27.71807", "zip": "31431", "country": "Swaziland"}, {"city": "Zwolle", "region": "Overijssel", "geo": "-69.92868, 2.04284", "zip": "07279", "country": "Sierra Leone"}, {"city": "Flensburg", "region": "Schleswig-Holstein", "geo": "-81.02961, 140.83935", "zip": "25594", "country": "Micronesia"}, {"city": "Londrina", "region": "PR", "geo": "-38.95975, 150.41147", "zip": "31211", "country": "Isle of Man"}, {"city": "Pelotas", "region": "Rio Grande do Sul", "geo": "60.21097, 64.87967", "zip": "42665", "country": "Saint Vincent and The Grenadines"}, {"city": "Purral", "region": "San Jos\u00c3\u00a9", "geo": "-1.95211, 87.37493", "zip": "15065", "country": "Honduras"}, {"city": "Montemignaio", "region": "Toscana", "geo": "-56.66875, -13.52442", "zip": "59593", "country": "Belize"}, {"city": "Strausberg", "region": "BB", "geo": "-37.90772, 155.69627", "zip": "85477", "country": "Australia"}, {"city": "Greater Hobart", "region": "Tasmania", "geo": "-0.82384, -150.70554", "zip": "55920", "country": "Sierra Leone"}, {"city": "Watson Lake", "region": "YK", "geo": "-30.73358, -13.70035", "zip": "88576", "country": "Liberia"}, {"city": "Rhemes-Saint-Georges", "region": "VD", "geo": "-65.13397, -127.786", "zip": "65467", "country": "Virgin Islands, British"}, {"city": "Sh?diac", "region": "NB", "geo": "31.65635, -120.03942", "zip": "94205", "country": "Jordan"}, {"city": "K\u00ef\u00bf\u00bdthen", "region": "ST", "geo": "-63.08073, -123.26254", "zip": "68872", "country": "Pakistan"}, {"city": "Campbelltown", "region": "NS", "geo": "32.23491, -176.87352", "zip": "71729", "country": "French Polynesia"}, {"city": "Watermaal-Bosvoorde", "region": "BU", "geo": "15.58301, -49.23318", "zip": "89404", "country": "Guernsey"}, {"city": "Brisbane", "region": "Queensland", "geo": "18.55251, -76.06845", "zip": "99443", "country": "Jamaica"}, {"city": "Naperville", "region": "IL", "geo": "-3.01983, -146.48793", "zip": "57376", "country": "Moldova"}, {"city": "Joliet", "region": "IL", "geo": "0.25007, -112.98428", "zip": "89459", "country": "Cuba"}, {"city": "Port Augusta", "region": "South Australia", "geo": "37.68851, 174.09906", "zip": "11933", "country": "Cook Islands"}, {"city": "Melville", "region": "WA", "geo": "-1.3378, -113.77458", "zip": "32912", "country": "Mauritania"}, {"city": "Ch\u00c3\u00a2tellerault", "region": "Poitou-Charentes", "geo": "-71.94203, -84.08266", "zip": "93258", "country": "Venezuela"}, {"city": "Kaneohe", "region": "Hawaii", "geo": "-83.09871, -7.74964", "zip": "41284", "country": "Puerto Rico"}, {"city": "C\u00ef\u00bf\u00bdroux-Mousty", "region": "Waals-Brabant", "geo": "-38.60792, 37.88498", "zip": "14574", "country": "Aruba"}, {"city": "Thorold", "region": "Ontario", "geo": "63.58281, 28.9994", "zip": "34415", "country": "Kazakhstan"}, {"city": "A\u00c3\u00a7ail\u00c3\u00a2ndia", "region": "Maranh\u00c3\u00a3o", "geo": "-31.47071, 132.90719", "zip": "05432", "country": "Bonaire, Sint Eustatius and Saba"}, {"city": "Gladstone", "region": "QL", "geo": "-23.08022, -30.45655", "zip": "29282", "country": "Denmark"}, {"city": "Braunau am Inn", "region": "O\u00c3", "geo": "-13.75208, 144.07334", "zip": "99521", "country": "El Salvador"}, {"city": "Orvault", "region": "Pays de la Loire", "geo": "46.05972, 123.17143", "zip": "72764", "country": "Paraguay"}, {"city": "Damme", "region": "West-Vlaanderen", "geo": "48.13208, 62.83261", "zip": "39999", "country": "Czech Republic"}, {"city": "\u00c3\u0081vila", "region": "CL", "geo": "-49.75961, 143.00495", "zip": "62456", "country": "Guyana"}, {"city": "Lisieux", "region": "Ba", "geo": "-51.10805, 47.69906", "zip": "47011", "country": "Finland"}, {"city": "Geelong", "region": "VI", "geo": "43.24323, 108.48008", "zip": "26079", "country": "Czech Republic"}, {"city": "Ajaccio", "region": "Corse", "geo": "12.6279, -22.21411", "zip": "54059", "country": "Montserrat"}, {"city": "Zaragoza", "region": "Arag\u00c3\u00b3n", "geo": "-42.91226, -168.1018", "zip": "68151", "country": "Aruba"}, {"city": "Bad Kreuznach", "region": "RP", "geo": "37.50965, 9.85084", "zip": "52790", "country": "Chile"}, {"city": "Amersfoort", "region": "Utrecht", "geo": "-22.70335, 7.25659", "zip": "24550", "country": "Iceland"}, {"city": "Canmore", "region": "Alberta", "geo": "-55.83007, -154.74758", "zip": "05486", "country": "Suriname"}, {"city": "Cisano sul Neva", "region": "LI", "geo": "42.47587, -33.74259", "zip": "99259", "country": "Bermuda"}, {"city": "San Juan (San Juan de Tib\u00c3\u00a1s)", "region": "SJ", "geo": "29.77564, 134.38992", "zip": "11532", "country": "Saint Lucia"}, {"city": "Camrose", "region": "Alberta", "geo": "-80.0825, -4.26966", "zip": "38817", "country": "Togo"}, {"city": "Moustier-sur-Sambre", "region": "NA", "geo": "70.63526, 177.39969", "zip": "77898", "country": "Cuba"}, {"city": "Buckingham", "region": "BK", "geo": "69.93053, 123.01373", "zip": "41268", "country": "Bouvet Island"}, {"city": "Connah's Quay", "region": "FL", "geo": "-68.33834, -71.87031", "zip": "72006", "country": "Greenland"}, {"city": "Forst", "region": "BB", "geo": "72.64054, -169.35883", "zip": "85762", "country": "Qatar"}, {"city": "Wadgassen", "region": "Saarland", "geo": "-61.73074, 139.63235", "zip": "15402", "country": "Kuwait"}, {"city": "Pierrefonds", "region": "Quebec", "geo": "-8.45645, 39.69307", "zip": "30900", "country": "Paraguay"}, {"city": "Terlago", "region": "TA", "geo": "-66.15476, 68.33", "zip": "22711", "country": "Uganda"}, {"city": "Puntarenas", "region": "P", "geo": "33.53545, 79.25017", "zip": "89690", "country": "Uzbekistan"}, {"city": "Cetara", "region": "CA", "geo": "-80.70348, 173.80687", "zip": "09376", "country": "Georgia"}, {"city": "Colomiers", "region": "Mi", "geo": "-21.1902, 159.37168", "zip": "45043", "country": "Korea, South"}, {"city": "Rebecq", "region": "WB", "geo": "65.88655, 160.3104", "zip": "73491", "country": "San Marino"}, {"city": "Marystown", "region": "NL", "geo": "-39.50882, 178.2885", "zip": "76222", "country": "Senegal"}, {"city": "Saint-Martin", "region": "Namen", "geo": "-11.34654, 177.55776", "zip": "42832", "country": "Reunion"}, {"city": "Diadema", "region": "SP", "geo": "-33.41045, -7.04744", "zip": "61327", "country": "Isle of Man"}, {"city": "Saintes", "region": "Po", "geo": "29.38279, -146.90596", "zip": "86228", "country": "Iraq"}, {"city": "Sloten", "region": "Friesland", "geo": "-41.94484, -52.20653", "zip": "91168", "country": "Austria"}, {"city": "Vitrolles", "region": "Provence-Alpes-C\u00c3\u00b4te d'Azur", "geo": "-20.65371, 94.20243", "zip": "79632", "country": "Libya"}, {"city": "Shepparton", "region": "Victoria", "geo": "-18.14976, 148.51207", "zip": "55245", "country": "Italy"}, {"city": "Crystal Springs", "region": "AB", "geo": "-58.90829, -117.31232", "zip": "61444", "country": "Malta"}, {"city": "Bastia", "region": "Corse", "geo": "22.34785, 57.57827", "zip": "11280", "country": "Tanzania"}, {"city": "Brandon", "region": "MB", "geo": "-29.65941, -166.24319", "zip": "88990", "country": "Saint Barth\u00c3\u00a9lemy"}, {"city": "Beausejour", "region": "MB", "geo": "9.75323, -124.27034", "zip": "74961", "country": "Cocos (Keeling) Islands"}, {"city": "Las Vegas", "region": "NV", "geo": "-30.34825, -107.03419", "zip": "13239", "country": "Honduras"}, {"city": "Patos", "region": "Para\u00c3\u00adba", "geo": "-13.86136, 130.53807", "zip": "25585", "country": "Madagascar"}, {"city": "C\u00c3\u00a1diz", "region": "Andaluc\u00c3\u00ada", "geo": "-78.03072, -161.36785", "zip": "31172", "country": "Saint Kitts and Nevis"}, {"city": "Municipal District", "region": "Nova Scotia", "geo": "33.43487, -133.74426", "zip": "68599", "country": "Saint Kitts and Nevis"}, {"city": "Port Pirie", "region": "SA", "geo": "-41.63975, -1.12587", "zip": "07736", "country": "Antigua and Barbuda"}, {"city": "Wekweti", "region": "Northwest Territories", "geo": "-11.6927, 105.45409", "zip": "80893", "country": "Saint Barth\u00c3\u00a9lemy"}, {"city": "San Miguel", "region": "San Jos\u00c3\u00a9", "geo": "21.31942, 117.32287", "zip": "70275", "country": "Virgin Islands, British"}, {"city": "Hannover", "region": "NI", "geo": "6.87689, 160.81075", "zip": "21207", "country": "Montenegro"}, {"city": "Lienz", "region": "Ti", "geo": "-8.73619, 85.53132", "zip": "17864", "country": "United States"}, {"city": "Indianapolis", "region": "Indiana", "geo": "38.86705, -158.24413", "zip": "72122", "country": "Qatar"}, {"city": "Chemnitz", "region": "Saxony", "geo": "16.48513, 2.78483", "zip": "49232", "country": "Algeria"}, {"city": "Cwmbran", "region": "Monmouthshire", "geo": "-34.94939, 38.85659", "zip": "14123", "country": "Dominica"}, {"city": "Goi\u00c3\u00a2nia", "region": "GO", "geo": "-22.44473, 46.79801", "zip": "70860", "country": "Togo"}, {"city": "Madison", "region": "WI", "geo": "-53.78518, 68.12022", "zip": "55538", "country": "Iceland"}, {"city": "Hamm", "region": "North Rhine-Westphalia", "geo": "-23.68456, -109.07404", "zip": "96159", "country": "Zimbabwe"}, {"city": "Lehrte", "region": "NI", "geo": "55.01946, -18.33771", "zip": "48070", "country": "Kiribati"}, {"city": "Manukau", "region": "North Island", "geo": "-64.02472, -75.69742", "zip": "64566", "country": "Romania"}, {"city": "Brussel", "region": "BU", "geo": "12.3553, -167.43411", "zip": "21844", "country": "Puerto Rico"}, {"city": "Belfort", "region": "Franche-Comt\u00c3\u00a9", "geo": "-0.28916, -155.96117", "zip": "99585", "country": "Philippines"}, {"city": "Ekeren", "region": "AN", "geo": "-3.77601, -22.38465", "zip": "14342", "country": "Peru"}, {"city": "Olathe", "region": "KS", "geo": "-21.19651, -15.81854", "zip": "67131", "country": "Pitcairn Islands"}, {"city": "Calvera", "region": "Basilicata", "geo": "-21.70783, 14.05402", "zip": "63181", "country": "Barbados"}, {"city": "Fort Simpson", "region": "Northwest Territories", "geo": "-28.96305, 78.71202", "zip": "36969", "country": "Australia"}, {"city": "Quickborn", "region": "SL", "geo": "-77.25499, 32.49871", "zip": "03634", "country": "Northern Mariana Islands"}, {"city": "Coaldale", "region": "AB", "geo": "-87.285, -142.88638", "zip": "26017", "country": "Oman"}, {"city": "Guadalupe", "region": "SJ", "geo": "77.69764, 80.88258", "zip": "68741", "country": "Bouvet Island"}, {"city": "Bathurst", "region": "New South Wales", "geo": "-23.91194, -130.57804", "zip": "05274", "country": "Argentina"}, {"city": "Landeck", "region": "Ti", "geo": "-3.30586, 8.57204", "zip": "57487", "country": "Myanmar"}, {"city": "Nieuwmunster", "region": "West-Vlaanderen", "geo": "72.07397, -74.44562", "zip": "68384", "country": "Chile"}, {"city": "Guarapuava", "region": "Paran\u00c3\u00a1", "geo": "52.75448, 130.25691", "zip": "68098", "country": "Bolivia"}, {"city": "Montb\u00c3\u00a9liard", "region": "Franche-Comt\u00c3\u00a9", "geo": "83.47589, 120.05379", "zip": "81329", "country": "Botswana"}, {"city": "Peterhead", "region": "Aberdeenshire", "geo": "45.81969, 158.3091", "zip": "57205", "country": "French Southern Territories"}, {"city": "Roccasicura", "region": "Molise", "geo": "0.95717, -13.1389", "zip": "20144", "country": "Morocco"}, {"city": "Levallois-Perret", "region": "\u00c3\u008e", "geo": "-65.40518, 5.83362", "zip": "57446", "country": "Bangladesh"}, {"city": "Sint-Lambrechts-Herk", "region": "Limburg", "geo": "-37.64616, 124.33369", "zip": "53675", "country": "Italy"}, {"city": "Concepci\u00c3\u00b3n", "region": "SJ", "geo": "35.93873, -158.80326", "zip": "72418", "country": "Cocos (Keeling) Islands"}, {"city": "Southaven", "region": "Mississippi", "geo": "-20.36619, -70.10182", "zip": "77365", "country": "Gabon"}, {"city": "Nampa", "region": "Idaho", "geo": "32.04032, 32.93517", "zip": "88228", "country": "Antigua and Barbuda"}, {"city": "Owen Sound", "region": "Ontario", "geo": "-14.74598, 76.72724", "zip": "54643", "country": "Maldives"}, {"city": "New Maryland", "region": "New Brunswick", "geo": "-73.49325, 144.75841", "zip": "71801", "country": "Turkey"}, {"city": "Opdorp", "region": "OV", "geo": "-14.92135, -108.75985", "zip": "43555", "country": "South Africa"}, {"city": "Berwick", "region": "NS", "geo": "43.229, 59.17782", "zip": "19838", "country": "Nauru"}, {"city": "Palmerston North", "region": "North Island", "geo": "-4.24038, 102.73013", "zip": "54527", "country": "Bouvet Island"}, {"city": "Bousval", "region": "Waals-Brabant", "geo": "85.70865, 157.26207", "zip": "85512", "country": "Dominican Republic"}, {"city": "Ortacesus", "region": "Sardegna", "geo": "-38.84256, 150.95028", "zip": "62297", "country": "Malta"}, {"city": "Allentown", "region": "Pennsylvania", "geo": "83.90299, 62.81361", "zip": "30214", "country": "United States Minor Outlying Islands"}, {"city": "Bromley", "region": "Kent", "geo": "-10.31576, -132.49883", "zip": "40670", "country": "Central African Republic"}, {"city": "Hollabrunn", "region": "Lower Austria", "geo": "72.87362, -133.09871", "zip": "70987", "country": "Antarctica"}, {"city": "Urbe", "region": "LI", "geo": "-34.52183, -106.23143", "zip": "88070", "country": "Uzbekistan"}, {"city": "Maidenhead", "region": "BR", "geo": "16.41983, 74.03103", "zip": "74851", "country": "Norway"}, {"city": "Pukekohe", "region": "North Island", "geo": "-43.8049, 164.67642", "zip": "68844", "country": "Latvia"}, {"city": "Caprauna", "region": "Piemonte", "geo": "3.27111, -122.37375", "zip": "99648", "country": "Laos"}, {"city": "Chicago", "region": "Illinois", "geo": "38.40439, 103.42864", "zip": "72318", "country": "Bolivia"}, {"city": "Burlington", "region": "VT", "geo": "29.35073, -56.0491", "zip": "32421", "country": "Angola"}, {"city": "Redlands", "region": "Queensland", "geo": "79.62646, 12.88903", "zip": "69869", "country": "Montenegro"}, {"city": "Whangarei", "region": "NI", "geo": "19.8068, 79.99541", "zip": "84079", "country": "Switzerland"}, {"city": "Muiden", "region": "Noord Holland", "geo": "65.31795, 21.62717", "zip": "89217", "country": "Saint Kitts and Nevis"}, {"city": "Irricana", "region": "Alberta", "geo": "-54.77277, 68.57749", "zip": "77640", "country": "British Indian Ocean Territory"}, {"city": "Lang", "region": "Saskatchewan", "geo": "85.99703, -153.13319", "zip": "64316", "country": "Cameroon"}, {"city": "Neubrandenburg", "region": "MV", "geo": "-24.86851, -42.9877", "zip": "00375", "country": "Poland"}, {"city": "Saint Paul", "region": "Minnesota", "geo": "-48.89097, -120.10525", "zip": "76995", "country": "Gabon"}, {"city": "Vit\u00c3\u00b3ria da Conquista", "region": "Bahia", "geo": "-2.33864, 74.68283", "zip": "22831", "country": "Saint Vincent and The Grenadines"}, {"city": "Kerikeri", "region": "NI", "geo": "-12.89792, -27.47584", "zip": "71092", "country": "Palestine, State of"}, {"city": "V\u00c3\u00b6cklabruck", "region": "O\u00c3", "geo": "70.52425, -163.94115", "zip": "17985", "country": "Spain"}, {"city": "Gosnells", "region": "WA", "geo": "-4.03244, -63.37584", "zip": "14926", "country": "Cyprus"}, {"city": "Heerenveen", "region": "Fr", "geo": "70.02672, -68.55496", "zip": "19390", "country": "Burundi"}, {"city": "Feldkirchen in K\u00c3\u00a4rnten", "region": "Kt", "geo": "46.84812, 9.86592", "zip": "99939", "country": "Guadeloupe"}, {"city": "Asso", "region": "Lombardia", "geo": "-68.26399, -100.037", "zip": "68400", "country": "Saint Kitts and Nevis"}, {"city": "Glendale", "region": "Arizona", "geo": "-86.88472, -80.7392", "zip": "15092", "country": "Serbia"}, {"city": "Sete Lagoas", "region": "Minas Gerais", "geo": "16.65072, -26.12347", "zip": "02577", "country": "Bouvet Island"}, {"city": "Swan Hill", "region": "Victoria", "geo": "-22.23618, 70.41153", "zip": "56004", "country": "Poland"}, {"city": "Zutphen", "region": "Gelderland", "geo": "-37.17516, -21.92652", "zip": "39878", "country": "Ghana"}, {"city": "Bochum", "region": "North Rhine-Westphalia", "geo": "32.67258, 104.65677", "zip": "79192", "country": "Nicaragua"}, {"city": "Tarragona", "region": "Catalunya", "geo": "22.76872, 142.99811", "zip": "42054", "country": "Uruguay"}, {"city": "Parauapebas", "region": "Par\u00c3\u00a1", "geo": "-59.03787, 127.1895", "zip": "32064", "country": "Chad"}, {"city": "Velaine-sur-Sambre", "region": "NA", "geo": "34.94734, -159.89189", "zip": "88645", "country": "United States"}, {"city": "Zwickau", "region": "SN", "geo": "-9.01959, 59.45668", "zip": "54527", "country": "Solomon Islands"}, {"city": "Troyes", "region": "Ch", "geo": "-77.54393, 18.3475", "zip": "35689", "country": "Anguilla"}, {"city": "Hilo", "region": "HI", "geo": "9.3886, -145.00113", "zip": "85841", "country": "Niue"}, {"city": "Grand Island", "region": "Nebraska", "geo": "88.67261, -138.08709", "zip": "28866", "country": "Tunisia"}, {"city": "Hawick", "region": "Roxburghshire", "geo": "27.55554, 69.95345", "zip": "11285", "country": "Austria"}, {"city": "Piovene Rocchette", "region": "Veneto", "geo": "-72.50352, -60.07565", "zip": "31544", "country": "Bulgaria"}, {"city": "Burnie", "region": "Tasmania", "geo": "-10.2873, -128.328", "zip": "61646", "country": "Turks and Caicos Islands"}, {"city": "Valpara\u00c3\u00adso de Goi\u00c3\u00a1s", "region": "GO", "geo": "87.86031, -161.56955", "zip": "65351", "country": "Hong Kong"}, {"city": "Burin", "region": "NL", "geo": "-40.80598, 63.10596", "zip": "75206", "country": "Brazil"}, {"city": "Enschede", "region": "Ov", "geo": "54.10238, 64.85157", "zip": "40850", "country": "Western Sahara"}, {"city": "Orilla", "region": "ON", "geo": "4.09491, -150.76503", "zip": "18749", "country": "Korea, North"}, {"city": "Midway", "region": "British Columbia", "geo": "73.56364, -52.13288", "zip": "61529", "country": "Congo, the Democratic Republic of the"}, {"city": "Cambridge", "region": "North Island", "geo": "5.33256, -102.78778", "zip": "52498", "country": "Martinique"}, {"city": "Frederick", "region": "MD", "geo": "43.79036, -174.83763", "zip": "87820", "country": "Haiti"}, {"city": "Marke", "region": "WV", "geo": "85.08291, -68.11614", "zip": "82770", "country": "Italy"}, {"city": "Cape Breton Island", "region": "NS", "geo": "34.55449, -135.06333", "zip": "24634", "country": "Sudan"}, {"city": "Dornoch", "region": "SU", "geo": "-51.92136, 94.85791", "zip": "64203", "country": "Guadeloupe"}, {"city": "Guadalajara", "region": "Castilla - La Mancha", "geo": "-34.3226, 157.30618", "zip": "01549", "country": "Nauru"}, {"city": "Amsterdam", "region": "Noord Holland", "geo": "-13.83261, -150.86392", "zip": "45517", "country": "Finland"}, {"city": "Kalgoorlie-Boulder", "region": "WA", "geo": "-7.0952, 50.54179", "zip": "98876", "country": "United Arab Emirates"}, {"city": "Mundare", "region": "Alberta", "geo": "65.22627, 113.47696", "zip": "34846", "country": "Bahamas"}, {"city": "Saint-L\u00c3\u00b4", "region": "Basse-Normandie", "geo": "-71.27091, 163.72748", "zip": "34423", "country": "Monaco"}, {"city": "St. Clears", "region": "Carmarthenshire", "geo": "62.49242, 165.1423", "zip": "29708", "country": "Greenland"}, {"city": "Neuruppin", "region": "Brandenburg", "geo": "12.03, -104.90468", "zip": "86109", "country": "Christmas Island"}, {"city": "Sint-Genesius-Rode", "region": "VB", "geo": "-12.01716, 14.41377", "zip": "24112", "country": "South Sudan"}, {"city": "Coleville Lake", "region": "Northwest Territories", "geo": "-6.48955, 13.48795", "zip": "65497", "country": "Tonga"}, {"city": "Certaldo", "region": "Toscana", "geo": "36.65537, 138.03354", "zip": "75812", "country": "Chile"}, {"city": "Mercedes", "region": "H", "geo": "-68.44082, 0.98524", "zip": "29851", "country": "Reunion"}, {"city": "Boston", "region": "Massachusetts", "geo": "56.49361, 93.67664", "zip": "60611", "country": "Spain"}, {"city": "Masullas", "region": "SA", "geo": "83.17563, -84.79077", "zip": "48404", "country": "Saudi Arabia"}, {"city": "San Jose", "region": "CA", "geo": "55.56498, 41.35054", "zip": "53116", "country": "Thailand"}, {"city": "Detroit", "region": "Michigan", "geo": "54.64968, 134.89673", "zip": "55858", "country": "Bhutan"}, {"city": "Sint-Joost-ten-Node", "region": "BU", "geo": "-83.37355, -37.14964", "zip": "64341", "country": "Yemen"}, {"city": "Telde", "region": "Canarias", "geo": "86.55405, 82.45161", "zip": "94642", "country": "Zimbabwe"}, {"city": "Lower Hutt", "region": "NI", "geo": "86.51739, 178.29012", "zip": "26066", "country": "Canada"}, {"city": "Gap", "region": "Pr", "geo": "71.52196, -160.56888", "zip": "17879", "country": "Chad"}, {"city": "Porirua", "region": "North Island", "geo": "49.0747, -54.08751", "zip": "07363", "country": "Malta"}, {"city": "Villach", "region": "Kt", "geo": "-19.92246, 43.01896", "zip": "92147", "country": "Benin"}, {"city": "Haguenau", "region": "Alsace", "geo": "-67.10176, -151.29419", "zip": "22227", "country": "Ethiopia"}, {"city": "Pocatello", "region": "ID", "geo": "13.37904, -14.64323", "zip": "26986", "country": "Monaco"}, {"city": "Crici\u00c3\u00bama", "region": "Santa Catarina", "geo": "62.48925, 172.32482", "zip": "59121", "country": "Panama"}, {"city": "Contagem", "region": "MG", "geo": "-1.96321, 12.78912", "zip": "41851", "country": "Bolivia"}, {"city": "Dolgellau", "region": "ME", "geo": "-10.95009, 88.30905", "zip": "69394", "country": "United Arab Emirates"}, {"city": "Castelnuovo Magra", "region": "Liguria", "geo": "-32.99309, 17.27736", "zip": "87404", "country": "Central African Republic"}, {"city": "Canora", "region": "Saskatchewan", "geo": "14.68408, -113.61972", "zip": "49778", "country": "Germany"}, {"city": "Laon", "region": "Pi", "geo": "-37.93957, 98.50616", "zip": "73757", "country": "Cura\u00c3\u00a7ao"}, {"city": "Luton", "region": "BD", "geo": "-32.52592, 29.21649", "zip": "17491", "country": "Togo"}, {"city": "Fayetteville", "region": "AR", "geo": "38.45491, 136.76214", "zip": "75366", "country": "Puerto Rico"}, {"city": "San Giovanni la Punta", "region": "Sicilia", "geo": "32.20355, -61.98246", "zip": "74124", "country": "Holy See (Vatican City State)"}, {"city": "Cessnock", "region": "NS", "geo": "46.3023, 170.22461", "zip": "16936", "country": "Canada"}, {"city": "\u00c3\u0089vreux", "region": "Ha", "geo": "82.15079, -160.35858", "zip": "36111", "country": "South Georgia and The South Sandwich Islands"}, {"city": "Tirrases", "region": "San Jos\u00c3\u00a9", "geo": "85.21842, -158.7412", "zip": "60519", "country": "Pitcairn Islands"}, {"city": "Kelso", "region": "RO", "geo": "-0.10804, -55.68287", "zip": "83047", "country": "Saudi Arabia"}, {"city": "G\u00c3\u00b6tzis", "region": "Vorarlberg", "geo": "86.44555, 161.27934", "zip": "07872", "country": "Georgia"}, {"city": "Tulita", "region": "NT", "geo": "26.93953, 85.87691", "zip": "97813", "country": "Zimbabwe"}, {"city": "\u00c3\u0089pinal", "region": "Lo", "geo": "-35.56472, -100.61333", "zip": "30314", "country": "Thailand"}, {"city": "Schwerin", "region": "Mecklenburg-Vorpommern", "geo": "-36.64114, -15.42267", "zip": "34057", "country": "Virgin Islands, United States"}, {"city": "Ponta Grossa", "region": "PR", "geo": "-83.89102, 97.17816", "zip": "03346", "country": "Ukraine"}, {"city": "Spaniard's Bay", "region": "NL", "geo": "-65.92637, -1.10279", "zip": "30637", "country": "Aruba"}, {"city": "Rankweil", "region": "Vb", "geo": "-57.98757, -90.92723", "zip": "59956", "country": "Bahrain"}, {"city": "Zeitz", "region": "Saxony-Anhalt", "geo": "41.06106, 132.68448", "zip": "20396", "country": "Nicaragua"}, {"city": "Nottingham", "region": "NT", "geo": "-39.05438, -64.92056", "zip": "25015", "country": "Turkmenistan"}, {"city": "Overland Park", "region": "KS", "geo": "5.67621, -92.22295", "zip": "97691", "country": "Austria"}, {"city": "Mazenzele", "region": "VB", "geo": "60.73123, 159.02377", "zip": "85431", "country": "Croatia"}, {"city": "San Diego", "region": "C", "geo": "-46.15604, 11.7837", "zip": "92370", "country": "Heard Island and Mcdonald Islands"}, {"city": "Isola del Gran Sasso d'Italia", "region": "AB", "geo": "9.40986, -3.2097", "zip": "65260", "country": "Yemen"}, {"city": "Heestert", "region": "WV", "geo": "52.72037, 71.05306", "zip": "74309", "country": "Morocco"}, {"city": "Zonhoven", "region": "Limburg", "geo": "7.74314, -44.10439", "zip": "70204", "country": "Namibia"}, {"city": "Westport", "region": "SI", "geo": "14.14603, 127.98307", "zip": "54449", "country": "Bermuda"}, {"city": "Phoenix", "region": "Arizona", "geo": "34.69575, 39.64948", "zip": "78296", "country": "Fiji"}, {"city": "Bremen", "region": "HB", "geo": "-72.70737, 2.11108", "zip": "08533", "country": "Hungary"}, {"city": "Guarulhos", "region": "SP", "geo": "-35.49409, -157.83009", "zip": "90321", "country": "Brunei"}, {"city": "Castellina in Chianti", "region": "TO", "geo": "32.88045, 38.76667", "zip": "14509", "country": "Eritrea"}, {"city": "Bo'ness", "region": "WL", "geo": "18.6829, -88.60475", "zip": "67780", "country": "Bosnia and Herzegovina"}, {"city": "Saint Andr\u00ef\u00bf\u00bd", "region": "NB", "geo": "59.81431, -71.14045", "zip": "78160", "country": "Ecuador"}, {"city": "Joondalup", "region": "WA", "geo": "48.8287, -29.86928", "zip": "18981", "country": "Timor-Leste"}, {"city": "Laramie", "region": "WY", "geo": "-24.26671, -34.32736", "zip": "06539", "country": "Brunei"}, {"city": "Falciano del Massico", "region": "CA", "geo": "-36.97561, 144.13539", "zip": "25310", "country": "Congo (Brazzaville)"}, {"city": "Tavistock", "region": "Devon", "geo": "-55.85452, 75.47878", "zip": "70481", "country": "Saint Lucia"}, {"city": "Gary", "region": "Indiana", "geo": "80.71379, 137.49581", "zip": "10310", "country": "Algeria"}, {"city": "Abergele", "region": "Denbighshire", "geo": "23.14979, 26.23188", "zip": "58967", "country": "Burkina Faso"}, {"city": "Piringen", "region": "Limburg", "geo": "-70.9718, -18.6327", "zip": "24723", "country": "French Guiana"}, {"city": "West Valley City", "region": "Utah", "geo": "-60.18667, 173.35299", "zip": "26971", "country": "Argentina"}, {"city": "Innsbruck", "region": "Tyrol", "geo": "58.99947, -169.00247", "zip": "59457", "country": "Antarctica"}, {"city": "Nossegem", "region": "VB", "geo": "39.49843, 83.13122", "zip": "00066", "country": "New Caledonia"}, {"city": "Scunthorpe", "region": "Lincolnshire", "geo": "42.21064, 143.07099", "zip": "31318", "country": "Faroe Islands"}, {"city": "Bruck an der Mur", "region": "Styria", "geo": "-23.87459, 55.93738", "zip": "65938", "country": "Lithuania"}, {"city": "Carlton", "region": "NB", "geo": "84.30335, -44.41645", "zip": "15575", "country": "Mauritania"}, {"city": "Duque de Caxias", "region": "RJ", "geo": "84.39619, -39.15234", "zip": "74104", "country": "Guyana"}, {"city": "Havr\u00ef\u00bf\u00bd", "region": "Henegouwen", "geo": "-26.4044, 29.80209", "zip": "90783", "country": "Marshall Islands"}, {"city": "Niort", "region": "Po", "geo": "57.84185, -22.89187", "zip": "90567", "country": "Saint Vincent and The Grenadines"}, {"city": "Itabuna", "region": "Bahia", "geo": "19.62859, 80.95332", "zip": "72565", "country": "Andorra"}, {"city": "Masterton", "region": "North Island", "geo": "0.72984, -110.17545", "zip": "52208", "country": "Estonia"}, {"city": "Grumo Appula", "region": "PU", "geo": "44.21864, 103.14543", "zip": "89457", "country": "Sweden"}, {"city": "Dos Hermanas", "region": "Andaluc\u00c3\u00ada", "geo": "84.41611, -117.69771", "zip": "00934", "country": "Tuvalu"}, {"city": "Wichita", "region": "KS", "geo": "-59.49562, -108.73531", "zip": "12188", "country": "American Samoa"}, {"city": "H\u00c3\u00a9rouville-Saint-Clair", "region": "Ba", "geo": "-21.6946, 25.86842", "zip": "23358", "country": "Kyrgyzstan"}, {"city": "Serrungarina", "region": "MA", "geo": "60.2107, -114.79982", "zip": "18358", "country": "Mongolia"}, {"city": "Gulfport", "region": "MS", "geo": "-19.74451, 142.57708", "zip": "01979", "country": "Virgin Islands, United States"}, {"city": "Priolo Gargallo", "region": "SI", "geo": "38.90967, -6.95299", "zip": "14678", "country": "Lesotho"}, {"city": "Crescentino", "region": "PI", "geo": "-35.53428, 90.58827", "zip": "66351", "country": "Australia"}, {"city": "Cuenca", "region": "Castilla - La Mancha", "geo": "69.6906, 11.95256", "zip": "49539", "country": "Australia"}, {"city": "Matamata", "region": "North Island", "geo": "65.75742, -78.59845", "zip": "78298", "country": "French Southern Territories"}, {"city": "Milestone", "region": "SK", "geo": "84.63506, -179.69941", "zip": "02356", "country": "Ireland"}, {"city": "Bolsward", "region": "Fr", "geo": "80.37444, 100.25048", "zip": "01532", "country": "Zimbabwe"}, {"city": "Saarlouis", "region": "Schleswig-Holstein", "geo": "63.83035, 55.54761", "zip": "25558", "country": "Yemen"}, {"city": "Brodick", "region": "BU", "geo": "-14.30732, -130.68321", "zip": "12824", "country": "Taiwan"}, {"city": "Ipatinga", "region": "MG", "geo": "-19.41376, 139.83723", "zip": "79792", "country": "Congo, the Democratic Republic of the"}, {"city": "Teruel", "region": "Arag\u00c3\u00b3n", "geo": "-72.746, 165.21576", "zip": "78031", "country": "Sierra Leone"}, {"city": "Angleur", "region": "Luik", "geo": "49.48076, 99.13359", "zip": "19849", "country": "Trinidad and Tobago"}, {"city": "Llangollen", "region": "Denbighshire", "geo": "65.32197, -3.65308", "zip": "12670", "country": "Italy"}, {"city": "Juneau", "region": "AK", "geo": "-45.53733, 79.83121", "zip": "00327", "country": "Qatar"}, {"city": "Whakatane", "region": "NI", "geo": "-38.994, -66.56648", "zip": "32452", "country": "Greenland"}, {"city": "Friedrichshafen", "region": "BW", "geo": "-44.04913, 102.72881", "zip": "55192", "country": "Saint Kitts and Nevis"}, {"city": "Woodlands County", "region": "AB", "geo": "-78.77314, -18.84968", "zip": "65373", "country": "Netherlands"}, {"city": "Wigtown", "region": "Wigtownshire", "geo": "-8.77217, -127.12317", "zip": "73623", "country": "Malaysia"}, {"city": "Alajuelita", "region": "San Jos\u00c3\u00a9", "geo": "8.18225, -25.9063", "zip": "52746", "country": "France"}, {"city": "Queenstown", "region": "South Island", "geo": "82.76028, -39.48714", "zip": "96526", "country": "Northern Mariana Islands"}, {"city": "Worksop", "region": "Nottinghamshire", "geo": "-38.20423, -163.77663", "zip": "69112", "country": "Grenada"}, {"city": "Rothesay", "region": "NB", "geo": "-14.8287, -131.42004", "zip": "07369", "country": "Puerto Rico"}, {"city": "Chesapeake", "region": "VA", "geo": "-11.23874, -46.97929", "zip": "94945", "country": "British Indian Ocean Territory"}, {"city": "Langenburg", "region": "SK", "geo": "-87.50291, 74.51801", "zip": "54046", "country": "Reunion"}, {"city": "Saarbr\u00ef\u00bf\u00bdcken", "region": "SH", "geo": "19.98879, -106.98636", "zip": "25124", "country": "Togo"}, {"city": "Davenport", "region": "Iowa", "geo": "-85.04408, -66.10257", "zip": "23102", "country": "Heard Island and Mcdonald Islands"}, {"city": "Launceston", "region": "TA", "geo": "-79.90916, 78.43572", "zip": "66918", "country": "Honduras"}, {"city": "Swindon", "region": "Wiltshire", "geo": "44.91499, -160.97877", "zip": "36164", "country": "Hong Kong"}] -------------------------------------------------------------------------------- /tests/test_superelasticsearch.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import json 3 | import logging 4 | import os 5 | import time 6 | 7 | from copy import deepcopy 8 | from datadiff.tools import assert_equal as assertDictEquals 9 | from elasticsearch import Elasticsearch, ElasticsearchException, TransportError 10 | from mock import Mock 11 | from random import randint 12 | 13 | from superelasticsearch import SuperElasticsearch 14 | from superelasticsearch import BulkOperation 15 | from superelasticsearch import _BulkAction 16 | try: 17 | import unittest2 as unittest 18 | except ImportError: 19 | import unittest 20 | 21 | elasticsearch_logger = logging.getLogger('elasticsearch') 22 | elasticsearch_logger.setLevel(logging.ERROR) 23 | 24 | # get file's path in current directory 25 | local_path = lambda x: os.path.join(os.path.dirname(__file__), x) 26 | 27 | 28 | class TestItersearch(unittest.TestCase): 29 | 30 | # create a common Elasticsearch object 31 | es = Elasticsearch(hosts=['localhost:9200']) 32 | 33 | # create a common SuperElasticsearch object 34 | ss = SuperElasticsearch(hosts=['localhost:9200']) 35 | 36 | _index = 'automated_test_index__%s' % randint(0, 1000) 37 | _doc_type = 'automated_test_doc_type__%s' % randint(0, 1000) 38 | 39 | @classmethod 40 | def setUpClass(cls): 41 | cls._total_docs = 0 42 | 43 | # read documents from a file and setup test index 44 | with open(local_path('test_data.dump'), 'r') as f: 45 | data = json.loads(f.read()) 46 | for doc in data: 47 | cls.es.index(index=cls._index, 48 | doc_type=cls._doc_type, 49 | body=doc) 50 | cls._total_docs += 1 51 | 52 | def test_itersearch_raises_typeerror_when_scroll_param_is_missing(self): 53 | self.assertRaises(TypeError, self.ss.itersearch) 54 | 55 | def test_chunked_itersearch_performs_scroll(self): 56 | for size in (10, 100): 57 | scrollCounter = 0 58 | docsCounter = 0 59 | time.sleep(1) 60 | for docs in self.ss.itersearch(index=self._index, 61 | doc_type=self._doc_type, 62 | body=dict( 63 | query=dict(match_all={})), 64 | scroll='10m', size=size): 65 | scrollCounter += 1 66 | docsCounter += len(docs) 67 | 68 | self.assertEquals(scrollCounter, self._total_docs / size + 1) 69 | 70 | def test_non_chunked_itersearch_performs_scroll(self): 71 | for size in (10, 100): 72 | docsCounter = 0 73 | time.sleep(1) 74 | for doc in self.ss.itersearch(index=self._index, 75 | doc_type=self._doc_type, 76 | body=dict(query=dict(match_all={})), 77 | scroll='10m', size=size, 78 | chunked=False): 79 | docsCounter += 1 80 | 81 | self.assertEquals(docsCounter, self._total_docs) 82 | 83 | def test_chunked_itersearch_with_meta_returns_meta(self): 84 | for size in (10, 100): 85 | scrollCounter = 0 86 | docsCounter = 0 87 | time.sleep(1) 88 | for docs, meta in self.ss.itersearch(index=self._index, 89 | doc_type=self._doc_type, 90 | body=dict(query=dict( 91 | match_all={})), 92 | scroll='10m', size=size, 93 | chunked=True, 94 | with_meta=True): 95 | docsCounter += len(docs) 96 | scrollCounter += 1 97 | 98 | self.assertEquals(docsCounter, self._total_docs) 99 | self.assertEquals(scrollCounter, self._total_docs / size + 1) 100 | self.assertTrue(isinstance(meta, dict)) 101 | self.assertEquals(meta['hits']['total'], self._total_docs) 102 | 103 | def test_non_chunked_itersearch_with_meta_returns_meta(self): 104 | for size in (10, 100): 105 | docsCounter = 0 106 | time.sleep(1) 107 | for doc, meta in self.ss.itersearch(index=self._index, 108 | doc_type=self._doc_type, 109 | body=dict(query=dict( 110 | match_all={})), 111 | scroll='10m', size=size, 112 | chunked=False, 113 | with_meta=True): 114 | docsCounter += 1 115 | 116 | self.assertEquals(docsCounter, self._total_docs) 117 | self.assertTrue(isinstance(meta, dict)) 118 | self.assertEquals(meta['hits']['total'], self._total_docs) 119 | 120 | def test_itersearch_raises_assertion_error_when_less_docs_fetched(self): 121 | mocked_value_template = { 122 | "took": 27, 123 | "timed_out": False, 124 | "_scroll_id": 123213, 125 | "_shards": { 126 | "total": 2, 127 | "successful": 2, 128 | "failed": 0 129 | }, 130 | "hits": { 131 | "total": 13, 132 | "max_score": None, 133 | "hits": [ 134 | dict(some_doc="with_some_val") for i in xrange(10) 135 | ] 136 | } 137 | } 138 | 139 | ss = SuperElasticsearch(hosts=['localhost:9200']) 140 | 141 | def assertion(chunked): 142 | # mock the client's scroll method 143 | mocked_search_result = deepcopy(mocked_value_template) 144 | ss.search = Mock(return_value=mocked_search_result) 145 | mocked_scroll_result = deepcopy(mocked_value_template) 146 | mocked_scroll_result['_scroll_id'] = 456456 147 | mocked_scroll_result['hits']['hits'] = [ 148 | dict(some_doc="with_some_val") for i in xrange(2) 149 | ] 150 | ss.scroll = Mock(return_value=mocked_scroll_result) 151 | 152 | search_generator = ss.itersearch(index=self._index, 153 | doc_type=self._doc_type, 154 | body=dict(query=dict( 155 | match_all={})), 156 | scroll='10m', 157 | chunked=chunked) 158 | if chunked: 159 | iterate_times = 2 160 | else: 161 | iterate_times = 12 162 | 163 | for _ in range(0, iterate_times): 164 | search_generator.next() 165 | 166 | mocked_scroll_result = deepcopy(mocked_value_template) 167 | mocked_scroll_result['_scroll_id'] = 789789 168 | mocked_scroll_result['hits']['hits'] = [] 169 | ss.scroll = Mock(return_value=mocked_scroll_result) 170 | search_generator.next() 171 | 172 | self.assertRaises(ElasticsearchException, 173 | functools.partial(assertion, True)) 174 | self.assertRaises(ElasticsearchException, 175 | functools.partial(assertion, False)) 176 | 177 | def test_that_itersearch_clears_scroll_on_successful_scroll(self): 178 | for docs, meta in self.ss.itersearch(index=self._index, 179 | doc_type=self._doc_type, 180 | body=dict( 181 | query=dict(match_all={})), 182 | scroll='10m', size=100, 183 | with_meta=True): 184 | scroll_id = meta['_scroll_id'] 185 | # check if it was the right exception 186 | self.assertRaises(TransportError, self.es.scroll, scroll_id) 187 | try: 188 | self.es.scroll(scroll_id) 189 | except TransportError, err: 190 | self.assertTrue('SearchContextMissingException' in str(err)) 191 | 192 | @classmethod 193 | def tearDownClass(cls): 194 | cls.es.indices.delete(index=cls._index) 195 | 196 | 197 | class TestBulkAction(unittest.TestCase): 198 | 199 | def test_bulk_action_must_not_accept_invalid_action(self): 200 | self.assertRaises(Exception, _BulkAction, type='asd', params={}) 201 | 202 | def test_bulk_action_must_accept_valid_actions(self): 203 | _BulkAction('index', params={}, body=dict(key1='val1')) 204 | _BulkAction('create', params={}, body=dict(key1='val1')) 205 | _BulkAction('update', params={}, body=dict(key1='val1')) 206 | _BulkAction('delete', params={}) 207 | 208 | def test_bulk_action_must_throw_exception_when_missing_expected_body(self): 209 | self.assertRaises(Exception, _BulkAction, 'index', params={}) 210 | _BulkAction('index', params={}, body=dict(key1='val1')) 211 | 212 | def test_bulk_action_must_generate_valid_bulk_op_for_es(self): 213 | body = dict(key1='val1') 214 | 215 | action = _BulkAction('index', params={}, body=body) 216 | self.assertEquals(action.es_op, 217 | (json.dumps({ 'index': {} }) + '\n' + 218 | json.dumps(body))) 219 | 220 | action = _BulkAction('create', params=dict(routing='123', refresh=True), 221 | body=body) 222 | self.assertEquals(action.es_op, 223 | (json.dumps({ 'create': dict(routing='123', 224 | refresh=True) }) + 225 | '\n' + json.dumps(body))) 226 | 227 | # make sure that body is ignored when the operation does not require it 228 | action = _BulkAction('delete', params=dict(routing='123', refresh=True), 229 | body=body) 230 | self.assertEquals(action.es_op, 231 | (json.dumps({ 'delete': dict(routing='123', 232 | refresh=True) }))) 233 | 234 | 235 | class TestBulkOperation(unittest.TestCase): 236 | 237 | # create a common Elasticsearch object 238 | es = Elasticsearch(hosts=['localhost:9200']) 239 | 240 | # create a common SuperElasticsearch object 241 | ss = SuperElasticsearch(hosts=['localhost:9200']) 242 | 243 | _index = 'automated_test_index__%s' % randint(0, 1000) 244 | 245 | def setUp(self): 246 | self._bulk = self.ss.bulk 247 | 248 | def tearDown(self): 249 | # restore bulk method back on SuperElasticsearch object 250 | self.ss.bulk = self._bulk 251 | 252 | def test_bulk_operation_returns_bulk_operation_object(self): 253 | self.assertTrue( 254 | isinstance(self.ss.bulk_operation(), BulkOperation)) 255 | 256 | def test_bulk_operation_must_pass_superlelasticsearch_object(self): 257 | self.assertEquals(self.ss, self.ss.bulk_operation()._client) 258 | 259 | def test_index_or_create_must_push_correct_action(self): 260 | bulk = self.ss.bulk_operation() 261 | body = dict(key1='val1') 262 | 263 | # Without params 264 | bulk._index_or_create('index', body) 265 | action = bulk._actions[-1] 266 | self.assertEquals(action.type, 'index') 267 | assertDictEquals(action.body, body) 268 | assertDictEquals(action.params, {}) 269 | 270 | # With params 271 | bulk._index_or_create('create', doc_type='test_doc_type', body=body, 272 | id=1, consistency='sync', ttl=200) 273 | action = bulk._actions[-1] 274 | self.assertEquals(action.type, 'create') 275 | assertDictEquals(action.body, body) 276 | assertDictEquals(action.params, { 277 | '_type': 'test_doc_type', 278 | '_id': 1, 279 | 'consistency': 'sync', 280 | 'ttl': '200' 281 | }) 282 | 283 | bulk._index_or_create('create', index='test_bulk', 284 | doc_type='test_doc_type', body=body, 285 | routing='abcd', refresh=True) 286 | action = bulk._actions[-1] 287 | self.assertEquals(action.type, 'create') 288 | assertDictEquals(action.body, body) 289 | assertDictEquals(action.params, { 290 | '_index': 'test_bulk', 291 | '_type': 'test_doc_type', 292 | 'routing': 'abcd', 293 | 'refresh': 'true', 294 | }) 295 | 296 | def test_index_calls_index_or_create_method_with_correct_args(self): 297 | bulk = self.ss.bulk_operation() 298 | body = dict(key1='val1') 299 | 300 | bulk._index_or_create = Mock() 301 | 302 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 303 | timeout=200) 304 | self.assertTrue(bulk._index_or_create) 305 | self.assertEquals(bulk._index_or_create.call_args[0][0], 'index') 306 | assertDictEquals(bulk._index_or_create.call_args[0][1], body) 307 | self.assertEquals(bulk._index_or_create.call_args[0][2], None) 308 | self.assertEquals(bulk._index_or_create.call_args[1]['timeout'], 200) 309 | self.assertEquals(bulk._index_or_create.call_args[1]['index'], 310 | 'test_bulk') 311 | self.assertEquals(bulk._index_or_create.call_args[1]['doc_type'], 312 | 'test_bulk_doc_type') 313 | 314 | def test_create_calls_index_or_create_method_with_correct_args(self): 315 | bulk = self.ss.bulk_operation() 316 | body = dict(key1='val1') 317 | 318 | bulk._index_or_create = Mock() 319 | 320 | bulk.create(doc_type='test_bulk_doc_type', body=body, 321 | id=4, timeout=200, routing='abcd') 322 | self.assertTrue(bulk._index_or_create) 323 | self.assertEquals(bulk._index_or_create.call_args[0][0], 'create') 324 | assertDictEquals(bulk._index_or_create.call_args[0][1], body) 325 | self.assertEquals(bulk._index_or_create.call_args[0][2], 4) 326 | self.assertEquals(bulk._index_or_create.call_args[1]['timeout'], 200) 327 | self.assertEquals(bulk._index_or_create.call_args[1]['doc_type'], 328 | 'test_bulk_doc_type') 329 | self.assertEquals(bulk._index_or_create.call_args[1]['routing'], 330 | 'abcd') 331 | 332 | def test_execute_must_empty_actions_after_executing_bulk_operation(self): 333 | bulk = self.ss.bulk_operation() 334 | body = dict(key1='val1') 335 | bulk.create(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 336 | id=4, routing='abcd') 337 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body) 338 | bulk.execute() 339 | self.assertEquals(len(bulk._actions), 0) 340 | 341 | def test_execute_must_return_bulk_response(self): 342 | bulk = self.ss.bulk_operation() 343 | body = dict(key1='val1') 344 | bulk.create(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 345 | id=4, routing='abcd') 346 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body) 347 | resp = bulk.execute() 348 | self.assertTrue(isinstance(resp, dict)) 349 | self.assertTrue(isinstance(resp['items'], list)) 350 | self.assertEquals(len(resp['items']), 2) 351 | 352 | def test_execute_must_call_bulk_with_correct_body_arg(self): 353 | body = dict(key1='val1') 354 | 355 | bulk = self.ss.bulk_operation() 356 | bulk._client.bulk = Mock() 357 | bulk.create(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 358 | id=4, routing='abcd') 359 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body) 360 | 361 | expected_bulk_body = '' 362 | for action in bulk._actions: 363 | expected_bulk_body += action.es_op + '\n' 364 | 365 | resp = bulk.execute() 366 | self.assertTrue(bulk._client.bulk.called) 367 | self.assertTrue(isinstance(bulk._client.bulk.call_args[1]['body'], str)) 368 | self.assertEquals(bulk._client.bulk.call_args[1]['body'], 369 | expected_bulk_body) 370 | 371 | def test_execute_must_use_kwargs_provided_at_the_creation_of_bulk_op(self): 372 | body = dict(key1='val1') 373 | 374 | bulk = self.ss.bulk_operation(index='default_index', 375 | doc_type='some_type', 376 | refresh=True) 377 | bulk._client.bulk = Mock() 378 | bulk.create(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 379 | id=4, routing='abcd') 380 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body) 381 | resp = bulk.execute() 382 | self.assertTrue(bulk._client.bulk.called) 383 | self.assertEquals(bulk._client.bulk.call_args[1]['index'], 384 | 'default_index') 385 | self.assertEquals(bulk._client.bulk.call_args[1]['doc_type'], 386 | 'some_type') 387 | self.assertEquals(bulk._client.bulk.call_args[1]['refresh'], 388 | 'true') 389 | 390 | def test_execute_must_override_kwargs_provided_at_bulk_op_creation(self): 391 | body = dict(key1='val1') 392 | 393 | bulk = self.ss.bulk_operation(index='default_index', 394 | doc_type='some_type', 395 | refresh=True) 396 | bulk._client.bulk = Mock() 397 | bulk.create(index='test_bulk', doc_type='test_bulk_doc_type', body=body, 398 | id=4, routing='abcd') 399 | bulk.index(index='test_bulk', doc_type='test_bulk_doc_type', body=body) 400 | resp = bulk.execute(index='some_other_index', refresh=False) 401 | self.assertTrue(bulk._client.bulk.called) 402 | self.assertEquals(bulk._client.bulk.call_args[1]['index'], 403 | 'some_other_index') 404 | self.assertEquals(bulk._client.bulk.call_args[1]['doc_type'], 405 | 'some_type') 406 | self.assertEquals(bulk._client.bulk.call_args[1]['refresh'], 407 | 'false') 408 | 409 | def test_update_must_push_correct_action(self): 410 | bulk = self.ss.bulk_operation() 411 | body = dict(key1='val1') 412 | 413 | # Without params 414 | bulk.update(id=123, body=body) 415 | action = bulk._actions[-1] 416 | self.assertEquals(action.type, 'update') 417 | assertDictEquals(action.body, body) 418 | assertDictEquals(action.params, dict(_id=123)) 419 | 420 | # With params 421 | bulk.update(index='test_index', doc_type='test_doc_type', body=body, 422 | id=123, consistency='sync', ttl=200) 423 | action = bulk._actions[-1] 424 | self.assertEquals(action.type, 'update') 425 | assertDictEquals(action.body, body) 426 | assertDictEquals(action.params, { 427 | '_index': 'test_index', 428 | '_type': 'test_doc_type', 429 | '_id': 123, 430 | 'consistency': 'sync', 431 | 'ttl': '200' 432 | }) 433 | 434 | def test_delete_must_push_correct_action(self): 435 | bulk = self.ss.bulk_operation() 436 | body = dict(key1='val1') 437 | 438 | # Without params 439 | bulk.delete(id=123) 440 | action = bulk._actions[-1] 441 | self.assertEquals(action.type, 'delete') 442 | assertDictEquals(action.body, None) 443 | assertDictEquals(action.params, dict(_id=123)) 444 | 445 | # With params 446 | bulk.delete(index='test_index', doc_type='test_doc_type', 447 | id=123, consistency='sync', parent=1) 448 | action = bulk._actions[-1] 449 | self.assertEquals(action.type, 'delete') 450 | assertDictEquals(action.body, None) 451 | assertDictEquals(action.params, { 452 | '_index': 'test_index', 453 | '_type': 'test_doc_type', 454 | '_id': 123, 455 | 'consistency': 'sync', 456 | 'parent': '1', 457 | }) 458 | 459 | # Make sure delete does not push body even if passed 460 | bulk.delete(id=123, body=body) 461 | action = bulk._actions[-1] 462 | self.assertEquals(action.type, 'delete') 463 | assertDictEquals(action.body, None) 464 | assertDictEquals(action.params, dict(_id=123)) 465 | -------------------------------------------------------------------------------- /vagrant/setup.sh: -------------------------------------------------------------------------------- 1 | # Aptitude update 2 | apt-get update 3 | 4 | # Install Python tools 5 | apt-get install -y python-pip 6 | 7 | # Install python packages 8 | pip install virtualenv virtualenvwrapper ipdb ipython 9 | 10 | # Install OpenJDK 11 | apt-get install -y openjdk-7-jdk 12 | 13 | # Install Elasticsearch 14 | if [[ ! -d /opt/elasticsearch ]]; then 15 | pushd /opt 16 | wget "https://download.elasticsearch.org/elasticsearch/elasticsearch/elasticsearch-1.4.1.tar.gz" 17 | tar zxvf elasticsearch-1.4.1.tar.gz 18 | mv elasticsearch-1.4.1 elasticsearch 19 | popd 20 | fi 21 | --------------------------------------------------------------------------------