Как мне указать в запросе на много совпадение ElasticSearch, что я хочу, чтобы поля чисел c, хранящиеся в виде строк, возвращали совпадения со строками чисел c? - PullRequest
0 голосов
/ 26 марта 2020

Я пишу приложение Flask и использую эластичный поиск.

Здесь search.py:

from flask import current_app

def query_object(index, fields, query, page, per_page, fuzziness=0):
    search = current_app.elasticsearch.search(
        index=index,
        body={'query': {'multi_match': {'query': str(query), 'fields': fields, 'fuzziness': fuzziness, 'lenient': True}},
                'from': (page - 1) * per_page, 'size': per_page}
    )

    ids = [int(hit['_id']) for hit in search['hits']['hits']]
    return ids, search['hits']['total']['value']

Индексируется следующая модель:

class WishList(db.Model, SearchableMixin):
    __searchable__ = ['first_name', 'gender', 'wants', 'needs', 'wear',
    'read', 'shoe_size_category', 'shoe_type', 'sheet_size', 'additional_comments', 'time_chosen',
    'age', 'shoe_sock_size', 'program_number']

    id = db.Column(db.Integer, primary_key=True)
    program_number = db.Column(db.String(4))
    first_name = db.Column(db.String(20))
    age = db.Column(db.String(10))
    gender = db.Column(db.String(20))
    wants = db.Column(db.String(300))
    needs = db.Column(db.String(300))
    wear = db.Column(db.String(300))
    read = db.Column(db.String(300))
    pant_dress_size = db.Column(db.String(20), default='unspecified')
    shirt_blouse_size = db.Column(db.String(20), default='unspecified')
    jacket_sweater_size = db.Column(db.String(20), default='unspecified')
    shoe_sock_size = db.Column(db.String(20), default='unspecified')
    shoe_size_category = db.Column(db.String(20), default='unspecified')
    shoe_type = db.Column(db.String(50), default='unspecified')
    sheet_size = db.Column(db.String(20), default='unspecified')
    additional_comments = db.Column(db.Text(), nullable=True, default=None)
    time_chosen = db.Column(db.String(40), nullable=True, default=None)
    sponsor_id = db.Column(db.Integer, db.ForeignKey(
        'user.id'), nullable=True, default=None)
    drive_id = db.Column(db.Integer, db.ForeignKey(
        'holiday_cheer_drive.id'), nullable=False, default=None)

Эта модель доступна для поиска благодаря наследованию от класса SearchableMixin следующим образом:

class SearchableMixin(object):
    @classmethod
    def search_object(cls, fields, expression, page, per_page, fuzziness=0):
        ids, total = query_object(
            cls.__tablename__, fields, expression, page, per_page, fuzziness=fuzziness)
        if total == 0:
            return cls.query.filter_by(id=0), 0
        when = []
        for i in range(len(ids)):
            when.append((ids[i], i))
        return cls.query.filter(cls.id.in_(ids)).order_by(
            db.case(when, value=cls.id)), total

Когда я ищу в данный момент, все поля доступны для поиска и возвращают действительный результат, ЕСЛИ Я не ищу с номером c value.

Вот пример вывода для поиска, который работает, когда я говорю python напечатать значения на консоль:

Query: bob
Body of search:
{'from': 0,
 'query': {'multi_match': {'fields': ['first_name',
                                      'gender',
                                      'wants',
                                      'needs',
                                      'wear',
                                      'read',
                                      'shoe_size_category',
                                      'shoe_type',
                                      'sheet_size',
                                      'additional_comments',
                                      'time_chosen',
                                      'age',
                                      'shoe_sock_size',
                                      'program_number'],
                           'fuzziness': 0,
                           'lenient': True,
                           'query': 'bob'}},
 'size': 10}
