Package: graphene-elastic
Elasticsearch (DSL)integration forGraphene.PrerequisitesGraphene 2.x.Support for Graphene 1.x is not intended.Python 3.6, 3.7, 3.8, 3.9 and 3.10.Support for Python 2 is not intended.Elasticsearch 6.x, 7.x.Support for Elasticsearch 5.x is not intended.OpenSearch 1.x, 2.x.Main features and highlightsImplementedElasticsearchConnectionFieldandElasticsearchObjectTypeare the core classes to work withgraphene.Pluggable backends for searching, filtering, ordering, etc. Don’t like
existing ones? Override, extend or write your own.Search backend.Filter backend.Ordering backend.Pagination.Highlighting backend.Source filter backend.Faceted search backend (including global aggregations).Post filter backend.Score filter backend.Query string backend.Simple query string backend.See theRoad-mapfor what’s yet planned to implemented.Do you need a similar tool for Django REST Framework? Checkdjango-elasticsearch-dsl-drf.DemoCheck thelive demo app(FastAPI + Graphene 2 + Elasticsearch 7) hosted on Heroku and bonsai.io.DocumentationDocumentation is available onRead the Docs.InstallationInstall latest stable version from PyPI:pipinstallgraphene-elasticOr latest development version from GitHub:pipinstallhttps://github.com/barseghyanartur/graphene-elastic/archive/master.zipExamplesNoteIn the examples, we useelasticsearch_dslpackage for schema definition.
You can however useopensearch_dslor if you want to achieve
portability betweenElasticsearchandOpenSearch, useanysearchpackage. Read morehere.Install requirementspipinstall-rrequirements.txtPopulate sample dataThe following command will create indexes forUserandPostdocuments
and populate them with sample data:./scripts/populate_elasticsearch_data.shSample document definitionsearch_index/documents/post.pySeeexamples/search_index/documents/post.pyfor full example.importdatetimefromelasticsearch_dslimport(Boolean,Date,Document,InnerDoc,Keyword,Nested,Text,Integer,)classComment(InnerDoc):author=Text(fields={'raw':Keyword()})content=Text(analyzer='snowball')created_at=Date()defage(self):returndatetime.datetime.now()-self.created_atclassPost(Document):title=Text(fields={'raw':Keyword()})content=Text()created_at=Date()published=Boolean()category=Text(fields={'raw':Keyword()})comments=Nested(Comment)tags=Text(analyzer=html_strip,fields={'raw':Keyword(multi=True)},multi=True)num_views=Integer()classIndex:name='blog_post'settings={'number_of_shards':1,'number_of_replicas':1,'blocks':{'read_only_allow_delete':None},}Sample appsSample Flask appRun the sample Flask app:./scripts/run_flask.shOpen Flask graphiql clienthttp://127.0.0.1:8001/graphqlSample Django appRun the sample Django app:./scripts/run_django.shrunserverOpen Django graphiql clienthttp://127.0.0.1:8000/graphqlConnectionField exampleConnectionField is the most flexible and feature rich solution you have. It
uses filter backends which you can tie to your needs the way you want in a
declarative manner.Sample schema definitionimportgraphenefromgraphene_elasticimport(ElasticsearchObjectType,ElasticsearchConnectionField,)fromgraphene_elastic.filter_backendsimport(FilteringFilterBackend,SearchFilterBackend,HighlightFilterBackend,OrderingFilterBackend,DefaultOrderingFilterBackend,)fromgraphene_elastic.constantsimport(LOOKUP_FILTER_PREFIX,LOOKUP_FILTER_TERM,LOOKUP_FILTER_TERMS,LOOKUP_FILTER_WILDCARD,LOOKUP_QUERY_EXCLUDE,LOOKUP_QUERY_IN,)# Object type definitionclassPost(ElasticsearchObjectType):classMeta(object):document=PostDocumentinterfaces=(Node,)filter_backends=[FilteringFilterBackend,SearchFilterBackend,HighlightFilterBackend,OrderingFilterBackend,DefaultOrderingFilterBackend,]# For `FilteringFilterBackend` backendfilter_fields={# The dictionary key (in this case `title`) is the name of# the corresponding GraphQL query argument. The dictionary# value could be simple or complex structure (in this case# complex). The `field` key points to the `title.raw`, which# is the field name in the Elasticsearch document# (`PostDocument`). Since `lookups` key is provided, number# of lookups is limited to the given set, while term is the# default lookup (as specified in `default_lookup`).'title':{'field':'title.raw',# Available lookups'lookups':[LOOKUP_FILTER_TERM,LOOKUP_FILTER_TERMS,LOOKUP_FILTER_PREFIX,LOOKUP_FILTER_WILDCARD,LOOKUP_QUERY_IN,LOOKUP_QUERY_EXCLUDE,],# Default lookup'default_lookup':LOOKUP_FILTER_TERM,},# The dictionary key (in this case `category`) is the name of# the corresponding GraphQL query argument. Since no lookups# or default_lookup is provided, defaults are used (all lookups# available, term is the default lookup). The dictionary value# (in this case `category.raw`) is the field name in the# Elasticsearch document (`PostDocument`).'category':'category.raw',# The dictionary key (in this case `tags`) is the name of# the corresponding GraphQL query argument. Since no lookups# or default_lookup is provided, defaults are used (all lookups# available, term is the default lookup). The dictionary value# (in this case `tags.raw`) is the field name in the# Elasticsearch document (`PostDocument`).'tags':'tags.raw',# The dictionary key (in this case `num_views`) is the name of# the corresponding GraphQL query argument. Since no lookups# or default_lookup is provided, defaults are used (all lookups# available, term is the default lookup). The dictionary value# (in this case `num_views`) is the field name in the# Elasticsearch document (`PostDocument`).'num_views':'num_views',}# For `SearchFilterBackend` backendsearch_fields={'title':{'boost':4},'content':{'boost':2},'category':None,}# For `OrderingFilterBackend` backendordering_fields={# The dictionary key (in this case `tags`) is the name of# the corresponding GraphQL query argument. The dictionary# value (in this case `tags.raw`) is the field name in the# Elasticsearch document (`PostDocument`).'title':'title.raw',# The dictionary key (in this case `created_at`) is the name of# the corresponding GraphQL query argument. The dictionary# value (in this case `created_at`) is the field name in the# Elasticsearch document (`PostDocument`).'created_at':'created_at',# The dictionary key (in this case `num_views`) is the name of# the corresponding GraphQL query argument. The dictionary# value (in this case `num_views`) is the field name in the# Elasticsearch document (`PostDocument`).'num_views':'num_views',}# For `DefaultOrderingFilterBackend` backendordering_defaults=('-num_views',# Field name in the Elasticsearch document'title.raw',# Field name in the Elasticsearch document)# For `HighlightFilterBackend` backendhighlight_fields={'title':{'enabled':True,'options':{'pre_tags':["<b>"],'post_tags':["</b>"],}},'content':{'options':{'fragment_size':50,'number_of_fragments':3}},'category':{},}# Query definitionclassQuery(graphene.ObjectType):all_post_documents=ElasticsearchConnectionField(Post)# Schema definitionschema=graphene.Schema(query=Query)FilterSample queriesSince we didn’t specify any lookups oncategory, by default all lookups
are available and the default lookup would beterm. Note, that in the{value:"Elastic"}part, thevaluestands for default lookup, whatever
it has been set to.queryPostsQuery{allPostDocuments(filter:{category:{value:"Elastic"}}){edges{node{idtitlecategorycontentcreatedAtcomments}}}}But, we could use another lookup (in example below -terms). Note, that
in the{terms:["Elastic","Python"]}part, thetermsis the lookup
name.queryPostsQuery{allPostDocuments(filter:{category:{terms:["Elastic","Python"]}}){edges{node{idtitlecategorycontentcreatedAtcomments}}}}Or apply agt(range) query in addition to filtering:{allPostDocuments(filter:{category:{term:"Python"},numViews:{gt:"700"}}){edges{node{categorytitlecommentsnumViews}}}}Implemented filter lookupsThe following lookups are available:containsends_with(orendsWithfor camelCase)excludeexistsgtgteinis_null(orisNullfor camelCase)ltlteprefixrangestarts_with(orstartsWithfor camelCase)termtermswildcardSeededicated documentation on filter lookupsfor
more information.SearchSearch in all fields:query{allPostDocuments(search:{query:"Release Box"}){edges{node{categorytitlecontent}}}}Search in specific fields:query{allPostDocuments(search:{title:{value:"Release",boost:2},content:{value:"Box"}}){edges{node{categorytitlecontent}}}}OrderingPossible choices areASCandDESC.query{allPostDocuments(filter:{category:{term:"Photography"}},ordering:{title:ASC}){edges{node{categorytitlecontentnumViewstags}}}}PaginationThefirst,last,beforeandafterarguments are supported.
By default number of results is limited to 100.query{allPostDocuments(first:12){pageInfo{startCursorendCursorhasNextPagehasPreviousPage}edges{cursornode{categorytitlecontentnumViews}}}}HighlightingSimply, list the fields you want to highlight. This works only in combination
with search.query{allPostDocuments(search:{content:{value:"alice"},title:{value:"alice"}},highlight:[category,content]){edges{node{titlecontenthighlight}cursor}}}Road-mapRoad-map and development plans.This package is designed afterdjango-elasticsearch-dsl-drfand
is intended to offer similar functionality.Lots of features are planned to be released in the upcoming Beta releases:Suggester backend.Nested backend.Geo-spatial backend.Filter lookupgeo_bounding_box(orgeoBoundingBoxfor camelCase).Filter lookupgeo_distance(orgeoDistancefor camelCase).Filter lookupgeo_polygon(orgeoPolygonfor camelCase).More-like-this backend.Stay tuned or reach out if you want to help.TestingProject is covered with tests.Testing with Dockermakedocker-testRunning tests with virtualenv or toxBy defaults tests are executed against the Elasticsearch 7.x.Run Elasticsearch 7.x with Dockerdocker-composeupelasticsearchInstall test requirementspipinstall-rrequirements/test.txtTo test with all supported Python versions type:toxTo test against specific environment, type:tox-epy38-elastic7To test just your working environment type:./runtests.pyTo run a single test module in your working environment type:./runtests.pysrc/graphene_elastic/tests/test_filter_backend.pyTo run a single test class in a given test module in your working environment
type:./runtests.pysrc/graphene_elastic/tests/test_filter_backend.py::FilterBackendElasticTestCaseDebuggingFor development purposes, you could use the flask app (easy to debug). Standardpdbworks (import pdb; pdb.set_trace()). Ifipdbdoes not work
well for you, useptpdb.Writing documentationKeep the following hierarchy.=====
title
=====

header
======

sub-header
----------

sub-sub-header
~~~~~~~~~~~~~~

sub-sub-sub-header
^^^^^^^^^^^^^^^^^^

sub-sub-sub-sub-header
++++++++++++++++++++++

sub-sub-sub-sub-sub-header
**************************LicenseGPL-2.0-only OR LGPL-2.1-or-laterSupportFor any security issues contact me at the e-mail given in theAuthorsection.
For overall issues, go toGitHub.AuthorArtur Barseghyan <artur.barseghyan@gmail.com>