================
graphene-elastic
================
`Elasticsearch (DSL) <https://elasticsearch-dsl.readthedocs.io/en/latest/>`__/
`OpenSearch (DSL) <https://opensearch.org/docs/latest/clients/python/>`__
integration for `Graphene <http://graphene-python.org/>`__.
.. image:: https://img.shields.io/pypi/v/graphene-elastic.svg
:target: https://pypi.python.org/pypi/graphene-elastic
:alt: PyPI Version
.. image:: https://img.shields.io/pypi/pyversions/graphene-elastic.svg
:target: https://pypi.python.org/pypi/graphene-elastic/
:alt: Supported Python versions
.. image:: https://github.com/barseghyanartur/graphene-elastic/workflows/test/badge.svg
:target: https://github.com/barseghyanartur/graphene-elastic/actions?query=workflow%3Atest
:alt: Build Status
.. image:: https://readthedocs.org/projects/graphene-elastic/badge/?version=latest
:target: http://graphene-elastic.readthedocs.io/en/latest/?badge=latest
:alt: Documentation Status
.. image:: https://img.shields.io/badge/license-GPL--2.0--only%20OR%20LGPL--2.1--or--later-blue.svg
:target: https://github.com/barseghyanartur/graphene-elastic/#License
:alt: GPL-2.0-only OR LGPL-2.1-or-later
.. image:: https://coveralls.io/repos/github/barseghyanartur/graphene-elastic/badge.svg?branch=master
:target: https://coveralls.io/github/barseghyanartur/graphene-elastic?branch=master
:alt: Coverage
Prerequisites
=============
- Graphene 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 highlights
============================
- Implemented ``ElasticsearchConnectionField`` and ``ElasticsearchObjectType``
are the core classes to work with ``graphene``.
- 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 the `Road-map`_ for what's yet planned to implemented.
Do you need a similar tool for Django REST Framework? Check
`django-elasticsearch-dsl-drf
<https://github.com/barseghyanartur/django-elasticsearch-dsl-drf>`__.
Demo
====
Check the `live demo app <https://graphene-elastic.herokuapp.com/?query=query%20%7B%0A%20%20allPostDocuments%7B%0A%20%20%20%20edges%20%7B%0A%20%20%20%20%20%20node%20%7B%0A%20%20%20%20%20%20%20%20id%0A%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20content%0A%20%20%20%20%20%20%20%20tags%0A%20%20%20%20%20%20%20%20createdAt%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D>`__
(FastAPI + Graphene 2 + Elasticsearch 7) hosted on Heroku and bonsai.io.
Documentation
=============
Documentation is available on `Read the Docs
<http://graphene-elastic.readthedocs.io/>`_.
Installation
============
Install latest stable version from PyPI:
.. code-block:: bash
pip install graphene-elastic
Or latest development version from GitHub:
.. code-block:: bash
pip install https://github.com/barseghyanartur/graphene-elastic/archive/master.zip
.. note::
Staring from version 0.8, the ``elasticsearch`` and ``elasticsearch-dsl``
packages are no longer installed by default. You must either install them
explicitly in your requirements or install as optional dependencies as
follows: ``pip install graphene-elastic[elasticsearch]``.
Alternatively, you can use ``opensearch-py`` and ``opensearch-dsl``.
You would then need to install the ``opensearch-py`` and ``opensearch-dsl``
packages explicitly in your requirements or install them as optional
dependencies as follows: ``pip install graphene-elastic[opensearch]``.
Examples
========
.. note::
In the examples, we use ``elasticsearch_dsl`` package for schema definition.
You can however use ``opensearch_dsl`` or if you want to achieve
portability between ``Elasticsearch`` and ``OpenSearch``, use ``anysearch``
package. Read more `here <https://github.com/barseghyanartur/anysearch>`__.
Install requirements
--------------------
.. code-block:: sh
pip install -r requirements.txt
Populate sample data
--------------------
The following command will create indexes for ``User`` and ``Post`` documents
and populate them with sample data:
.. code-block:: sh
./scripts/populate_elasticsearch_data.sh
Sample document definition
--------------------------
*search_index/documents/post.py*
See `examples/search_index/documents/post.py
<https://github.com/barseghyanartur/graphene-elastic/blob/master/examples/search_index/documents/post.py>`_
for full example.
.. code-block:: python
import datetime
from elasticsearch_dsl import (
Boolean,
Date,
Document,
InnerDoc,
Keyword,
Nested,
Text,
Integer,
)
class Comment(InnerDoc):
author = Text(fields={'raw': Keyword()})
content = Text(analyzer='snowball')
created_at = Date()
def age(self):
return datetime.datetime.now() - self.created_at
class Post(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()
class Index:
name = 'blog_post'
settings = {
'number_of_shards': 1,
'number_of_replicas': 1,
'blocks': {'read_only_allow_delete': None},
}
Sample apps
-----------
Sample Flask app
~~~~~~~~~~~~~~~~
**Run the sample Flask app:**
.. code-block:: sh
./scripts/run_flask.sh
**Open Flask graphiql client**
.. code-block:: text
http://127.0.0.1:8001/graphql
Sample Django app
~~~~~~~~~~~~~~~~~
**Run the sample Django app:**
.. code-block:: sh
./scripts/run_django.sh runserver
**Open Django graphiql client**
.. code-block:: text
http://127.0.0.1:8000/graphql
ConnectionField example
~~~~~~~~~~~~~~~~~~~~~~~
ConnectionField 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 definition**
.. code-block:: python
import graphene
from graphene_elastic import (
ElasticsearchObjectType,
ElasticsearchConnectionField,
)
from graphene_elastic.filter_backends import (
FilteringFilterBackend,
SearchFilterBackend,
HighlightFilterBackend,
OrderingFilterBackend,
DefaultOrderingFilterBackend,
)
from graphene_elastic.constants import (
LOOKUP_FILTER_PREFIX,
LOOKUP_FILTER_TERM,
LOOKUP_FILTER_TERMS,
LOOKUP_FILTER_WILDCARD,
LOOKUP_QUERY_EXCLUDE,
LOOKUP_QUERY_IN,
)
# Object type definition
class Post(ElasticsearchObjectType):
class Meta(object):
document = PostDocument
interfaces = (Node,)
filter_backends = [
FilteringFilterBackend,
SearchFilterBackend,
HighlightFilterBackend,
OrderingFilterBackend,
DefaultOrderingFilterBackend,
]
# For `FilteringFilterBackend` backend
filter_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` backend
search_fields = {
'title': {'boost': 4},
'content': {'boost': 2},
'category': None,
}
# For `OrderingFilterBackend` backend
ordering_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` backend
ordering_defaults = (
'-num_views', # Field name in the Elasticsearch document
'title.raw', # Field name in the Elasticsearch document
)
# For `HighlightFilterBackend` backend
highlight_fields = {
'title': {
'enabled': True,
'options': {
'pre_tags': ["<b>"],
'post_tags': ["</b>"],
}
},
'content': {
'options': {
'fragment_size': 50,
'number_of_fragments': 3
}
},
'category': {},
}
# Query definition
class Query(graphene.ObjectType):
all_post_documents = ElasticsearchConnectionField(Post)
# Schema definition
schema = graphene.Schema(query=Query)
Filter
^^^^^^
Sample queries
++++++++++++++
Since we didn't specify any lookups on `category`, by default all lookups
are available and the default lookup would be ``term``. Note, that in the
``{value:"Elastic"}`` part, the ``value`` stands for default lookup, whatever
it has been set to.
.. code-block:: javascript
query PostsQuery {
allPostDocuments(filter:{category:{value:"Elastic"}}) {
edges {
node {
id
title
category
content
createdAt
comments
}
}
}
}
But, we could use another lookup (in example below - ``terms``). Note, that
in the ``{terms:["Elastic", "Python"]}`` part, the ``terms`` is the lookup
name.
.. code-block:: javascript
query PostsQuery {
allPostDocuments(
filter:{category:{terms:["Elastic", "Python"]}}
) {
edges {
node {
id
title
category
content
createdAt
comments
}
}
}
}
Or apply a ``gt`` (``range``) query in addition to filtering:
.. code-block:: javascript
{
allPostDocuments(filter:{
category:{term:"Python"},
numViews:{gt:"700"}
}) {
edges {
node {
category
title
comments
numViews
}
}
}
}
Implemented filter lookups
++++++++++++++++++++++++++
The following lookups are available:
- ``contains``
- ``ends_with`` (or ``endsWith`` for camelCase)
- ``exclude``
- ``exists``
- ``gt``
- ``gte``
- ``in``
- ``is_null`` (or ``isNull`` for camelCase)
- ``lt``
- ``lte``
- ``prefix``
- ``range``
- ``starts_with`` (or ``startsWith`` for camelCase)
- ``term``
- ``terms``
- ``wildcard``
See `dedicated documentation on filter lookups
<https://graphene-elastic.readthedocs.io/en/latest/filtering.html>`__ for
more information.
Search
^^^^^^
Search in all fields:
.. code-block:: javascript
query {
allPostDocuments(
search:{query:"Release Box"}
) {
edges {
node {
category
title
content
}
}
}
}
Search in specific fields:
.. code-block:: javascript
query {
allPostDocuments(
search:{
title:{value:"Release", boost:2},
content:{value:"Box"}
}
) {
edges {
node {
category
title
content
}
}
}
}
Ordering
^^^^^^^^
Possible choices are ``ASC`` and ``DESC``.
.. code-block:: javascript
query {
allPostDocuments(
filter:{category:{term:"Photography"}},
ordering:{title:ASC}
) {
edges {
node {
category
title
content
numViews
tags
}
}
}
}
Pagination
^^^^^^^^^^
The ``first``, ``last``, ``before`` and ``after`` arguments are supported.
By default number of results is limited to 100.
.. code-block:: javascript
query {
allPostDocuments(first:12) {
pageInfo {
startCursor
endCursor
hasNextPage
hasPreviousPage
}
edges {
cursor
node {
category
title
content
numViews
}
}
}
}
Highlighting
^^^^^^^^^^^^
Simply, list the fields you want to highlight. This works only in combination
with search.
.. code-block:: javascript
query {
allPostDocuments(
search:{content:{value:"alice"}, title:{value:"alice"}},
highlight:[category, content]
) {
edges {
node {
title
content
highlight
}
cursor
}
}
}
Road-map
========
Road-map and development plans.
This package is designed after `django-elasticsearch-dsl-drf
<https://github.com/barseghyanartur/django-elasticsearch-dsl-drf>`__ and
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 lookup ``geo_bounding_box`` (or ``geoBoundingBox`` for camelCase).
- Filter lookup ``geo_distance`` (or ``geoDistance`` for camelCase).
- Filter lookup ``geo_polygon`` (or ``geoPolygon`` for camelCase).
- More-like-this backend.
Stay tuned or reach out if you want to help.
Testing
=======
Project is covered with tests.
Testing with Docker
-------------------
.. code-block:: sh
make docker-test
Running tests with virtualenv or tox
------------------------------------
By defaults tests are executed against the Elasticsearch 7.x.
**Run Elasticsearch 7.x with Docker**
.. code-block:: bash
docker-compose up elasticsearch
**Install test requirements**
.. code-block:: sh
pip install -r requirements/test.txt
To test with all supported Python versions type:
.. code-block:: sh
tox
To test against specific environment, type:
.. code-block:: sh
tox -e py38-elastic7
To test just your working environment type:
.. code-block:: sh
./runtests.py
To run a single test module in your working environment type:
.. code-block:: sh
./runtests.py src/graphene_elastic/tests/test_filter_backend.py
To run a single test class in a given test module in your working environment
type:
.. code-block:: sh
./runtests.py src/graphene_elastic/tests/test_filter_backend.py::FilterBackendElasticTestCase
Debugging
=========
For development purposes, you could use the flask app (easy to debug). Standard
``pdb`` works (``import pdb; pdb.set_trace()``). If ``ipdb`` does not work
well for you, use ``ptpdb``.
Writing documentation
=====================
Keep the following hierarchy.
.. code-block:: text
=====
title
=====
header
======
sub-header
----------
sub-sub-header
~~~~~~~~~~~~~~
sub-sub-sub-header
^^^^^^^^^^^^^^^^^^
sub-sub-sub-sub-header
++++++++++++++++++++++
sub-sub-sub-sub-sub-header
**************************
License
=======
GPL-2.0-only OR LGPL-2.1-or-later
Support
=======
For any security issues contact me at the e-mail given in the `Author`_ section.
For overall issues, go to `GitHub <https://github.com/barseghyanartur/graphene-elastic/issues>`_.
Author
======
Artur Barseghyan <artur.barseghyan@gmail.com>
Raw data
{
"_id": null,
"home_page": "https://github.com/barseghyanartur/graphene-elastic",
"name": "graphene-elastic",
"maintainer": "",
"docs_url": null,
"requires_python": ">=3.6",
"maintainer_email": "",
"keywords": "api graphql protocol rest relay graphene anysearch elasticsearch elasticsearch-dsl opensearch opensearch-dsl",
"author": "Artur Barseghyan",
"author_email": "artur.barseghyan@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/2e/c3/07117aa415f2df22bf5eade1dcbd8c9a95c004715a2efe66122a77fa2fa3/graphene-elastic-0.8.1.tar.gz",
"platform": null,
"description": "================\ngraphene-elastic\n================\n`Elasticsearch (DSL) <https://elasticsearch-dsl.readthedocs.io/en/latest/>`__/\n`OpenSearch (DSL) <https://opensearch.org/docs/latest/clients/python/>`__\nintegration for `Graphene <http://graphene-python.org/>`__.\n\n.. image:: https://img.shields.io/pypi/v/graphene-elastic.svg\n :target: https://pypi.python.org/pypi/graphene-elastic\n :alt: PyPI Version\n\n.. image:: https://img.shields.io/pypi/pyversions/graphene-elastic.svg\n :target: https://pypi.python.org/pypi/graphene-elastic/\n :alt: Supported Python versions\n\n.. image:: https://github.com/barseghyanartur/graphene-elastic/workflows/test/badge.svg\n :target: https://github.com/barseghyanartur/graphene-elastic/actions?query=workflow%3Atest\n :alt: Build Status\n\n.. image:: https://readthedocs.org/projects/graphene-elastic/badge/?version=latest\n :target: http://graphene-elastic.readthedocs.io/en/latest/?badge=latest\n :alt: Documentation Status\n\n.. image:: https://img.shields.io/badge/license-GPL--2.0--only%20OR%20LGPL--2.1--or--later-blue.svg\n :target: https://github.com/barseghyanartur/graphene-elastic/#License\n :alt: GPL-2.0-only OR LGPL-2.1-or-later\n\n.. image:: https://coveralls.io/repos/github/barseghyanartur/graphene-elastic/badge.svg?branch=master\n :target: https://coveralls.io/github/barseghyanartur/graphene-elastic?branch=master\n :alt: Coverage\n\nPrerequisites\n=============\n- Graphene 2.x. *Support for Graphene 1.x is not intended.*\n- Python 3.6, 3.7, 3.8, 3.9 and 3.10. *Support for Python 2 is not intended.*\n- Elasticsearch 6.x, 7.x. *Support for Elasticsearch 5.x is not intended.*\n- OpenSearch 1.x, 2.x.\n\nMain features and highlights\n============================\n- Implemented ``ElasticsearchConnectionField`` and ``ElasticsearchObjectType``\n are the core classes to work with ``graphene``.\n- Pluggable backends for searching, filtering, ordering, etc. Don't like\n existing ones? Override, extend or write your own.\n- Search backend.\n- Filter backend.\n- Ordering backend.\n- Pagination.\n- Highlighting backend.\n- Source filter backend.\n- Faceted search backend (including global aggregations).\n- Post filter backend.\n- Score filter backend.\n- Query string backend.\n- Simple query string backend.\n\nSee the `Road-map`_ for what's yet planned to implemented.\n\nDo you need a similar tool for Django REST Framework? Check\n`django-elasticsearch-dsl-drf\n<https://github.com/barseghyanartur/django-elasticsearch-dsl-drf>`__.\n\nDemo\n====\nCheck the `live demo app <https://graphene-elastic.herokuapp.com/?query=query%20%7B%0A%20%20allPostDocuments%7B%0A%20%20%20%20edges%20%7B%0A%20%20%20%20%20%20node%20%7B%0A%20%20%20%20%20%20%20%20id%0A%20%20%20%20%20%20%20%20title%0A%20%20%20%20%20%20%20%20content%0A%20%20%20%20%20%20%20%20tags%0A%20%20%20%20%20%20%20%20createdAt%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D>`__\n(FastAPI + Graphene 2 + Elasticsearch 7) hosted on Heroku and bonsai.io.\n\nDocumentation\n=============\nDocumentation is available on `Read the Docs\n<http://graphene-elastic.readthedocs.io/>`_.\n\nInstallation\n============\nInstall latest stable version from PyPI:\n\n.. code-block:: bash\n\n pip install graphene-elastic\n\nOr latest development version from GitHub:\n\n.. code-block:: bash\n\n pip install https://github.com/barseghyanartur/graphene-elastic/archive/master.zip\n\n.. note::\n\n Staring from version 0.8, the ``elasticsearch`` and ``elasticsearch-dsl``\n packages are no longer installed by default. You must either install them\n explicitly in your requirements or install as optional dependencies as\n follows: ``pip install graphene-elastic[elasticsearch]``.\n Alternatively, you can use ``opensearch-py`` and ``opensearch-dsl``.\n You would then need to install the ``opensearch-py`` and ``opensearch-dsl``\n packages explicitly in your requirements or install them as optional\n dependencies as follows: ``pip install graphene-elastic[opensearch]``.\n\nExamples\n========\n.. note::\n\n In the examples, we use ``elasticsearch_dsl`` package for schema definition.\n You can however use ``opensearch_dsl`` or if you want to achieve\n portability between ``Elasticsearch`` and ``OpenSearch``, use ``anysearch``\n package. Read more `here <https://github.com/barseghyanartur/anysearch>`__.\n\nInstall requirements\n--------------------\n.. code-block:: sh\n\n pip install -r requirements.txt\n\nPopulate sample data\n--------------------\nThe following command will create indexes for ``User`` and ``Post`` documents\nand populate them with sample data:\n\n.. code-block:: sh\n\n ./scripts/populate_elasticsearch_data.sh\n\nSample document definition\n--------------------------\n*search_index/documents/post.py*\n\nSee `examples/search_index/documents/post.py\n<https://github.com/barseghyanartur/graphene-elastic/blob/master/examples/search_index/documents/post.py>`_\nfor full example.\n\n.. code-block:: python\n\n import datetime\n from elasticsearch_dsl import (\n Boolean,\n Date,\n Document,\n InnerDoc,\n Keyword,\n Nested,\n Text,\n Integer,\n )\n\n class Comment(InnerDoc):\n\n author = Text(fields={'raw': Keyword()})\n content = Text(analyzer='snowball')\n created_at = Date()\n\n def age(self):\n return datetime.datetime.now() - self.created_at\n\n\n class Post(Document):\n\n title = Text(\n fields={'raw': Keyword()}\n )\n content = Text()\n created_at = Date()\n published = Boolean()\n category = Text(\n fields={'raw': Keyword()}\n )\n comments = Nested(Comment)\n tags = Text(\n analyzer=html_strip,\n fields={'raw': Keyword(multi=True)},\n multi=True\n )\n num_views = Integer()\n\n class Index:\n name = 'blog_post'\n settings = {\n 'number_of_shards': 1,\n 'number_of_replicas': 1,\n 'blocks': {'read_only_allow_delete': None},\n }\n\nSample apps\n-----------\nSample Flask app\n~~~~~~~~~~~~~~~~\n**Run the sample Flask app:**\n\n.. code-block:: sh\n\n ./scripts/run_flask.sh\n\n**Open Flask graphiql client**\n\n.. code-block:: text\n\n http://127.0.0.1:8001/graphql\n\nSample Django app\n~~~~~~~~~~~~~~~~~\n**Run the sample Django app:**\n\n.. code-block:: sh\n\n ./scripts/run_django.sh runserver\n\n**Open Django graphiql client**\n\n.. code-block:: text\n\n http://127.0.0.1:8000/graphql\n\nConnectionField example\n~~~~~~~~~~~~~~~~~~~~~~~\nConnectionField is the most flexible and feature rich solution you have. It\nuses filter backends which you can tie to your needs the way you want in a\ndeclarative manner.\n\n**Sample schema definition**\n\n.. code-block:: python\n\n import graphene\n from graphene_elastic import (\n ElasticsearchObjectType,\n ElasticsearchConnectionField,\n )\n from graphene_elastic.filter_backends import (\n FilteringFilterBackend,\n SearchFilterBackend,\n HighlightFilterBackend,\n OrderingFilterBackend,\n DefaultOrderingFilterBackend,\n )\n from graphene_elastic.constants import (\n LOOKUP_FILTER_PREFIX,\n LOOKUP_FILTER_TERM,\n LOOKUP_FILTER_TERMS,\n LOOKUP_FILTER_WILDCARD,\n LOOKUP_QUERY_EXCLUDE,\n LOOKUP_QUERY_IN,\n )\n\n # Object type definition\n class Post(ElasticsearchObjectType):\n\n class Meta(object):\n document = PostDocument\n interfaces = (Node,)\n filter_backends = [\n FilteringFilterBackend,\n SearchFilterBackend,\n HighlightFilterBackend,\n OrderingFilterBackend,\n DefaultOrderingFilterBackend,\n ]\n\n # For `FilteringFilterBackend` backend\n filter_fields = {\n # The dictionary key (in this case `title`) is the name of\n # the corresponding GraphQL query argument. The dictionary\n # value could be simple or complex structure (in this case\n # complex). The `field` key points to the `title.raw`, which\n # is the field name in the Elasticsearch document\n # (`PostDocument`). Since `lookups` key is provided, number\n # of lookups is limited to the given set, while term is the\n # default lookup (as specified in `default_lookup`).\n 'title': {\n 'field': 'title.raw',\n # Available lookups\n 'lookups': [\n LOOKUP_FILTER_TERM,\n LOOKUP_FILTER_TERMS,\n LOOKUP_FILTER_PREFIX,\n LOOKUP_FILTER_WILDCARD,\n LOOKUP_QUERY_IN,\n LOOKUP_QUERY_EXCLUDE,\n ],\n # Default lookup\n 'default_lookup': LOOKUP_FILTER_TERM,\n },\n\n # The dictionary key (in this case `category`) is the name of\n # the corresponding GraphQL query argument. Since no lookups\n # or default_lookup is provided, defaults are used (all lookups\n # available, term is the default lookup). The dictionary value\n # (in this case `category.raw`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'category': 'category.raw',\n\n # The dictionary key (in this case `tags`) is the name of\n # the corresponding GraphQL query argument. Since no lookups\n # or default_lookup is provided, defaults are used (all lookups\n # available, term is the default lookup). The dictionary value\n # (in this case `tags.raw`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'tags': 'tags.raw',\n\n # The dictionary key (in this case `num_views`) is the name of\n # the corresponding GraphQL query argument. Since no lookups\n # or default_lookup is provided, defaults are used (all lookups\n # available, term is the default lookup). The dictionary value\n # (in this case `num_views`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'num_views': 'num_views',\n }\n\n # For `SearchFilterBackend` backend\n search_fields = {\n 'title': {'boost': 4},\n 'content': {'boost': 2},\n 'category': None,\n }\n\n # For `OrderingFilterBackend` backend\n ordering_fields = {\n # The dictionary key (in this case `tags`) is the name of\n # the corresponding GraphQL query argument. The dictionary\n # value (in this case `tags.raw`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'title': 'title.raw',\n\n # The dictionary key (in this case `created_at`) is the name of\n # the corresponding GraphQL query argument. The dictionary\n # value (in this case `created_at`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'created_at': 'created_at',\n\n # The dictionary key (in this case `num_views`) is the name of\n # the corresponding GraphQL query argument. The dictionary\n # value (in this case `num_views`) is the field name in the\n # Elasticsearch document (`PostDocument`).\n 'num_views': 'num_views',\n }\n\n # For `DefaultOrderingFilterBackend` backend\n ordering_defaults = (\n '-num_views', # Field name in the Elasticsearch document\n 'title.raw', # Field name in the Elasticsearch document\n )\n\n # For `HighlightFilterBackend` backend\n highlight_fields = {\n 'title': {\n 'enabled': True,\n 'options': {\n 'pre_tags': [\"<b>\"],\n 'post_tags': [\"</b>\"],\n }\n },\n 'content': {\n 'options': {\n 'fragment_size': 50,\n 'number_of_fragments': 3\n }\n },\n 'category': {},\n }\n\n # Query definition\n class Query(graphene.ObjectType):\n all_post_documents = ElasticsearchConnectionField(Post)\n\n # Schema definition\n schema = graphene.Schema(query=Query)\n\nFilter\n^^^^^^\n\nSample queries\n++++++++++++++\n\nSince we didn't specify any lookups on `category`, by default all lookups\nare available and the default lookup would be ``term``. Note, that in the\n``{value:\"Elastic\"}`` part, the ``value`` stands for default lookup, whatever\nit has been set to.\n\n.. code-block:: javascript\n\n query PostsQuery {\n allPostDocuments(filter:{category:{value:\"Elastic\"}}) {\n edges {\n node {\n id\n title\n category\n content\n createdAt\n comments\n }\n }\n }\n }\n\nBut, we could use another lookup (in example below - ``terms``). Note, that\nin the ``{terms:[\"Elastic\", \"Python\"]}`` part, the ``terms`` is the lookup\nname.\n\n.. code-block:: javascript\n\n query PostsQuery {\n allPostDocuments(\n filter:{category:{terms:[\"Elastic\", \"Python\"]}}\n ) {\n edges {\n node {\n id\n title\n category\n content\n createdAt\n comments\n }\n }\n }\n }\n\nOr apply a ``gt`` (``range``) query in addition to filtering:\n\n.. code-block:: javascript\n\n {\n allPostDocuments(filter:{\n category:{term:\"Python\"},\n numViews:{gt:\"700\"}\n }) {\n edges {\n node {\n category\n title\n comments\n numViews\n }\n }\n }\n }\n\nImplemented filter lookups\n++++++++++++++++++++++++++\nThe following lookups are available:\n\n- ``contains``\n- ``ends_with`` (or ``endsWith`` for camelCase)\n- ``exclude``\n- ``exists``\n- ``gt``\n- ``gte``\n- ``in``\n- ``is_null`` (or ``isNull`` for camelCase)\n- ``lt``\n- ``lte``\n- ``prefix``\n- ``range``\n- ``starts_with`` (or ``startsWith`` for camelCase)\n- ``term``\n- ``terms``\n- ``wildcard``\n\nSee `dedicated documentation on filter lookups\n<https://graphene-elastic.readthedocs.io/en/latest/filtering.html>`__ for\nmore information.\n\nSearch\n^^^^^^\nSearch in all fields:\n\n.. code-block:: javascript\n\n query {\n allPostDocuments(\n search:{query:\"Release Box\"}\n ) {\n edges {\n node {\n category\n title\n content\n }\n }\n }\n }\n\nSearch in specific fields:\n\n.. code-block:: javascript\n\n query {\n allPostDocuments(\n search:{\n title:{value:\"Release\", boost:2},\n content:{value:\"Box\"}\n }\n ) {\n edges {\n node {\n category\n title\n content\n }\n }\n }\n }\n\nOrdering\n^^^^^^^^\nPossible choices are ``ASC`` and ``DESC``.\n\n.. code-block:: javascript\n\n query {\n allPostDocuments(\n filter:{category:{term:\"Photography\"}},\n ordering:{title:ASC}\n ) {\n edges {\n node {\n category\n title\n content\n numViews\n tags\n }\n }\n }\n }\n\nPagination\n^^^^^^^^^^\nThe ``first``, ``last``, ``before`` and ``after`` arguments are supported.\nBy default number of results is limited to 100.\n\n.. code-block:: javascript\n\n query {\n allPostDocuments(first:12) {\n pageInfo {\n startCursor\n endCursor\n hasNextPage\n hasPreviousPage\n }\n edges {\n cursor\n node {\n category\n title\n content\n numViews\n }\n }\n }\n }\n\nHighlighting\n^^^^^^^^^^^^\nSimply, list the fields you want to highlight. This works only in combination\nwith search.\n\n.. code-block:: javascript\n\n query {\n allPostDocuments(\n search:{content:{value:\"alice\"}, title:{value:\"alice\"}},\n highlight:[category, content]\n ) {\n edges {\n node {\n title\n content\n highlight\n }\n cursor\n }\n }\n }\n\nRoad-map\n========\nRoad-map and development plans.\n\nThis package is designed after `django-elasticsearch-dsl-drf\n<https://github.com/barseghyanartur/django-elasticsearch-dsl-drf>`__ and\nis intended to offer similar functionality.\n\nLots of features are planned to be released in the upcoming Beta releases:\n\n- Suggester backend.\n- Nested backend.\n- Geo-spatial backend.\n- Filter lookup ``geo_bounding_box`` (or ``geoBoundingBox`` for camelCase).\n- Filter lookup ``geo_distance`` (or ``geoDistance`` for camelCase).\n- Filter lookup ``geo_polygon`` (or ``geoPolygon`` for camelCase).\n- More-like-this backend.\n\nStay tuned or reach out if you want to help.\n\nTesting\n=======\nProject is covered with tests.\n\nTesting with Docker\n-------------------\n.. code-block:: sh\n\n make docker-test\n\nRunning tests with virtualenv or tox\n------------------------------------\nBy defaults tests are executed against the Elasticsearch 7.x.\n\n**Run Elasticsearch 7.x with Docker**\n\n.. code-block:: bash\n\n docker-compose up elasticsearch\n\n**Install test requirements**\n\n.. code-block:: sh\n\n pip install -r requirements/test.txt\n\nTo test with all supported Python versions type:\n\n.. code-block:: sh\n\n tox\n\nTo test against specific environment, type:\n\n.. code-block:: sh\n\n tox -e py38-elastic7\n\nTo test just your working environment type:\n\n.. code-block:: sh\n\n ./runtests.py\n\nTo run a single test module in your working environment type:\n\n.. code-block:: sh\n\n ./runtests.py src/graphene_elastic/tests/test_filter_backend.py\n\nTo run a single test class in a given test module in your working environment\ntype:\n\n.. code-block:: sh\n\n ./runtests.py src/graphene_elastic/tests/test_filter_backend.py::FilterBackendElasticTestCase\n\nDebugging\n=========\nFor development purposes, you could use the flask app (easy to debug). Standard\n``pdb`` works (``import pdb; pdb.set_trace()``). If ``ipdb`` does not work\nwell for you, use ``ptpdb``.\n\nWriting documentation\n=====================\nKeep the following hierarchy.\n\n.. code-block:: text\n\n =====\n title\n =====\n\n header\n ======\n\n sub-header\n ----------\n\n sub-sub-header\n ~~~~~~~~~~~~~~\n\n sub-sub-sub-header\n ^^^^^^^^^^^^^^^^^^\n\n sub-sub-sub-sub-header\n ++++++++++++++++++++++\n\n sub-sub-sub-sub-sub-header\n **************************\n\nLicense\n=======\nGPL-2.0-only OR LGPL-2.1-or-later\n\nSupport\n=======\nFor any security issues contact me at the e-mail given in the `Author`_ section.\nFor overall issues, go to `GitHub <https://github.com/barseghyanartur/graphene-elastic/issues>`_.\n\nAuthor\n======\nArtur Barseghyan <artur.barseghyan@gmail.com>\n",
"bugtrack_url": null,
"license": "GPL 2.0/LGPL 2.1",
"summary": "Graphene Elasticsearch/OpenSearch (DSL) integration",
"version": "0.8.1",
"project_urls": {
"Bug Tracker": "https://github.com/barseghyanartur/graphene-elastic/issues",
"Changelog": "https://graphene-elastic.readthedocs.io/en/latest/changelog.html",
"Documentation": "https://graphene-elastic.readthedocs.io/",
"Homepage": "https://github.com/barseghyanartur/graphene-elastic",
"Source Code": "https://github.com/barseghyanartur/graphene-elastic/"
},
"split_keywords": [
"api",
"graphql",
"protocol",
"rest",
"relay",
"graphene",
"anysearch",
"elasticsearch",
"elasticsearch-dsl",
"opensearch",
"opensearch-dsl"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "b25130c2e56da3bef5e163dfdbdd6e223bd62b4738193e598bde3b8e5746b9ed",
"md5": "732f925d43cbf860d283f1122c040cf8",
"sha256": "14209df67ca7812fa150a42d958db530cc8ca42a0e1d9c9ea497b80962bca9c6"
},
"downloads": -1,
"filename": "graphene_elastic-0.8.1-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "732f925d43cbf860d283f1122c040cf8",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": ">=3.6",
"size": 116265,
"upload_time": "2024-03-05T22:36:05",
"upload_time_iso_8601": "2024-03-05T22:36:05.458846Z",
"url": "https://files.pythonhosted.org/packages/b2/51/30c2e56da3bef5e163dfdbdd6e223bd62b4738193e598bde3b8e5746b9ed/graphene_elastic-0.8.1-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "2ec307117aa415f2df22bf5eade1dcbd8c9a95c004715a2efe66122a77fa2fa3",
"md5": "4b2582b38df5f0b03a087a4b78a7583d",
"sha256": "2ee1f8adb5f82a81680172ef8240abc3ea723126f752a5c02efde07cc95c03ce"
},
"downloads": -1,
"filename": "graphene-elastic-0.8.1.tar.gz",
"has_sig": false,
"md5_digest": "4b2582b38df5f0b03a087a4b78a7583d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 83504,
"upload_time": "2024-03-05T22:36:08",
"upload_time_iso_8601": "2024-03-05T22:36:08.178798Z",
"url": "https://files.pythonhosted.org/packages/2e/c3/07117aa415f2df22bf5eade1dcbd8c9a95c004715a2efe66122a77fa2fa3/graphene-elastic-0.8.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-05 22:36:08",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "barseghyanartur",
"github_project": "graphene-elastic",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"tox": true,
"lcname": "graphene-elastic"
}