mirror of
https://github.com/graphql-python/graphene-django.git
synced 2025-07-13 17:52:19 +03:00
added processors for all type of es query
This commit is contained in:
parent
3698b4b370
commit
4e4387d674
|
@ -6,10 +6,6 @@ class QuerysetBridge(object):
|
|||
"""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):
|
||||
|
@ -23,3 +19,15 @@ class QuerysetBridge(object):
|
|||
"""Applying slice to ES and generating a QS from that"""
|
||||
_slice = self.search.__getitem__(k)
|
||||
return _slice.to_queryset()
|
||||
|
||||
|
||||
class ManagerBridge(object):
|
||||
"""Bridge to Queryset through ES query"""
|
||||
|
||||
def __init__(self, search_manager):
|
||||
"""Taking as search, the ES search resolved by DjangoESFilterConnectionField"""
|
||||
self.search_manager = search_manager
|
||||
|
||||
def get_queryset(self):
|
||||
"""Returning self as Queryset to be the bridge"""
|
||||
return QuerysetBridge(search=self.search_manager())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
from elasticsearch_dsl.query import Query
|
||||
|
||||
from graphene_django.elasticsearch.filter.bridges import QuerysetBridge
|
||||
from graphene_django.elasticsearch.filter.bridges import ManagerBridge
|
||||
from graphene_django.filter import DjangoFilterConnectionField
|
||||
|
||||
|
||||
|
@ -19,11 +19,14 @@ class DjangoESFilterConnectionField(DjangoFilterConnectionField):
|
|||
filterset_class = kwargs.get('filterset_class', None)
|
||||
if filterset_class is None:
|
||||
raise ValueError('You should provide a FilterSetES as filterset_class argument.')
|
||||
|
||||
super(DjangoESFilterConnectionField, self).__init__(object_type, *args, **kwargs)
|
||||
|
||||
self.manager = ManagerBridge(search_manager=self.filterset_class._meta.index.search)
|
||||
|
||||
def get_manager(self):
|
||||
"""Returning a QuerysetBridge to replace the direct use over the QS"""
|
||||
return QuerysetBridge(search=self.filterset_class._meta.index.search())
|
||||
"""Returning a ManagerBridge to replace the direct use over the Model manager"""
|
||||
return self.manager
|
||||
|
||||
def merge_querysets(cls, default_queryset, queryset):
|
||||
"""Merge ES queries"""
|
||||
|
|
|
@ -1,58 +1,47 @@
|
|||
"""Filters to ElasticSearch"""
|
||||
from collections import OrderedDict
|
||||
|
||||
import six
|
||||
from elasticsearch_dsl import Q
|
||||
from graphene import String
|
||||
from graphene import String, Boolean, Int
|
||||
from graphene_django.elasticsearch.filter.processors import PROCESSORS
|
||||
|
||||
|
||||
class StringFilterES(object): # pylint: disable=R0902
|
||||
"""String Fields specific to ElasticSearch."""
|
||||
class FilterES(object):
|
||||
"""Fields specific to ElasticSearch."""
|
||||
default_processor = 'term'
|
||||
default_argument = String()
|
||||
|
||||
default_expr = 'contain'
|
||||
variants = {
|
||||
"contain": lambda name, value: Q('match',
|
||||
**{name: {
|
||||
"query": value,
|
||||
"fuzziness": "auto"
|
||||
}}),
|
||||
|
||||
"term": lambda name, value: Q('term', **{name: value}),
|
||||
}
|
||||
|
||||
def __init__(self, name=None, attr=None, lookup_expressions=None, default_expr=None):
|
||||
def __init__(self, field_name, field_name_es=None, lookup_expressions=None,
|
||||
default_processor=None, argument=None):
|
||||
"""
|
||||
:param name: Name of the field. This is the name that will be exported.
|
||||
:param attr: Path to the index attr that will be used as filter.
|
||||
"""
|
||||
assert name or attr, "At least the field name or the field attr should be passed"
|
||||
self.field_name = name or attr.replace('.', '_')
|
||||
self.default_expr = default_expr or self.default_expr
|
||||
self.field_name = field_name
|
||||
|
||||
if isinstance(field_name_es, list):
|
||||
self.field_name_es = field_name_es
|
||||
else:
|
||||
self.field_name_es = [field_name_es or field_name]
|
||||
|
||||
self.default_filter_processor = default_processor or self.default_processor
|
||||
|
||||
self.lookup_expressions = lookup_expressions
|
||||
self.argument = String()
|
||||
self.fields = self.generate_fields()
|
||||
|
||||
def generate_fields(self):
|
||||
"""
|
||||
All FilterSet objects should specify its fields for the introspection.
|
||||
|
||||
:return: A mapping of field to Filter type of field with all the suffix
|
||||
expressions combinations.
|
||||
"""
|
||||
fields = OrderedDict()
|
||||
self.processor = None
|
||||
if self.lookup_expressions:
|
||||
|
||||
for variant in self.lookup_expressions:
|
||||
if 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
|
||||
if variant in PROCESSORS:
|
||||
self.processor = self.build_processor(variant)
|
||||
else:
|
||||
raise ValueError('We do not have processor: %s.' % variant)
|
||||
|
||||
else:
|
||||
variant_name = self.field_name
|
||||
fields[variant_name] = self
|
||||
self.processor = self.build_processor(self.default_processor)
|
||||
|
||||
return fields
|
||||
self.fields = self.processor.generate_field()
|
||||
self.argument = argument or self.default_argument
|
||||
|
||||
def build_processor(self, variant):
|
||||
processor_class = PROCESSORS[variant]
|
||||
return processor_class(self, self.processor)
|
||||
|
||||
def generate_es_query(self, arguments):
|
||||
"""
|
||||
|
@ -60,24 +49,7 @@ class StringFilterES(object): # pylint: disable=R0902
|
|||
:param arguments: parameters of the query.
|
||||
:return: Returns a elasticsearch_dsl.Q query object.
|
||||
"""
|
||||
queries = []
|
||||
|
||||
for argument, value in six.iteritems(arguments):
|
||||
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}})
|
||||
return self.processor.generate_es_query(arguments)
|
||||
|
||||
def Argument(self):
|
||||
"""
|
||||
|
@ -85,3 +57,18 @@ class StringFilterES(object): # pylint: disable=R0902
|
|||
:return: A Argument type
|
||||
"""
|
||||
return self.argument.Argument()
|
||||
|
||||
|
||||
class StringFilterES(FilterES):
|
||||
"""String Fields specific to ElasticSearch."""
|
||||
default_processor = 'contains'
|
||||
|
||||
|
||||
class BoolFilterES(FilterES):
|
||||
"""Boolean filter to ES"""
|
||||
default_argument = Boolean()
|
||||
|
||||
|
||||
class NumberFilterES(FilterES):
|
||||
"""Filter to an numeric value to ES"""
|
||||
default_argument = Int()
|
||||
|
|
|
@ -2,19 +2,40 @@
|
|||
import copy
|
||||
from collections import OrderedDict
|
||||
from elasticsearch_dsl import Q
|
||||
from graphene import Enum, InputObjectType, Field
|
||||
from django_elasticsearch_dsl import StringField, TextField
|
||||
from graphene import Enum, InputObjectType, Field, Int, Float
|
||||
from django_elasticsearch_dsl import StringField, TextField, BooleanField, IntegerField, FloatField, LongField, \
|
||||
ShortField, DoubleField, DateField, KeywordField
|
||||
from django.utils import six
|
||||
|
||||
from django_filters.utils import try_dbfield
|
||||
from django_filters.filterset import BaseFilterSet
|
||||
|
||||
from .filters import StringFilterES
|
||||
from .filters import StringFilterES, FilterES, BoolFilterES, NumberFilterES
|
||||
|
||||
# Basic conversion from ES fields to FilterES fields
|
||||
FILTER_FOR_ESFIELD_DEFAULTS = {
|
||||
StringField: {'filter_class': StringFilterES},
|
||||
TextField: {'filter_class': StringFilterES},
|
||||
BooleanField: {'filter_class': BoolFilterES},
|
||||
IntegerField: {'filter_class': NumberFilterES},
|
||||
FloatField: {'filter_class': NumberFilterES,
|
||||
'extra': {
|
||||
'argument': Int()
|
||||
}},
|
||||
LongField: {'filter_class': NumberFilterES,
|
||||
'extra': {
|
||||
'argument': Int()
|
||||
}},
|
||||
ShortField: {'filter_class': NumberFilterES,
|
||||
'extra': {
|
||||
'argument': Int()
|
||||
}},
|
||||
DoubleField: {'filter_class': NumberFilterES,
|
||||
'extra': {
|
||||
'argument': Float()
|
||||
}},
|
||||
DateField: {'filter_class': StringFilterES},
|
||||
KeywordField: {'filter_class': StringFilterES},
|
||||
}
|
||||
|
||||
|
||||
|
@ -54,9 +75,12 @@ class FilterSetESOptions(object):
|
|||
class Meta:
|
||||
index = UserIndex
|
||||
includes = {
|
||||
'username': ['term']
|
||||
'last_login': ['lte', 'gte]
|
||||
}
|
||||
'username': {
|
||||
'field_name': 'graphene_field',
|
||||
'field_name_es': 'elasticsearch_field',
|
||||
'lookup_expressions': ['term', 'contains']
|
||||
}
|
||||
}
|
||||
|
||||
The list syntax will create an filter with a behavior by default,
|
||||
for each field included in includes. The dictionary syntax will
|
||||
|
@ -68,11 +92,12 @@ class FilterSetESOptions(object):
|
|||
|
||||
Example:
|
||||
class UserFilter(FilterSetES):
|
||||
username = StringFieldES('username', core_type='text', expr=['partial'])
|
||||
username = StringFieldES(field_name='username', lookup_expressions=['contains'])
|
||||
class Meta:
|
||||
index = UserIndex
|
||||
includes = {
|
||||
'username': ['term', 'word']
|
||||
'username': {
|
||||
'lookup_expressions': ['term', 'contains']
|
||||
}
|
||||
|
||||
A query with username as a parameter, will match those words with the
|
||||
|
@ -127,7 +152,7 @@ class FilterSetESMetaclass(type):
|
|||
|
||||
def __new__(mcs, name, bases, attrs):
|
||||
"""Get filters declared explicitly in the class"""
|
||||
|
||||
# get declared as field
|
||||
declared_filters = mcs.get_declared_filters(bases, attrs)
|
||||
attrs['declared_filters'] = declared_filters
|
||||
|
||||
|
@ -135,16 +160,20 @@ class FilterSetESMetaclass(type):
|
|||
|
||||
if issubclass(new_class, BaseFilterSet):
|
||||
new_class._meta = FilterSetESOptions(getattr(new_class, 'Meta', None))
|
||||
base_filters = OrderedDict()
|
||||
|
||||
# get declared as meta
|
||||
meta_filters = mcs.get_meta_filters(new_class._meta)
|
||||
|
||||
declared_filters.update(meta_filters)
|
||||
new_class.filters_es = declared_filters
|
||||
|
||||
# recollecting registered graphene fields
|
||||
base_filters = OrderedDict()
|
||||
for name, filter_field in six.iteritems(declared_filters):
|
||||
base_filters.update(filter_field.fields)
|
||||
|
||||
meta_filters = mcs.get_meta_filters(new_class._meta)
|
||||
base_filters.update(meta_filters)
|
||||
|
||||
# adding sort field
|
||||
sort_fields = {}
|
||||
|
||||
if new_class._meta.order_by is not None:
|
||||
sort_fields = mcs.generate_sort_field(new_class._meta.order_by)
|
||||
sort_type = mcs.create_sort_enum(name, sort_fields)
|
||||
|
@ -166,9 +195,9 @@ class FilterSetESMetaclass(type):
|
|||
|
||||
# List of filters declared in the class as static fields.
|
||||
filters = [
|
||||
(filter_name, attrs.pop(filter_name))
|
||||
(obj.field_name, attrs.pop(filter_name))
|
||||
for filter_name, obj in list(attrs.items())
|
||||
if isinstance(obj, StringFilterES)
|
||||
if isinstance(obj, FilterES)
|
||||
]
|
||||
|
||||
# Merge declared filters from base classes
|
||||
|
@ -191,7 +220,7 @@ class FilterSetESMetaclass(type):
|
|||
for name, index_field, data in index_fields:
|
||||
|
||||
filter_class = mcs.get_filter_exp(name, index_field, data)
|
||||
meta_filters.update(filter_class.fields)
|
||||
meta_filters.update({name: filter_class})
|
||||
|
||||
return meta_filters
|
||||
|
||||
|
@ -229,7 +258,6 @@ class FilterSetESMetaclass(type):
|
|||
# This inner field is not filterable
|
||||
continue
|
||||
inner_data = data[inner_name] if data else None
|
||||
|
||||
index_fields.append(mcs.get_filter_exp(inner_name, inner_field, inner_data, root=name))
|
||||
|
||||
return index_fields
|
||||
|
@ -245,27 +273,23 @@ class FilterSetESMetaclass(type):
|
|||
|
||||
# Get lookup_expr from configuration
|
||||
if data and 'lookup_expressions' in data:
|
||||
if 'lookup_expressions' in kwargs:
|
||||
kwargs['lookup_expressions'] = set(kwargs['lookup_expressions'])\
|
||||
.intersection(set(data['lookup_expressions']))
|
||||
else:
|
||||
kwargs['lookup_expressions'] = set(data['lookup_expressions'])
|
||||
kwargs['lookup_expressions'] = set(data['lookup_expressions'])
|
||||
elif 'lookup_expressions' in kwargs:
|
||||
kwargs['lookup_expressions'] = set(kwargs['lookup_expressions'])
|
||||
|
||||
kwargs['name'], kwargs['attr'] = mcs.get_name(name, root, data)
|
||||
kwargs['field_name'], kwargs['field_name_es'] = mcs.get_name(name, root, data)
|
||||
return filter_class(**kwargs)
|
||||
|
||||
@staticmethod
|
||||
def get_name(name, root, data):
|
||||
"""Get names of the field and the path to resolve it"""
|
||||
field_name = data.get('name', None) if data else None
|
||||
attr = data.get('attr', None) if data else None
|
||||
field_name = data.get('field_name', None) if data else None
|
||||
field_name_es = data.get('field_name_es', None) if data else None
|
||||
if not field_name:
|
||||
field_name = '{root}_{name}'.format(root=root, name=name) if root else name
|
||||
if not attr:
|
||||
attr = '{root}.{name}'.format(root=root, name=name) if root else name
|
||||
return field_name, attr
|
||||
if not field_name_es:
|
||||
field_name_es = '{root}.{name}'.format(root=root, name=name) if root else name
|
||||
return field_name, field_name_es
|
||||
|
||||
@staticmethod
|
||||
def create_sort_enum(name, sort_fields):
|
||||
|
@ -347,12 +371,11 @@ class FilterSetES(six.with_metaclass(FilterSetESMetaclass, object)):
|
|||
# if the query have data
|
||||
if len(self.data):
|
||||
# for each field passed to the query
|
||||
for name in self.data:
|
||||
filter_es = self.base_filters.get(name)
|
||||
for name, filter in six.iteritems(self.filters_es):
|
||||
# If a target filter is en FilterEs
|
||||
if isinstance(filter_es, StringFilterES):
|
||||
if isinstance(filter, FilterES):
|
||||
# It is generated a query or response None if the filter don't have data
|
||||
query_filter = filter_es.generate_es_query(self.data)
|
||||
query_filter = filter.generate_es_query(self.data)
|
||||
|
||||
if query_filter is not None:
|
||||
query_base += query_filter
|
||||
|
|
167
graphene_django/elasticsearch/filter/processors.py
Normal file
167
graphene_django/elasticsearch/filter/processors.py
Normal file
|
@ -0,0 +1,167 @@
|
|||
from collections import OrderedDict
|
||||
|
||||
from elasticsearch_dsl import Q
|
||||
from graphene import List
|
||||
|
||||
|
||||
class Processor(object):
|
||||
suffix_expr = 'term'
|
||||
|
||||
def __init__(self, filter_es, parent_processor=None):
|
||||
"""
|
||||
Abstract processor to generate graphene field and ES query to lookups
|
||||
:type filter_es: graphene_django.elasticsearch.filter.filterset.FilterES
|
||||
:type parent_processor: graphene_django.elasticsearch.filter.filterset.Processor
|
||||
"""
|
||||
self.filter_es = filter_es
|
||||
self.parent_processor = parent_processor
|
||||
self.variant_name = self._get_variant_name()
|
||||
|
||||
def generate_field(self):
|
||||
"""Field Decorator"""
|
||||
self_field = self._build_field()
|
||||
|
||||
if self.parent_processor is not None:
|
||||
parent_fields = self.parent_processor.generate_field()
|
||||
parent_fields.update(self_field)
|
||||
return parent_fields
|
||||
|
||||
else:
|
||||
return self_field
|
||||
|
||||
def get_type(self):
|
||||
return self.filter_es.argument
|
||||
|
||||
def generate_es_query(self, data):
|
||||
|
||||
if self.variant_name in data:
|
||||
value = data.get(self.variant_name)
|
||||
self_query = self._build_query(value)
|
||||
else:
|
||||
self_query = Q("bool")
|
||||
|
||||
if self.parent_processor is not None:
|
||||
parent_query = self.parent_processor.generate_es_query(data)
|
||||
parent_query += self_query
|
||||
return parent_query
|
||||
|
||||
else:
|
||||
return self_query
|
||||
|
||||
def _build_field(self):
|
||||
variant_name = self.variant_name
|
||||
|
||||
return OrderedDict({variant_name: self.filter_es})
|
||||
|
||||
def _get_variant_name(self):
|
||||
if self.suffix_expr == self.filter_es.default_filter_processor:
|
||||
variant_name = self.filter_es.field_name
|
||||
|
||||
else:
|
||||
variant_name = "%s_%s" % (self.filter_es.field_name, self.suffix_expr)
|
||||
|
||||
return variant_name
|
||||
|
||||
def _build_query(self, value):
|
||||
result = len(self.filter_es.field_name_es)
|
||||
|
||||
if result > 1:
|
||||
queries = [self._get_query(name, value) for name in self.filter_es.field_name_es]
|
||||
return Q("bool", must={"bool": {"should": queries}})
|
||||
|
||||
return Q("bool", must=self._get_query(self.filter_es.field_name_es[0], value))
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('term', **{name: value})
|
||||
|
||||
|
||||
class TermProcessor(Processor):
|
||||
pass
|
||||
|
||||
|
||||
class ContainsProcessor(Processor):
|
||||
suffix_expr = 'contains'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('match',
|
||||
**{name: {
|
||||
"query": value,
|
||||
"fuzziness": "auto"
|
||||
}})
|
||||
|
||||
|
||||
class RegexProcessor(Processor):
|
||||
suffix_expr = 'regex'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('wildcard', **{name: value})
|
||||
|
||||
|
||||
class PhraseProcessor(Processor):
|
||||
suffix_expr = 'phrase'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('match_phrase',
|
||||
**{name: {
|
||||
"query": value
|
||||
}})
|
||||
|
||||
|
||||
class PrefixProcessor(Processor):
|
||||
suffix_expr = 'prefix'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('match_phrase_prefix',
|
||||
**{name: {
|
||||
"query": value
|
||||
}})
|
||||
|
||||
|
||||
class InProcessor(Processor):
|
||||
suffix_expr = 'in'
|
||||
|
||||
def get_type(self):
|
||||
return List(self.filter_es.argument.Argument().type)
|
||||
|
||||
|
||||
class ExitsProcessor(Processor):
|
||||
suffix_expr = 'exits'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q('bool', **{
|
||||
'must' if value else 'must_not': {'exists': {'field': name}}
|
||||
})
|
||||
|
||||
|
||||
class LteProcessor(Processor):
|
||||
suffix_expr = 'lte'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q("bool", must={'range': {name: {'lte': value}}})
|
||||
|
||||
|
||||
class GteProcessor(Processor):
|
||||
suffix_expr = 'gte'
|
||||
|
||||
@staticmethod
|
||||
def _get_query(name, value):
|
||||
return Q("bool", must={'range': {name: {'gte': value}}})
|
||||
|
||||
|
||||
PROCESSORS = {
|
||||
"contains": ContainsProcessor,
|
||||
"term": TermProcessor,
|
||||
"regex": RegexProcessor,
|
||||
"phrase": PhraseProcessor,
|
||||
"prefix": PrefixProcessor,
|
||||
"in": InProcessor,
|
||||
"lte": LteProcessor,
|
||||
"gte": GteProcessor,
|
||||
}
|
|
@ -29,7 +29,7 @@ class ArticleFilterESAsField(FilterSetES):
|
|||
includes = []
|
||||
order_by = ['id']
|
||||
|
||||
headline = filters.StringFilterES(attr='headline', lookup_expressions=['term', 'contain'])
|
||||
headline = filters.StringFilterES(field_name='headline', lookup_expressions=['term', 'contains'])
|
||||
|
||||
|
||||
class ArticleFilterESInMeta(FilterSetES):
|
||||
|
@ -47,11 +47,25 @@ class ArticleFilterESInMetaDict(FilterSetES):
|
|||
index = ArticleDocument
|
||||
includes = {
|
||||
'headline': {
|
||||
'lookup_expressions': ['term', 'contain']
|
||||
'lookup_expressions': ['term', 'contains']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ArticleFilterMultiField(FilterSetES):
|
||||
"""Article Filter for ES"""
|
||||
class Meta(object):
|
||||
"""Metaclass data"""
|
||||
index = ArticleDocument
|
||||
includes = []
|
||||
|
||||
headline = filters.StringFilterES(
|
||||
field_name='contain',
|
||||
field_name_es=['headline', 'lang'],
|
||||
lookup_expressions=['contains']
|
||||
)
|
||||
|
||||
|
||||
class ESFilterQuery(ObjectType):
|
||||
"""A query for ES fields"""
|
||||
articles_as_field = DjangoESFilterConnectionField(
|
||||
|
@ -63,3 +77,6 @@ class ESFilterQuery(ObjectType):
|
|||
articles_in_meta_dict = DjangoESFilterConnectionField(
|
||||
ArticleNode, filterset_class=ArticleFilterESInMetaDict
|
||||
)
|
||||
articles_in_multi_field = DjangoESFilterConnectionField(
|
||||
ArticleNode, filterset_class=ArticleFilterMultiField
|
||||
)
|
||||
|
|
|
@ -78,11 +78,19 @@ def filter_generation(field, query_str, expected_arguments, method_to_mock="quer
|
|||
assert result.data[field]["edges"][1]["node"]["headline"] == "a2"
|
||||
|
||||
|
||||
def test_filter_as_field():
|
||||
def test_filter_string():
|
||||
filter_generation(
|
||||
"articlesAsField",
|
||||
"headline: \"A text\"",
|
||||
filters.StringFilterES(attr='headline').generate_es_query({"headline": "A text"}),
|
||||
filters.StringFilterES(field_name='headline').generate_es_query({"headline": "A text"}),
|
||||
)
|
||||
|
||||
|
||||
def test_filter_string_date():
|
||||
filter_generation(
|
||||
"articlesAsField",
|
||||
"headline: \"A text\"",
|
||||
filters.StringFilterES(field_name='headline').generate_es_query({"headline": "A text"}),
|
||||
)
|
||||
|
||||
|
||||
|
@ -99,7 +107,7 @@ def test_filter_in_meta():
|
|||
filter_generation(
|
||||
"articlesInMeta",
|
||||
"headline: \"A text\"",
|
||||
filters.StringFilterES(attr='headline').generate_es_query({"headline": "A text"}),
|
||||
filters.StringFilterES(field_name='headline').generate_es_query({"headline": "A text"}),
|
||||
)
|
||||
|
||||
|
||||
|
@ -107,5 +115,16 @@ def test_filter_in_meta_dict():
|
|||
filter_generation(
|
||||
"articlesInMetaDict",
|
||||
"headline: \"A text\"",
|
||||
filters.StringFilterES(attr='headline').generate_es_query({"headline": "A text"}),
|
||||
filters.StringFilterES(field_name='headline').generate_es_query({"headline": "A text"}),
|
||||
)
|
||||
|
||||
|
||||
def test_filter_in_multi_field():
|
||||
filter_generation(
|
||||
"articlesInMultiField",
|
||||
"contain: \"A text\"",
|
||||
filters.StringFilterES(
|
||||
field_name='contain',
|
||||
field_name_es=['headline', 'lang'],
|
||||
).generate_es_query({"contain": "A text"}),
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue
Block a user