mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-13 17:52:19 +03:00
generating queries from filters to resolve the data first in ES.
This commit is contained in:
parent
5b4d8144ee
commit
837d74f941
25
graphene_django/elasticsearch/filter/bridges.py
Normal file
25
graphene_django/elasticsearch/filter/bridges.py
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
class QuerysetBridge(object):
|
||||
"""Bridge to Queryset through ES query"""
|
||||
|
||||
def __init__(self, search):
|
||||
"""Taking as search, the ES search resolved by DjangoESFilterConnectionField"""
|
||||
self.search = search
|
||||
|
||||
def get_queryset(self):
|
||||
"""Returning self as Queryset to be the bridge"""
|
||||
return self
|
||||
|
||||
def apply_query(self, method, *args, **kwargs):
|
||||
"""Helper method to apply mutation to ES Query"""
|
||||
if hasattr(self.search, method):
|
||||
self.search = getattr(self.search, method)(*args, **kwargs)
|
||||
|
||||
def __len__(self):
|
||||
"""Bridget method to response the ES count as QS len"""
|
||||
return self.search.count()
|
||||
|
||||
def __getitem__(self, k):
|
||||
"""Applying slice to ES and generating a QS from that"""
|
||||
_slice = self.search.__getitem__(k)
|
||||
return _slice.to_queryset()
|
17
graphene_django/elasticsearch/filter/fields.py
Normal file
17
graphene_django/elasticsearch/filter/fields.py
Normal file
|
@ -0,0 +1,17 @@
|
|||
from graphene_django.elasticsearch.filter.bridges import QuerysetBridge
|
||||
from graphene_django.filter import DjangoFilterConnectionField
|
||||
from elasticsearch_dsl.query import Query
|
||||
|
||||
|
||||
class DjangoESFilterConnectionField(DjangoFilterConnectionField):
|
||||
"""A Field to replace DjangoFilterConnectionField manager by QuerysetBridge"""
|
||||
|
||||
def get_manager(self):
|
||||
"""Retuning a QuerysetBridge to replace the direct use over the QS"""
|
||||
return QuerysetBridge(search=self.filterset_class._meta.index.search())
|
||||
|
||||
def merge_querysets(cls, default_queryset, queryset):
|
||||
"""Merge ES queries"""
|
||||
if isinstance(default_queryset, Query):
|
||||
return default_queryset & queryset
|
||||
return default_queryset.query(queryset)
|
|
@ -39,6 +39,30 @@ class StringFilterES(object): # pylint: disable=R0902
|
|||
for variant in self.variants:
|
||||
variant_name = self.field_name if variant in ["default", self.default_expr] \
|
||||
else "%s_%s" % (self.field_name, variant)
|
||||
fields[variant_name] = self.argument
|
||||
fields[variant_name] = self
|
||||
|
||||
return fields
|
||||
|
||||
def get_q(self, arguments):
|
||||
"""
|
||||
:param arguments: parameters of the query.
|
||||
:return: Returns a elasticsearch_dsl.Q query object.
|
||||
"""
|
||||
queries = []
|
||||
|
||||
for argument, value in arguments.iteritems():
|
||||
if argument in self.fields:
|
||||
|
||||
if argument == self.field_name:
|
||||
suffix_expr = self.default_expr or 'default'
|
||||
else:
|
||||
argument_split = argument.split("_")
|
||||
suffix_expr = argument_split[len(argument_split) - 1]
|
||||
|
||||
if suffix_expr in self.variants:
|
||||
query = self.variants.get(suffix_expr, None)
|
||||
|
||||
if query:
|
||||
queries.extend([query(self.field_name, value)])
|
||||
|
||||
return Q("bool", must=queries[0]) if len(queries) == 1 else Q("bool", must={"bool": {"should": queries}})
|
||||
|
|
|
@ -1,11 +1,24 @@
|
|||
"""Fields"""
|
||||
from collections import OrderedDict
|
||||
|
||||
from elasticsearch_dsl import Q
|
||||
from django.utils import six
|
||||
from django_filters.filterset import BaseFilterSet
|
||||
|
||||
from .filters import StringFilterES
|
||||
|
||||
|
||||
class FilterSetESOptions(object):
|
||||
"""Basic FilterSetES options to Metadata"""
|
||||
def __init__(self, options=None):
|
||||
"""
|
||||
The field option is combined with the index to automatically generate
|
||||
filters.
|
||||
"""
|
||||
self.index = getattr(options, 'index', None)
|
||||
self.model = self.index._doc_type.model if self.index else None
|
||||
|
||||
|
||||
class FilterSetESMetaclass(type):
|
||||
"""Captures the meta class of the filterSet class."""
|
||||
|
||||
|
@ -23,6 +36,7 @@ class FilterSetESMetaclass(type):
|
|||
base_filters.update(filter_field.fields)
|
||||
new_class.base_filters = base_filters
|
||||
|
||||
new_class._meta = FilterSetESOptions(getattr(new_class, 'Meta', None))
|
||||
return new_class
|
||||
|
||||
@classmethod
|
||||
|
@ -52,4 +66,33 @@ class FilterSetESMetaclass(type):
|
|||
|
||||
class FilterSetES(six.with_metaclass(FilterSetESMetaclass, object)):
|
||||
"""FilterSet specific for ElasticSearch."""
|
||||
pass
|
||||
def __init__(self, data, queryset, request):
|
||||
"""
|
||||
Receiving params necessaries to resolved the data
|
||||
:param data: argument passed to query
|
||||
:param queryset: a ES queryset
|
||||
:param request: the context of request
|
||||
"""
|
||||
self.data = data
|
||||
self.es_query = queryset
|
||||
self.request = request
|
||||
|
||||
@property
|
||||
def qs(self):
|
||||
"""Returning ES queryset as QS"""
|
||||
query_base = self.generate_q()
|
||||
self.es_query.apply_query("query", query_base)
|
||||
self.es_query.apply_query("source", ["id"])
|
||||
return self.es_query
|
||||
|
||||
def generate_q(self):
|
||||
"""
|
||||
Generate a query for each filter.
|
||||
:return: Generates a super query with bool as root, and combines all sub-queries from each argument.
|
||||
"""
|
||||
query_base = Q("bool")
|
||||
for name, filter_es in six.iteritems(self.declared_filters):
|
||||
query_filter = filter_es.get_q(self.data)
|
||||
if query_filter is not None:
|
||||
query_base += query_filter
|
||||
return query_base
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import six
|
||||
from graphene import Argument
|
||||
from django_filters import Filter
|
||||
|
||||
from .filterset import custom_filterset_factory, setup_filterset
|
||||
|
||||
|
@ -14,11 +14,11 @@ def get_filtering_args_from_filterset(filterset_class, type):
|
|||
args = {}
|
||||
for name, filter_field in six.iteritems(filterset_class.base_filters):
|
||||
|
||||
if not isinstance(filter_field, Argument):
|
||||
if isinstance(filter_field, Filter):
|
||||
field_type = convert_form_field(filter_field.field).Argument()
|
||||
field_type.description = filter_field.label
|
||||
else:
|
||||
field_type = filter_field
|
||||
field_type = filter_field.argument
|
||||
|
||||
args[name] = field_type
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ except ImportError:
|
|||
|
||||
|
||||
try:
|
||||
import elasticsearch_dsl # noqa
|
||||
import django_elasticsearch_dsl # noqa
|
||||
|
||||
DJANGO_ELASTICSEARCH_DSL_INSTALLED = True
|
||||
except ImportError:
|
||||
|
|
Loading…
Reference in New Issue
Block a user