{ "info": { "author": "Robin Tissot", "author_email": "aj3sshh@gmail.com", "bugtrack_url": null, "classifiers": [ "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python :: 3" ], "description": "django_elasticsearch is a wrapper around py-elasticsearch that automates the indexation and search of django models. \n**Note**: if your elasticsearch documents/mappings are not close to django models, this package is probably not for you.\n\nINSTALL\n=======\n\n* Install django_elasticsearch\n ```shell\n pip install djangoelasticsearch\n ```\n\n\nELASTICSEARCH VERSION COMPATIBILITY\n===================================\n\nAs stated in the python elasticsearch module documentation:\n\n\n>There are two branches for development - master and 0.4. Master branch is used to track all the changes for Elasticsearch 1.0 and beyond whereas 0.4 tracks Elasticsearch 0.90.\n>\n>Releases with major version 1 (1.X.Y) are to be used with Elasticsearch 1.* and later, 0.4 releases are meant to work with Elasticsearch 0.90.*.\n\ndjango_elasticsearch has only been tested with Elasticsearch 1.3.9 and it's corresponding python interface version 1.2.0, but since [the API hasn't change](https://elasticsearch-py.readthedocs.org/en/master/Changelog.html) i'm quite positive that newer and older versions should work fine too, as long as you use the right python module for your Elasticsearch version. [See the official docs on the matter](https://elasticsearch-py.readthedocs.org/en/master/#compatibility).\n\nUSAGE\n=====\n\nSubclass the models you wish to index/search with ```django_elasticsearch.models.EsIndexable```.\n```python\nfrom django.db import models\nfrom django_elasticsearch.models import EsIndexable\n\n\nMyModel(EsIndexable, models.Model):\n foo = models.CharField(max_length=64)\n [...]\n\n```\n\nThen you can do:\n```python\n>>> q = MyModel.es.search('value')\n>>> q\n[{'id': 1, 'foo': 'A value'}, {'id': 2, 'foo': 'Another value'}, ...]\n>>> q.deserialize()\n[, , ...]\n>>> MyModel.es.get(id=1)\n{'id': 1, 'foo': 'A value'}\n```\nThe elasticsearch manager methods (all, search, mlt) returns an instance of a EsQueryset, it's like a django Queryset but it queries elasticsearch instead of your db. \nLike a regular Queryset, an EsQueryset is lazy, and if evaluated, returns a list of documents. The ```.deserialize()``` method makes the queryset return instances of models instead of dicts.\n\n> django-elasticsearch **DOES NOT** index documents by itself unless told to, either set settings.ELASTICSEARCH_AUTO_INDEX to True to index your models when you save them, or call directly myinstance.es.do_index().\n\nTo specify the size of output of documents, it is necessary to make a slice of data, for example:\n\n```\nlen(list(MyModel.es.search('value')))\n>>> 10\nlen(list(MyModel.es.search('value')[0:100]))\n>>> 42\n```\n\nCONFIGURATION\n=============\nProject scope configuration (django settings):\n----------------------------------------------\n\n* **ELASTICSEARCH_URL** \n Defaults to 'http://localhost:9200' \n The url of your elasticsearch cluster/instance.\n\n* **ELASTICSEARCH_AUTO_INDEX** \n Defaults to False \n Set to True if you **don't** want to handle the elasticsearch operations yourself. In that case the creation of the index, the indexation and deletions are hooked respectively to the post_syncdb, post_save and post_delete signals. Should probably only be used in a dev environment or for small scale databases.\n If you have already done a syncdb, you can just call ```MyModel.es.create_index()``` to create the index/mapping.\n\n* **ELASTICSEARCH_DEFAULT_INDEX** \n Defaults to 'django' \n The default index name used for every document, can be overrided for a model with the ```model.Meta.Elasticsearch.index``` attribute.\n\n* **ELASTICSEARCH_SETTINGS** \n No defaults \n If set, will be passed when creating any index [as is](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-create-index.html#create-index-settings).\n\n* **ELASTICSEARCH_FUZZINESS** \n Defaults to 0.5 \n Will be applied to any es.search query, See the [fuzziness section](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/common-options.html#fuzziness) of the elasticsearch documentation.\n\n* **ELASTICSEARCH_CONNECTION_KWARGS** \n Defaults to {} \n Additional kwargs to be passed to at the instantiation of the elasticsearch client. Useful to manage HTTPS connection for example ([Reference](http://elasticsearch-py.readthedocs.org/en/master/api.html#elasticsearch.Elasticsearch)).\n\nModel scope configuration:\n--------------------------\n\nEach EsIndexable model receive an Elasticsearch class that contains its options (just like the Model.Meta class).\n\n* **index** \n Defaults to 'django' \n The elasticsearch index in which this model(document type) will be indexed.\n\n* **doc_type** \n Defaults to 'model-{model_name}' \n The elasticsearch type in which this model will be indexed.\n\n* **fields** \n Defaults to None \n The fields to be indexed by elasticsearch, if left to None, all models fields will be indexed.\n\n* **mappings** \n Defaults to None \n You can override some or all of the fields mapping with this dictionary\n Example: \n\n ```python\n\n MyModel(EsIndexable, models.Model):\n title = models.CharField(max_length=64)\n\n class Elasticsearch(EsIndexable.Elasticsearch):\n mappings = {'title': {'boost': 2.0}}\n ```\n In this example we only override the 'boost' attribute of the 'title' field, but there are plenty of possible configurations, see [the docs](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-put-mapping.html).\n\n* **serializer_class** \n Defaults to EsJsonSerializer \n This is the class used to translate from the django model to elasticsearch document both ways.\n\n* **facets_fields** \n Defaults to None \n Can be set to a list of fields to return as facets when doing a search query on the model, if not set explicitly in the query itself.\n\n* **facets_limits** \n Defaults to None \n The maximum number of facets to return per query, if None, use the elasticsearch setting.\n\n* **suggest_fields** \n Defaults to None \n A dictionary of fields to add in the suggestions, if not set at a search level.\n\n* **suggest_limit** \n Defaults to None \n The maximum number of suggestions to return, if None, use the elasticsearch setting.\n\n* **completion_fields** \n Defaults to None \n The fields on which to activate auto-completion (needs a specific mapping).\n\nAPI\n===\n\nEsIndexable API:\n----------------\n\nThe Elasticsearch manager is available from the 'es' attribute of EsIndexable Model classes or instances. Some methods requires an instance though. \n\n**Manager methods that returns a EsQueryset instance** \n\n* **es.search**(query,\n facets=None,\n facets_limit=None,\n global_facets=True,\n suggest_fields=None,\n suggest_limit=None,\n fuzziness=None) \n Returns a configured EsQueryset with the given options, or the defaults set in ```EsIndexable.Elasticsearch```. \n\n* **es.all**() \n Proxy to an empty query ```.search(\"\")```.\n\n* **es.mlt** *needs_instance* \n Returns an EsQueryset of documents that are 'like' the given instance's document. See the [more like this api](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-more-like-this.html).\n\n**Other Manager methods** \n* **es.count**() \n Returns the number of documents in the model's doc_type.\n\n* **es.get**() *needs_instance* \n Returns the elasticsearch document of the model instance.\n\n* **es.delete**() *needs_instance* \n Delete the given instance's document.\n\n* **es.do_index**() *needs_instance* \n Serialize and index the given instance.\n\n* **es.complete**(field_name, query) \n Returns a list of suggestions from elasticsearch for the given field and query.\n **Note**: field_name must be present in ```Elasticsearch.completion_fields``` because it needs a specific mapping. \n Example:\n ```\n >>>MyModel.es.complete('title', 'tset')\n ['test',]\n ```\n\n* **es.do_update**() \n Refresh the whole index of the model. This should probably be only used in a TestCase. See the [refresh api](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/indices-refresh.html).\n\n* **es.get_mapping**() \n Returns the current mapping for the model's document type.\n\n* **es.get_settings**() \n Returns the current settings for the model's index.\n\n* **es.diff**() \n Returns a dict containing differences between the db instance and the elasticsearch instance.\n\n* **es.check_cluster**() \n Returns True if the elasticsearch cluster is alive.\n\n* **es.reindex_all**(queryset=None) \n queryset defaults to ```self.model.objects.all()``` \n Calls ```es.do_index()``` for every instance in queryset.\n\n* **es.flush**() \n Deletes the model's index and then reindex all instances of it.\n\n\nEsQueryset API:\n---------------\nThis class is as close as possible to a standard relational db Queryset, however the db operations (update and delete) are deactivated (i'm open for discussion on if and how to implement these). Note that just like regular Querysets, EsQuerysets are lazy, they can be ordered, filtered and faceted. \n\nNote that the return value of the queryset is higly dependent on your mapping, for example, if you want to be able to do an exact filtering with filter() you need a field with {\"index\" : \"not_analyzed\"}.\nAlso by default, filters are case insensitive, if you have a case sensitive tokenizer, you need to instantiate EsQueryset with ignore_case=False.\n\nAn EsQueryset acts a lot like a regular Queryset:\n```\n>>> q = MyModel.es.queryset.all()\n>>> q = q.filter(title='foo')\n>>> q = q.search('test')\n>>> q # only now is the query evaluated\n[{'title': 'foo', 'some_content': 'this is a test.'}]\n```\n\nIf you need models methods or attributes, you can get model instances instead of documents (dicts) by calling the deserialize method on the query before evaluating it. See the Serializer API below.\n\nTo access the facets you can use the facets property of the EsQueryset:\n```python\n>>> MyModel.Elasticsearch.default_facets_fields\n['author']\n>>> q = MyModel.es.search('woot', facets=['foo']) # returns a lazy EsQueryset instance\n>>> q = MyModel.es.search('woot').facet(['foo']) # is exactly the same\n>>> q.facets # evals the query and returns the facets\n{u'doc_count': 45,\n u'foo': {u'buckets': [\n {u'doc_count': 13, u'key': u'bar'},\n]}}\n```\nNote that es.search automatically add the default facets set on the model to the query, but you can also set them manually with the ```facets``` and ```facets_limit``` parameters.\n\n**Available methods** all of those are chainable.\n* **es.queryset.search**(query) \n\n* **es.queryset.all**() \n\n* **es.queryset.facet**(fields, limit=None, use_globals=True) \n If ```use_globals``` is set to False, the facets will be filtered like the documents.\n\n* **es.queryset.suggest**(fields, limit) \n Add ```fields``` for suggestions.\n\n* **es.queryset.order_by**(**kwargs) \n\n* **es.queryset.filter**(**kwargs) \n Accepted lookups are: __exact, __should, __contains, __gt, __gte, __lt, __lte, __range \n Just like in django, the default lookup is __exact. \n See the [bool query](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html) for a difference between __exact (which maps to 'must') and __should. \n\n* **es.queryset.exclude**(**kwargs) \n\n* **es.queryset.mlt**(id) \n See the [more like this api](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-more-like-this.html).\n\n* **es.queryset.deserialize**()\n Makes the queryset return model instances instead of documents.\n\n* **es.queryset.extra**(body)\n Blindly updates the elasticsearch query body with ```body``` allowing to use any non-implemented elasticsearch feature.\n\n**Does not return an EsQueryset** and thus are not chainable. \n* **es.queryset.count**()\n\n* **es.queryset.get**(pk=X)\n\n* **es.queryset.complete**(field_name, query)\n\n\nSerializer API:\n---------------\n\nThe serializer's role is to format django model instances to something indexable by elasticsearch : json. The only mandatory method for a serializer is the ```serialize(instance)``` method, deserializing is only an option. \n\nThe default serializer does a little bit more though: \nFor each indexed fields, look for either ```serialize_{field_name}``` or ```serialize_{field_type}``` methods, and fallback on ```getattr(instance, field_name)```. Also allow naive nested serialization, by looking for an Elasticsearch class attribute on the target model class of the related field, or falling back on ```dict(id=instance.id, value=unicode(instance))```. \nLet's look at a bit complex example: \n\nmy_app.models.py \n```python\nfrom django.db import models\nfrom django_elasticsearch.models import EsIndexable\nfrom my_app.serializers import MyModelEsSerializer\n\nclass MyModel(models.Model):\n some_content = models.CharField(max_length=255)\n more_content = models.TextField()\n a_date = models.DateTimeField(auto_now_add=True)\n another_date = models.DateTimeField(auto_now=True)\n some_relation = models.ForeignKey(AnotherModel)\n\n class Elasticsearch(EsIndexable.Elasticsearch):\n serializer_class = MyModelEsSerializer\n fields = ['some_content', 'more_content', 'content_length', 'a_date', 'some_relation']\n mappings = {'content_length': {'type': 'long'},\n 'a_date': {'type': 'object'}}\n\n```\n\nNote that since ```content_length``` is an abstract field (not present in db), and ```a_date``` is serialized to a dict instead of it's default (datetime), we need to tell elasticsearch their types in the mappings attribute. \n\nserializers.py \n```python\nfrom django_elasticsearch.serializers import EsJsonSerializer\n\nclass MyModelEsSerializer(EsJsonSerializer):\n def serialize_more_content(self, instance, field_name):\n # Specific attribute serializer\n return getattr(instance, field_name)[:5]\n\n def serialize_content_length(self, instance, field_name):\n # Abstract field serializer\n content = getattr(instance, 'some_content')\n return len(content)\n\n def serialize_type_datetimefield(self, instance, field_name):\n # Specific field type serializer\n d = getattr(instance, field_name)\n # A rather typical api output,\n # The reasons for indexing dates as this are debatable, but it's just an example\n return {\n 'timestamp': d and d.strftime('%s'),\n 'date': d and d.date().isoformat(),\n 'time': d and d.time().isoformat()[:5]\n }\n```\n\noutput\n```python\n>>> instance = MyModel(some_content=u\"This is some minimalist content.\",\n more_content=u\"And that too.\")\n\n>>> instance.es.serialize()\n\"{'some_content': 'This is some minimalist content.',\n 'more_content': 'And t',\n 'content_length': 32,\n 'a_date': {\n 'timestamp': '1434452101',\n 'date': '2015-06-16',\n 'time': '05:53:56.626532'\n },\n 'some_relation': {'id': 15, 'value': 'something something'}\n}\"\n```\n\nCONTRIB\n=======\n\n* **restframework.ElasticsearchFilterBackend** \n A filter backend for [rest framework](http://www.django-rest-framework.org/) that returns a EsQueryset. \n\n* **restframework.FacetedListModelMixin** \n A viewset mixin that adds the facets to the response data in case the ElasticsearchFilterBackend was used. \n\nLOGGING\n=======\n\nTwo loggers are available 'elasticsearch' and 'elasticsearch.trace'.\n\n\nFAILING GRACEFULLY\n==================\n\nYou can catch ```elasticsearch.ConnectionError``` and ```elasticsearch.TransportError``` if you want to recover from an error on elasticsearch side. There is an example of it in ```django_elasticsearch.views.ElasticsearchListView```.\nYou can also use the ```MyModel.es.check_cluster()``` method which returns True if the cluster is available, in case you want to make sure of it before doing anything.\n\n\nTESTS\n=====\n\nDjango-elasticsearch has a 95% test coverage, and tests pass for django 1.4 to 1.9.\n\nUsing tox\n---------\n\nInstall [tox](https://testrun.org/tox/), then:\n```shell\ncd test_project\ntox\n```\n\nOr to test one specific python/django version combo:\n```\ntox -e py27-django16\n```\n\nOr a specific test case / unit test, with a weird syntax:\nDjango 1.4\n```\ntox -epy27-django14 -- .EsQuerysetTestCase\ntox -epy27-django14 -- .EsQuerysetTestCase.test_use_cache\n```\n\nDjango >1.6\n```\ntox -epy27-django16 -- .tests.test_qs.EsQuerysetTestCase\ntox -epy27-django16 -- .tests.test_qs.EsQuerysetTestCase.test_use_cache\n```\n\n\nThe old way\n-----------\n\n```\n$ cd test_project\n$ virtualenv env\n$ . env/bin/activate\n$ pip install -r ../requirements.txt # app requirements\n$ pip install -r requirements.txt # tests requirements\n$ python manage.py test django_elasticsearch\n```\n\nCoverage\n--------\n\n```\ncoverage run --source=django_elasticsearch --omit='*tests*','*migrations*' manage.py test django_elasticsearch\n```\n\nNOTES\n=====\n\nWhy not make a django database backend ? Because django *does not* support non relational databases, which means that the db backend API is very heavily designed around SQL. I'm usually in favor of hiding the complexity, but in this case for every bit that feels right - auto db and test db creation, client handling, .. - there is one that feels wrong and keeping up with the api changes makes it worse. There is an avorted prototype branch (feature/db-backend) going this way though.\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/liberation/django_elasticsearch", "keywords": "", "license": "", "maintainer": "", "maintainer_email": "", "name": "djangoelasticsearch", "package_url": "https://pypi.org/project/djangoelasticsearch/", "platform": "", "project_url": "https://pypi.org/project/djangoelasticsearch/", "project_urls": { "Homepage": "https://github.com/liberation/django_elasticsearch" }, "release_url": "https://pypi.org/project/djangoelasticsearch/0.5.1/", "requires_dist": [ "elasticsearch (==2.3.0)", "elasticsearch-dsl (==5.4.0)" ], "requires_python": "", "summary": "Simple wrapper around py-elasticsearch to index/search a django Model.", "version": "0.5.1" }, "last_serial": 5094624, "releases": { "0.5": [ { "comment_text": "", "digests": { "md5": "0b49b66c1b29f69a89481b58147903b6", "sha256": "092be68790085ec94be2ecd530350cb95fb775d3ac78041a52a10c353130b54e" }, "downloads": -1, "filename": "djangoelasticsearch-0.5-py3-none-any.whl", "has_sig": false, "md5_digest": "0b49b66c1b29f69a89481b58147903b6", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 36082, "upload_time": "2019-04-04T04:55:26", "url": "https://files.pythonhosted.org/packages/5a/65/00122626b3e7d81c547267ef4b99770734647fd921fee4922400caed4dd3/djangoelasticsearch-0.5-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "b52145293d43ed69a076cbdc1600d149", "sha256": "9f4b709bc87dafee32b38886f2e5610cb343b0d8c1ad48be999ae97cc955b453" }, "downloads": -1, "filename": "djangoelasticsearch-0.5.tar.gz", "has_sig": false, "md5_digest": "b52145293d43ed69a076cbdc1600d149", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 26060, "upload_time": "2019-04-04T04:55:29", "url": "https://files.pythonhosted.org/packages/cc/da/41655bdc7319c5245a8b8961775aedcbca259c1e2ebde97291d6eab77bf3/djangoelasticsearch-0.5.tar.gz" } ], "0.5.1": [ { "comment_text": "", "digests": { "md5": "913d86e045946d06e2123d0e31c03e65", "sha256": "2b76e0ac1f322da0f1770c01828d25b80abfa605ef34a4e7fb54bf93e5fe549d" }, "downloads": -1, "filename": "djangoelasticsearch-0.5.1-py3-none-any.whl", "has_sig": false, "md5_digest": "913d86e045946d06e2123d0e31c03e65", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 36026, "upload_time": "2019-04-04T05:00:02", "url": "https://files.pythonhosted.org/packages/65/57/bd004e8ae19280da2b5fd677468026e6daadb75f8c8a2fe7fcc33cb1f160/djangoelasticsearch-0.5.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0e6cd137b58ebf56ac126352d396b03f", "sha256": "fe99937b87fa0748cf752ca5f4673caa95fe81b0724532456e032991a0eb4888" }, "downloads": -1, "filename": "djangoelasticsearch-0.5.1.tar.gz", "has_sig": false, "md5_digest": "0e6cd137b58ebf56ac126352d396b03f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25986, "upload_time": "2019-04-04T05:00:05", "url": "https://files.pythonhosted.org/packages/a5/06/ff7e304e3554c1e184bf90faa54483708525553297979121d9589aa732b7/djangoelasticsearch-0.5.1.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "913d86e045946d06e2123d0e31c03e65", "sha256": "2b76e0ac1f322da0f1770c01828d25b80abfa605ef34a4e7fb54bf93e5fe549d" }, "downloads": -1, "filename": "djangoelasticsearch-0.5.1-py3-none-any.whl", "has_sig": false, "md5_digest": "913d86e045946d06e2123d0e31c03e65", "packagetype": "bdist_wheel", "python_version": "py3", "requires_python": null, "size": 36026, "upload_time": "2019-04-04T05:00:02", "url": "https://files.pythonhosted.org/packages/65/57/bd004e8ae19280da2b5fd677468026e6daadb75f8c8a2fe7fcc33cb1f160/djangoelasticsearch-0.5.1-py3-none-any.whl" }, { "comment_text": "", "digests": { "md5": "0e6cd137b58ebf56ac126352d396b03f", "sha256": "fe99937b87fa0748cf752ca5f4673caa95fe81b0724532456e032991a0eb4888" }, "downloads": -1, "filename": "djangoelasticsearch-0.5.1.tar.gz", "has_sig": false, "md5_digest": "0e6cd137b58ebf56ac126352d396b03f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 25986, "upload_time": "2019-04-04T05:00:05", "url": "https://files.pythonhosted.org/packages/a5/06/ff7e304e3554c1e184bf90faa54483708525553297979121d9589aa732b7/djangoelasticsearch-0.5.1.tar.gz" } ] }