graphene-elastic


Namegraphene-elastic JSON
Version 0.8.1 PyPI version JSON
download
home_pagehttps://github.com/barseghyanartur/graphene-elastic
SummaryGraphene Elasticsearch/OpenSearch (DSL) integration
upload_time2024-03-05 22:36:08
maintainer
docs_urlNone
authorArtur Barseghyan
requires_python>=3.6
licenseGPL 2.0/LGPL 2.1
keywords api graphql protocol rest relay graphene anysearch elasticsearch elasticsearch-dsl opensearch opensearch-dsl
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ================
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"
}
        
Elapsed time: 0.20170s