首页 > 解决方案 > Flask 应用程序中的 ElasticSeach RequestError:查询仅适用于两个模型之一(search_phase_execution_exception,未能创建查询)

问题描述

当我尝试使用 ElasticSearch 查询一个索引而不是另一个索引时,我不断收到此错误:

elasticsearch.exceptions.RequestError: RequestError(400, 'search_phase_execution_exception', 'failed to 
create query: {\n  "multi_match" : {\n    "query" : "bob",\n    "fields" : [\n      
"additional_comments^1.0",\n      "age^1.0",\n      "first_name^1.0",\n      "gender^1.0",\n      
"needs^1.0",\n      "program_number^1.0",\n      "read^1.0",\n      "sheet_size^1.0",\n      
"shoe_size_category^1.0",\n      "shoe_sock_size^1.0",\n      "shoe_type^1.0",\n      
"time_chosen^1.0",\n      "wants^1.0",\n      "wear^1.0"\n    ],\n    "type" : "best_fields",\n    
"operator" : "OR",\n    "slop" : 0,\n    "fuzziness" : "50",\n    "prefix_length" : 0,\n    
"max_expansions" : 50,\n    "zero_terms_query" : "NONE",\n    "auto_generate_synonyms_phrase_query" : 
true,\n    "fuzzy_transpositions" : true,\n    "boost" : 1.0\n  }\n}')

我有以下 ES 查询:

def query_object(index, fields, query, page, per_page, fuzziness=0):
    print(index)
    print(query)
    print(fields)
    search = current_app.elasticsearch.search(
        index=index,
        body={'query': {'multi_match': {'query': query, 'fields': fields, 'fuzziness': fuzziness}},
              '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 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

这是用户类:


class User(db.Model, UserMixin, SearchableMixin):
    __searchable__ = ['email', 'phone', 'street_address',
                      'city', 'state', 'zip_code', 'last_reminded']
    id = db.Column(db.Integer, primary_key=True)
    email = db.Column(db.String(100), unique=True)
    phone = db.Column(db.String(20), nullable=True, default=None)
    password = db.Column(db.String(200))
    street_address = db.Column(db.String(100))
    city = db.Column(db.String(100))
    state = db.Column(db.String(52))
    zip_code = db.Column(db.String(10))
    last_reminded = db.Column(db.String(40), default=datetime.utcnow().strftime(
        "%A, %B %e{}, %Y".format(suffix(datetime.today().day))))
    admin = db.Column(db.Boolean, default=False)
    wish_lists = db.relationship('WishList', backref='sponsor', lazy='dynamic')
    drives = db.relationship(
        'HolidayCheerDrive', secondary=years, lazy='dynamic')

这是 WishList 类:

class WishList(db.Model, SearchableMixin):
    __searchable__ = ['program_number', 'first_name', 'age', 'gender', 'wants', 'needs', 'wear',
    'read', 'shoe_sock_size', 'shoe_size_category', 'shoe_type', 'sheet_size', 'additional_comments', 'time_chosen',
                      ]
    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), nullable=True, default=None)
    sheet_size = db.Column(db.String(20))
    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)

我尝试删除time_chosen可搜索的术语,因为它尚未定义,但这没有帮助。我也尝试只搜索一个字段,但也没有用。当我搜索用户时,它不会返回任何错误。据我所知,python 路由似乎没有任何错误,它们是:

@bp.route('/manage_users', methods=['GET', 'POST'])
#@admin_only
def manage_users():
    form = SearchUsersForm()
    page = request.args.get('page', 1, type=int)
    if not form.validate():
        all_users = User.query.all()
        total = len(all_users)
        start = ((page-1) * current_app.config['RESULTS_PER_PAGE'])
        end = (start + current_app.config['RESULTS_PER_PAGE'])
        if end >= total:
            display_users = all_users[start:]
        else:
            display_users = all_users[start:end]
    else:
        if form.fields.data == 'default':
            fields = [field for field in User.__searchable__]
        elif form.fields.data == 'address':
            fields = ['street_address', 'city', 'state', 'zip_code']
        else:
            fields = [form.fields.data]

        fuzzy = True
        if fuzzy:
            fuzziness = 50
        else:
            fuzziness = 0

        display_users, total = User.search_object(fields, form.q.data, page,
                                                  current_app.config['RESULTS_PER_PAGE'], fuzziness=fuzziness)

    q = form.q.data if form.q.data else None
    next_url = url_for('admin.manage_users', q=q, page=page + 1) \
        if total > page * current_app.config['RESULTS_PER_PAGE'] else None
    prev_url = url_for('admin.manage_users', q=q, page=page - 1) \
        if page > 1 else None
    context = {
        'next_url': next_url,
        'prev_url': prev_url,
        'display_users': display_users,
        'form': form,
    }
    return render_template('/admin/manage_users.html', **context)


