{"id":163,"date":"2015-05-26T15:15:17","date_gmt":"2015-05-26T07:15:17","guid":{"rendered":"http:\/\/vinta.ws\/code\/?p=163"},"modified":"2026-02-18T01:20:36","modified_gmt":"2026-02-17T17:20:36","slug":"using-elasticsearch-dsl-with-python","status":"publish","type":"post","link":"https:\/\/vinta.ws\/code\/using-elasticsearch-dsl-with-python.html","title":{"rendered":"elasticsearch-dsl-py: The Official Elasticsearch ORM in Python"},"content":{"rendered":"<p>Query DSL \u662f Elasticsearch \u7684\u67e5\u8a62\u7528 Domain-specific Language (DSL)\uff0c\u5be6\u969b\u4e0a\u5c31\u662f\u4e00\u5806 JSON\u3002<code>elasticsearch-dsl<\/code> \u662f\u5b98\u65b9\u767c\u4f48\u7684\u4e00\u5957\u7528\u4f86\u64cd\u4f5c Query DSL \u7684 Python package\uff0c\u53ef\u4ee5\u7576\u6210\u662f Elasticsearch \u7684 ORM\u3002<\/p>\n<p>\u5e0c\u671b\u4e4b\u5f8c\u53ef\u4ee5\u76f4\u63a5\u652f\u63f4\u7528 SQL \u4f86\u67e5\u8a62\uff0c\u4e0d\u7136 Query DSL \u771f\u7684\u6709\u5920\u96e3\u5beb\u3002<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/github.com\/elastic\/elasticsearch-dsl-py\">https:\/\/github.com\/elastic\/elasticsearch-dsl-py<\/a><\/p>\n<h2>Installation<\/h2>\n<pre class=\"line-numbers\"><code class=\"language-bash\">$ pip install elasticsearch-dsl&gt;=5.0.0,&lt;6.0.0<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/index.html\">https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/index.html<\/a><\/p>\n<h2>Schema<\/h2>\n<p>in app\/mappings.py<\/p>\n<pre class=\"line-numbers\"><code class=\"language-py\">from elasticsearch_dsl import DocType, String, Boolean\nfrom elasticsearch_dsl.connections import connections\nconnections.create_connection(hosts=['127.0.0.1', ])\n\nclass AlbumDoc(DocType):\n    upc = String(index='not_analyzed')\n    title = String(analyzer='ik', fields={'raw': String(index='not_analyzed')})\n    artist = String(analyzer='ik')\n    is_ready = Boolean()\n\n    class Meta:\n        index = 'dps'\n        doc_type = 'album'\n\n    @classmethod\n    def sync(cls, album):\n        album_doc = AlbumDoc(meta={'id': album.id})\n        album_doc.upc = album.get_upcs(output_str=False)\n        album_doc.title = album.name\n        album_doc.artist = album.artist.name\n        album_doc.is_ready = album.is_ready\n        album_doc.save()\n\n    def save(self, *args, **kwargs):\n        return super(AlbumDoc, self).save(*args, **kwargs)\n\n    def get_model_obj(self):\n        from svapps.dps.models import Album\n        return Album.objects.get(id=self.meta.id)\n\n# to create mappings\nAlbumDoc.init()<\/code><\/pre>\n<p>\u4e00\u5b9a\u8981\u57f7\u884c\u4e00\u6b21 <code>YourDocType.init()<\/code>\uff0c\u9019\u6a23 Elasticsearch \u624d\u6703\u6839\u64da\u4f60\u7684 DocType \u7522\u751f\u5c0d\u61c9\u7684 mapping\u3002\u5426\u5247 Elasticsearch \u5c31\u6703\u5728\u4f60\u7b2c\u4e00\u6b21\u5012\u8cc7\u6599\u9032\u53bb\u7684\u6642\u5019\u6839\u64da\u4f60\u7684\u8cc7\u6599\u7684 data type \u5efa\u7acb\u5c0d\u61c9\u7684 mapping\uff0c\u6240\u4ee5 analyzer \u4e4b\u985e\u7684\u8a2d\u5b9a\u5c31\u6703\u662f\u9810\u8a2d\u7684 <code>standard<\/code>\uff0c\u4f60\u53ef\u4ee5\u900f\u904e <code>_mapping<\/code> API \u4f86\u6aa2\u67e5\u3002<\/p>\n<ul>\n<li><a href=\"http:\/\/127.0.0.1:9200\/dps\/_mapping\/track\">http:\/\/127.0.0.1:9200\/dps\/_mapping\/track<\/a><\/li>\n<li><a href=\"http:\/\/127.0.0.1:9200\/dps\/_mapping\/album\">http:\/\/127.0.0.1:9200\/dps\/_mapping\/album<\/a><\/li>\n<\/ul>\n<p>\u9700\u8981\u5168\u6587\u641c\u5c0b\u7684\u6b04\u4f4d\u8981\u8a2d\u70ba <code>analyzed<\/code>\uff08string \u6b04\u4f4d\u9ed8\u8a8d\u90fd\u662f analyzed\uff09\uff0c\u4e0d\u9700\u8981\u5168\u6587\u641c\u5c0b\u7684\u6b04\u4f4d\uff0c\u4e5f\u5c31\u662f\u8981\u6c42\u7cbe\u78ba\u7684\u6b04\u4f4d\uff0c\u4f8b\u5982\uff1a<code>username<\/code>\u3001<code>email<\/code>\u3001<code>zip code<\/code>\uff0c\u5c31\u53ef\u4ee5\u8a2d\u6210 <code>not_analyzed<\/code>\uff0c\u4f46\u662f\u4f60\u5c31\u4e0d\u80fd\u5c0d analyzed \u7684\u6b04\u4f4d\u4f7f\u7528 <code>term<\/code> \u4e86\uff0c\u9664\u975e\u4f60\u5c0d\u8a72\u6b04\u4f4d\u984d\u5916\u518d\u5efa\u7acb\u4e00\u500b <code>raw<\/code> \u6b04\u4f4d\u3002<\/p>\n<p>ref:<br \/>\n<a href=\"https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/persistence.html\">https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/persistence.html<\/a><br \/>\n<a href=\"https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-term-query.html#CO59-2\">https:\/\/www.elastic.co\/guide\/en\/elasticsearch\/reference\/current\/query-dsl-term-query.html#CO59-2<\/a><\/p>\n<h2>Store Data<\/h2>\n<pre class=\"line-numbers\"><code class=\"language-py\">album_doc = AlbumDoc(meta={'id': 42})\nalbum_doc.upc = ['887375000619', '887375502069']\nalbum_doc.title = 'abc'\nalbum_doc.artist = 'xyz'\nalbum_doc.is_ready = True\nalbum_doc.save()\n\n# \u53ef\u4ee5\u5982\u5e38\u5730 query\uff0c\u4e0d\u7528\u7ba1\u5b83\u662f\u4e0d\u662f list\nsearch = AlbumDoc.search().filter('term', upc='887375000619')\nresponse = search.execute()<\/code><\/pre>\n<p>\u56e0\u70ba Elasticsearch \u662f schemaless\uff0c\u6240\u4ee5\u5373\u4f7f\u4f60\u5b9a\u7fa9\u4e86 String \u6b04\u4f4d\uff0c\u9084\u662f\u53ef\u4ee5\u5b58\u4e00\u500b list \u9032\u53bb\u3002<\/p>\n<h2>Search Data<\/h2>\n<ul>\n<li><code>must<\/code>\uff1a\u5fc5\u9808\u7b26\u5408\u6240\u6709\u689d\u4ef6<\/li>\n<li><code>should<\/code>\uff1a\u7b26\u5408\u5176\u4e2d\u4e00\u500b\u689d\u4ef6\u5373\u53ef<\/li>\n<\/ul>\n<pre class=\"line-numbers\"><code class=\"language-py\">search = TrackDoc.search() \n    .filter('term', is_ready=True) \n    .query('match', title=u'\u6c92\u6709\u7684\u554a')\n\nsearch = TrackDoc.search() \n    .filter('term', is_ready=True) \n    .query(\n        Q('match', title='\u6c92\u6709\u7684\u554a') &amp; \n        Q('match', artist='\u90a3\u6211\u61c2\u4f60\u610f\u601d\u4e86') &amp; \n        Q('match', album='\u6c92\u6709\u7684, \u554a!?')\n    )\n\nq = Q(\n    'bool',\n    must=[\n        Q('match', title={'query': track_name, 'fuzziness': 'AUTO'}),\n    ],\n    should=[\n        Q('match', album={'query': album_name, 'minimum_should_match': '60%'}),\n        Q('match', artist={'query': artist_name, 'minimum_should_match': '80%'}),\n    ],\n    minimum_should_match=1\n)\nsearch = TrackDoc.search().filter('term', is_ready=True).query(q)\n\nq = Q(\n    'bool',\n    should=[\n        Q('term', isrc=q),\n        Q('term', upc=q),\n        Q('match', **{'title.raw': {'query': q}}),\n        Q('multi_match', query=q, fields=['title', 'artist', 'album']),\n    ],\n)\nsearch = Search(index='dps', doc_type=['track', 'album']).query(q)\nsearch = search[:20]\n\n# print the raw Query DSL\nimport uniout\nfrom pprint import pprint\npprint(search.to_dict())\n\nresponse = search.execute()\n\nprint(response.hits.total)\nprint(response[0].title)\nprint(response[0].artist)\nprint(response[0].album)\nprint(response[0].is_ready)<\/code><\/pre>\n<p>ref:<br \/>\n<a href=\"https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/search_dsl.html\">https:\/\/elasticsearch-dsl.readthedocs.org\/en\/latest\/search_dsl.html<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>elasticsearch-dsl \u5c31\u662f\u5b98\u65b9\u767c\u4f48\u7684\u4e00\u5957\u7528\u4f86\u64cd\u4f5c Query DSL \u7684 Python package\uff0c\u7528\u8d77\u4f86\u6709\u9ede\u50cf Django \u7684 ORM\u3002<\/p>\n","protected":false},"author":1,"featured_media":776,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22,4,116],"tags":[70,2,71],"class_list":["post-163","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-about-database","category-about-python","category-about-web-development","tag-elasticsearch","tag-python","tag-search"],"_links":{"self":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/163","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/comments?post=163"}],"version-history":[{"count":0,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/posts\/163\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media\/776"}],"wp:attachment":[{"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/media?parent=163"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/categories?post=163"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/vinta.ws\/code\/wp-json\/wp\/v2\/tags?post=163"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}