跳转到主要内容

Graphene Elasticsearch/OpenSearch (DSL)集成

项目描述

Elasticsearch (DSL)/ OpenSearch (DSL)集成,用于Graphene

PyPI Version Supported Python versions Build Status Documentation Status GPL-2.0-only OR LGPL-2.1-or-later Coverage

先决条件

  • Graphene 2.x。 不打算支持Graphene 1.x。

  • Python 3.6, 3.7, 3.8, 3.9和3.10。 不打算支持Python 2。

  • Elasticsearch 6.x, 7.x。 不打算支持Elasticsearch 5.x。

  • OpenSearch 1.x, 2.x。

主要功能和亮点

  • 实现了ElasticsearchConnectionFieldElasticsearchObjectType,它们是处理graphene的核心类。

  • 可插拔的后端用于搜索、过滤、排序等。不喜欢现有的?重写、扩展或编写自己的。

  • 搜索后端。

  • 过滤后端。

  • 排序后端。

  • 分页。

  • 高亮后端。

  • 源过滤后端。

  • 分面搜索后端(包括全局聚合)。

  • 后过滤后端。

  • 分数过滤后端。

  • 查询字符串后端。

  • 简单的查询字符串后端。

查看路线图了解尚未实施的功能。

您需要Django REST Framework的类似工具吗?请查看django-elasticsearch-dsl-drf

演示

查看实时演示应用程序(FastAPI + Graphene 2 + Elasticsearch 7),该应用程序托管在Heroku和bonsai.io。

文档

文档可在Read the Docs上找到。

安装

从PyPI安装最新稳定版本

pip install graphene-elastic

或从GitHub安装最新开发版本

pip install https://github.com/barseghyanartur/graphene-elastic/archive/master.zip

示例

安装需求

pip install -r requirements.txt

填充示例数据

以下命令将为UserPost文档创建索引,并用示例数据填充它们

./scripts/populate_elasticsearch_data.sh

示例文档定义

search_index/documents/post.py

查看examples/search_index/documents/post.py的完整示例。

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},
        }

示例应用程序

示例Flask应用程序

运行示例Flask应用程序

./scripts/run_flask.sh

打开Flask graphiql客户端

http://127.0.0.1:8001/graphql

示例Django应用程序

运行示例Django应用程序

./scripts/run_django.sh runserver

打开Django graphiql客户端

http://127.0.0.1:8000/graphql

连接字段示例

连接字段是您拥有的最灵活且功能丰富的解决方案。它使用筛选后端,您可以以声明性的方式根据需要将其绑定到您的需求。

示例模式定义

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)
筛选器
示例查询

由于我们没有在category上指定任何查找,因此默认所有查找都可用,默认查找将是term。注意,在{value:"Elastic"}部分,value表示默认查找,无论其设置为多少。

query PostsQuery {
  allPostDocuments(filter:{category:{value:"Elastic"}}) {
    edges {
      node {
        id
        title
        category
        content
        createdAt
        comments
      }
    }
  }
}

但是,我们可以使用另一个查找(以下示例中的terms)。注意,在{terms:["Elastic", "Python"]}部分,terms是查找名称。

query PostsQuery {
  allPostDocuments(
        filter:{category:{terms:["Elastic", "Python"]}}
    ) {
    edges {
      node {
        id
        title
        category
        content
        createdAt
        comments
      }
    }
  }
}

或者,在筛选的同时应用一个gt(范围)查询

{
  allPostDocuments(filter:{
        category:{term:"Python"},
        numViews:{gt:"700"}
    }) {
    edges {
      node {
        category
        title
        comments
        numViews
      }
    }
  }
}
实现的筛选查找

以下查找可用

  • 包含

  • ends_with(或endsWith用于驼峰式命名)

  • 排除

  • 存在

  • gt

  • gte

  • in

  • is_null(或驼峰式写法的isNull

  • lt

  • lte

  • prefix

  • range

  • starts_with(或驼峰式写法的startsWith

  • term

  • terms

  • wildcard

有关过滤器查找的更多信息,请参阅专用文档

排序

可能的选项是ASCDESC

query {
  allPostDocuments(
        filter:{category:{term:"Photography"}},
        ordering:{title:ASC}
    ) {
    edges {
      node {
        category
        title
        content
        numViews
        tags
      }
    }
  }
}
分页

支持firstlastbeforeafter参数。默认情况下,结果数量限制为100。

query {
  allPostDocuments(first:12) {
    pageInfo {
      startCursor
      endCursor
      hasNextPage
      hasPreviousPage
    }
    edges {
      cursor
      node {
        category
        title
        content
        numViews
      }
    }
  }
}
高亮显示

只需列出您想要高亮的字段。这仅在结合搜索时才有效。

query {
  allPostDocuments(
        search:{content:{value:"alice"}, title:{value:"alice"}},
        highlight:[category, content]
    ) {
    edges {
      node {
        title
        content
        highlight
      }
      cursor
    }
  }
}

路线图

路线图和开发计划。

本包是在django-elasticsearch-dsl-drf的基础上设计的,旨在提供类似的功能。

计划在即将推出的Beta版本中发布许多功能

  • 建议器后端。

  • 嵌套后端。

  • 地理空间后端。

  • 过滤器查找geo_bounding_box(或驼峰式写法的geoBoundingBox)。

  • 过滤器查找geo_distance(或驼峰式写法的geoDistance)。

  • 过滤器查找geo_polygon(或驼峰式写法的geoPolygon)。

  • 类似的后端。

请继续关注或联系我,如果您想帮忙。

测试

项目包含测试。

使用Docker进行测试

make docker-test

使用virtualenv或tox运行测试

默认情况下,测试针对Elasticsearch 7.x执行。

使用Docker运行Elasticsearch 7.x

docker-compose up elasticsearch

安装测试需求

pip install -r requirements/test.txt

要测试所有支持的Python版本,请输入

tox

要测试特定环境,请输入

tox -e py38-elastic7

要仅测试您的工作环境,请输入

./runtests.py

要运行您工作环境中的单个测试模块,请输入

./runtests.py src/graphene_elastic/tests/test_filter_backend.py

要运行在给定测试模块中的单个测试类,请输入

./runtests.py src/graphene_elastic/tests/test_filter_backend.py::FilterBackendElasticTestCase

调试

出于开发目的,您可以使用flask应用(易于调试)。标准的pdb也可以工作(import pdb; pdb.set_trace())。如果ipdb对您不起作用,请使用ptpdb

编写文档

请保持以下层次结构。

=====
title
=====

header
======

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

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

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

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

sub-sub-sub-sub-sub-header
**************************

许可证

GPL-2.0-only OR LGPL-2.1-or-later

支持

有关任何安全问题,请通过作者部分提供的电子邮件联系我。对于总体问题,请访问GitHub

作者

Artur Barseghyan <artur.barseghyan@gmail.com>

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。

源分布

graphene-elastic-0.8.1.tar.gz (83.5 kB 查看哈希值)

上传时间 源码

构建分发版

graphene_elastic-0.8.1-py2.py3-none-any.whl (116.3 kB 查看哈希值)

上传时间 Python 2 Python 3

支持者