@bp.route('/manage_wish_lists', methods=['GET', 'POST'])
#@admin_only
def manage_wish_lists():
    form = SearchWishListsForm()
    page = request.args.get('page', 1, type=int)
    if not form.validate():
        all_wish_lists = WishList.query.all()
        total = len(all_wish_lists)
        start = ((page-1) * current_app.config['RESULTS_PER_PAGE'])
        end = (start + current_app.config['RESULTS_PER_PAGE'])
        if end >= total:
            display_lists = all_wish_lists[start:]
        else:
            display_lists = all_wish_lists[start:end]
    else:
        if form.fields.data == 'default':
            fields = [field for field in WishList.__searchable__]
        elif form.fields.data == 'wish_list_items':
            fields = ['wants', 'need', 'wear', 'read']
        elif form.fields.data == "shoes":
            fields = ['shoe_sock_size', 'shoe_size_category', 'shoe_type']
        else:
            fields = [form.fields.data]

        fuzzy = True
        if fuzzy:
            fuzziness = 50
        else:
            fuzziness = 0

        display_lists, total = WishList.search_object(fields, form.q.data, page,
                                                      current_app.config['RESULTS_PER_PAGE'], fuzziness=fuzziness)

    q = form.q.data if form.q.data else None
    next_url = url_for('admin.manage_wish_lists', q=q, page=page + 1) \
        if total > page * current_app.config['RESULTS_PER_PAGE'] else None
    prev_url = url_for('admin.manage_wish_lists', q=q, page=page - 1) \
        if page > 1 else None
    context = {
        'next_url': next_url,
        'prev_url': prev_url,
        'display_lists': display_lists,
        'form': form,
    }
    return render_template('/admin/manage_wish_lists.html', **context)

我不明白。我正在使用相同的查询查询一个非常相似的对象,并且所有内容(包括索引和查询)都正确打印到终端。

在我添加的搜索功能中:

    print(index)
    print(query)
    print(fields)
    import json
    body={'query': {'multi_match': {'query': query, 'fields': fields, 'fuzziness': fuzziness}},
              'from': (page - 1) * per_page, 'size': per_page}
    print(json.dumps(body))
    search = current_app.elasticsearch.search(
        index=index,
        body={'query': {'multi_match': {'query': query, 'fields': fields, 'fuzziness': fuzziness}},
              'from': (page - 1) * per_page, 'size': per_page}
    )
    print(json.dumps(search))

print这些语句的编辑输出

对于用户对象:

#INDEX NAME
user
#QUERY TERMS
bob
#SEARCH FIELDS
['email', 'phone', 'street_address', 'city', 'state', 'zip_code', 'last_reminded']
#BODY OF SEARCH
{"query": {"multi_match": {"query": "bob", "fields": ["email", "phone", "street_address", "city", "state", "zip_code", "last_reminded"], "fuzziness": 50}}, "from": 0, "size": 10}
#SEARCH JSON
{"took": 18, "timed_out": false, "_shards": {"total": 1, "successful": 1, "skipped": 0, "failed": 0}, "hits": {"total": {"value": 0, "relation": "eq"}, "max_score": null, "hits": []}}

对于 WishList 对象:

#INDEX NAME
wish_list
#QUERY TERMS
bob
#SEARCH FIELDS
['program_number', 'first_name', 'age', 'gender', 'wants', 'needs', 'wear', 'read', 'shoe_sock_size', 'shoe_size_category', 'shoe_type', 'sheet_size', 'additional_comments', 'time_chosen']
#BODY OF SEARCH
{"query": {"multi_match": {"query": "bob", "fields": ["program_number", "first_name", "age", "gender", "wants", "needs", "wear", "read", "shoe_sock_size", "shoe_size_category", "shoe_type", "sheet_size", "additional_comments", "time_chosen"], "fuzziness": 50}}, "from": 0, "size": 10}
#Cannot build query so search object never instantiates

唯一的区别是,User 对象不会给我一个错误并返回一个我可以在我的 HTML 页面上看到的搜索结果。查询时的 WishList 对象会引发我在上面粘贴的错误。

请让我知道...我的路线有问题吗?是否可以索引太多字段?additional_comments是文本列而不是字符串列;这有可能发挥作用吗?

我不知所措。非常感谢您的帮助。先感谢您!

编辑:我将输出添加到下面的 ElasticSearch 服务器终端,以防万一。从视觉上讲,这个 JSON 可能会更好地组织化?