Python elasticsearch object:
{'took': 27, 'timed_out': False, '_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}, 'hits': {'total': {'value': 2, 'relation': 'eq'}, 'max_score': 1.6916759, 'hits': [{'_index': 'wish_list', '_type': '_doc', '_id': '1', '_score': 1.6916759, '_source': {'first_name': 'bob', 'gender': 'male', 'wants': 'bike', 'needs': 'calculator', 'wear': 'hat', 'read': 'book', 'shoe_size_category': "men's", 'shoe_type': 'sneaker', 'sheet_size': 'unspecified', 'additional_comments': 'Likes cheese', 'time_chosen': None, 'age': '5', 'shoe_sock_size': '4', 'program_number': '215', 'mappings': {'properties': {'first_name': {'type': 'text'}, 'gender': {'type':
'text'}, 'wants': {'type': 'text'}, 'needs': {'type': 'text'}, 'wear': {'type': 'text'}, 'read': {'type': 'text'}, 'shoe_size_category': {'type': 'text'}, 'shoe_type': {'type': 'text'}, 'sheet_size': {'type': 'text'}, 'additional_comments': {'type': 'text'}, 'time_chosen': {'type': 'text'}, 'age': {'type': 'text'}, 'shoe_sock_size': {'type': 'text'}, 'program_number': {'type': 'text'}}}}}, {'_index': 'wish_list', '_type': '_doc', '_id': '9', '_score': 1.6916759, '_source': {'first_name': 'bob', 'gender': 'male', 'wants': 'bike', 'needs': 'calculator', 'wear': 'hat', 'read': 'book', 'shoe_size_category': "men's", 'shoe_type': 'sneaker', 'sheet_size': 'unspecified', 'additional_comments': 'Likes cheese', 'time_chosen': None, 'age': 5, 'shoe_sock_size': 4, 'program_number': 215, 'mappings': {'properties': {'first_name': {'type': 'text'}, 'gender': {'type': 'text'}, 'wants': {'type': 'text'}, 'needs': {'type': 'text'}, 'wear': {'type': 'text'}, 'read': {'type': 'text'}, 'shoe_size_category': {'type': 'text'}, 'shoe_type': {'type': 'text'}, 'sheet_size': {'type': 'text'}, 'additional_comments': {'type': 'text'}, 'time_chosen': {'type': 'text'}, 'age': {'type': 'text'}, 'shoe_sock_size': {'type': 'text'}, 'program_number': {'type': 'text'}}}}}]}}

И вот то же самое точно запрос для того же точного объекта, но с числовой c строкой:

Query: 215
Body of search:
{'from': 0,
 'query': {'multi_match': {'fields': ['first_name',
                                      'gender',
                                      'wants',
                                      'needs',
                                      'wear',
                                      'read',
                                      'shoe_size_category',
                                      'shoe_type',
                                      'sheet_size',
                                      'additional_comments',
                                      'time_chosen',
                                      'age',
                                      'shoe_sock_size',
                                      'program_number'],
                           'fuzziness': 0,
                           'lenient': True,
                           'query': '215'}},
 'size': 10}
Python elasticsearch object:
{'took': 18, 'timed_out': False, '_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}, 'hits': {'total': {'value': 0, 'relation': 'eq'}, 'max_score': None, 'hits': []}}

В функцию передается строка, и все данные сохраняются в виде строк, но, похоже, что-то вроде ошибки типа. Прежде чем я добавил в lenient: True, он выдавал ошибку о том, чтоasticsearch не может построить запрос.

Если я могу понять, как я это сделаю с REST API эластичного поиска, то я, вероятно, могу понять, как сделай это с python.

1 Ответ

1 голос
/ 26 марта 2020

Проблема возникает из-за использования параметра fuzziness для типа данных numeric и последующего использования lenient true, чтобы заставить его работать, как указано removes format-based errors, such as providing a text query value for a numeric field, are ignored. в this ссылка.

Ниже приведена ошибка, которая появляется при попытке использовать fuzziness для числовых c типов данных.

reason ":" Можно использовать только нечеткие запросы для ключевого слова и текстовые поля - не для [age], который имеет тип [integer] "

И когда вы добавляете "lenient" : true, то вышеупомянутая ошибка уходит, но не возвращает никакого документа.

Чтобы это сработало, просто удалите параметры fuzziness и lenient из вашего поискового запроса, и оно должно работать, поскольку Elasticsearch автоматически преобразует действительные string в numeric и наоборот, как описано в coerce article.

Рабочий пример, чтобы показать его с помощью REST API

Index def

{
    "mappings": {
        "properties": {
            "age" :{
                "type" : "integer"
            }
        }
    }
}

Пример индекса do c

{
  "age" : "25" --> note use of `""`, sending it as string
}

{
  "age" : 28 :- note sending numneric value
}

Поисковый запрос в строковом формате

{
    "query": {
        "bool": {
            "must": [
                {
                    "multi_match": {
                        "query": "28", --> note string format
                        "fields": [
                            "age" --> note you can add more fields
                        ]
                    }
                }
            ]
        }
    }
}

Результат поиска

"hits": [
      {
        "_index": "so_numberic",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "program_number": "123456789",
          "age": "28"
        }
      }
    ]

Поисковый запрос в числительном c формате * 104 1 * { "query": { "match" : { --> query on single field. "age" : { "query" : 28 --> note numeric format } } } } Результат

"hits": [
      {
        "_index": "so_numberic",
        "_type": "_doc",
        "_id": "1",
        "_score": 1.0,
        "_source": {
          "program_number": "123456789",
          "age": "28"
        }
      }
    ]

Отображение ваших fuzziness и lenient не дает результатов, как объяснено ранее.

Поисковый запрос

{
    "query": {
        "match": {
            "age": {
                "query": 28,
                "fuzziness": 2,
                "lenient": true
            }
        }
    }
}

Результат

{
    "took": 1,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "skipped": 0,
        "failed": 0
    },
    "hits": { --> note 0 results.
        "total": {
            "value": 0,
            "relation": "eq"
        },
        "max_score": null,
        "hits": []
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...