at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37) ~[elasticsearch-7.6.1.jar:7.6.1]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) ~[?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) ~[?:?]
        at java.lang.Thread.run(Thread.java:830) ~[?:?]
[2020-03-23T09:34:43,437][DEBUG][o.e.a.s.TransportSearchAction] [LAPTOP-G8TTIC4C] All shards failed for phase: [query]
org.elasticsearch.index.query.QueryShardException: failed to create query: {
  "multi_match" : {
    "query" : "bob",
    "fields" : [
      "additional_comments^1.0",
      "age^1.0",
      "first_name^1.0",
      "gender^1.0",
      "needs^1.0",
      "program_number^1.0",
      "read^1.0",
      "sheet_size^1.0",
      "shoe_size_category^1.0",
      "shoe_sock_size^1.0",
      "shoe_type^1.0",
      "time_chosen^1.0",
      "wants^1.0",
      "wear^1.0"
    ],
    "type" : "best_fields",
    "operator" : "OR",
    "slop" : 0,
    "fuzziness" : "50",
    "prefix_length" : 0,
    "max_expansions" : 50,
    "zero_terms_query" : "NONE",
    "auto_generate_synonyms_phrase_query" : true,
    "fuzzy_transpositions" : true,
    "boost" : 1.0
  }
}
        at org.elasticsearch.index.query.QueryShardContext.toQuery(QueryShardContext.java:350) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.query.QueryShardContext.toQuery(QueryShardContext.java:333) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.search.SearchService.parseSource(SearchService.java:750) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.search.SearchService.createContext(SearchService.java:591) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.search.SearchService.createAndPutContext(SearchService.java:550) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.search.SearchService.executeQueryPhase(SearchService.java:351) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.search.SearchService.lambda$executeQueryPhase$1(SearchService.java:343) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.action.ActionListener.lambda$map$2(ActionListener.java:146) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.action.ActionListener$1.onResponse(ActionListener.java:63) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.action.ActionRunnable.lambda$supply$0(ActionRunnable.java:58) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.action.ActionRunnable$2.doRun(ActionRunnable.java:73) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.common.util.concurrent.TimedRunnable.doRun(TimedRunnable.java:44) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.common.util.concurrent.ThreadContext$ContextPreservingAbstractRunnable.doRun(ThreadContext.java:692) [elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.common.util.concurrent.AbstractRunnable.run(AbstractRunnable.java:37) [elasticsearch-7.6.1.jar:7.6.1]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at java.lang.Thread.run(Thread.java:830) [?:?]
Caused by: java.lang.IllegalArgumentException: Can only use fuzzy queries on keyword and text fields - not on [shoe_sock_size] which is of type [long]
        at org.elasticsearch.index.mapper.MappedFieldType.fuzzyQuery(MappedFieldType.java:356) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.search.MatchQuery$MatchQueryBuilder.lambda$newTermQuery$0(MatchQuery.java:569) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.search.MatchQuery$MatchQueryBuilder.newTermQuery(MatchQuery.java:579) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.search.MatchQuery.parse(MatchQuery.java:277) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.search.MultiMatchQuery.buildFieldQueries(MultiMatchQuery.java:108) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.search.MultiMatchQuery.parse(MultiMatchQuery.java:76) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.query.MultiMatchQueryBuilder.doToQuery(MultiMatchQueryBuilder.java:833) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.query.AbstractQueryBuilder.toQuery(AbstractQueryBuilder.java:99) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.query.QueryShardContext.lambda$toQuery$1(QueryShardContext.java:334) ~[elasticsearch-7.6.1.jar:7.6.1]
        at org.elasticsearch.index.query.QueryShardContext.toQuery(QueryShardContext.java:346) ~[elasticsearch-7.6.1.jar:7.6.1]
        ... 17 more

标签: pythonelasticsearchflaskflask-sqlalchemy

解决方案


我发现,当我将搜索字段的数据类型明确设置为“文本”时,在我不搜索类似数字的字段的所有情况下,查询都可以正常构建。以前,所有字段都没有正确构建。

我已将类中的索引类函数设置SearchableMixin为以下内容:

def add_to_index(index, model):
    payload = {}
    properties = {}
    for field in model.__searchable__:
        payload[field] = getattr(model, field)
        properties[field] = {'type': 'text'}
    payload['mappings'] = {'properties': properties}
    current_app.elasticsearch.index(index=index, id=model.id, body=payload)

我现在可以查询这两个类,但就像我说的那样,只是不在类似数字的字段内(program_number、、shoe_sock_sizeage都是强制转换为字符串的整数或浮点数)。我会研究或提出另一个问题来解决这个问题。

谢谢您的帮助!


推荐阅读