【问题标题】:How do I tell an ElasticSearch multi-match query that I want numeric fields, stored as strings, to return matches with numeric strings?如何告诉 ElasticSearch 多重匹配查询我希望存储为字符串的数字字段返回与数字字符串的匹配项?
【发布时间】:2020-03-27 00:49:01
【问题描述】:

我正在编写一个 Flask 应用程序,我正在使用 elasticsearch。

这里是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

当我当前搜索它时,所有字段都是可搜索的并返回有效结果,除非我使用数字值进行搜索。

以下是当我告诉 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'}}}}}]}}

这是对同一个确切对象的相同确切查询,但使用的是数字字符串:

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 之前,它抛出了一个错误,说 elasticsearch 无法构建查询。

如果我能理解如何使用 elasticsearch REST API 来实现,那么我可能会弄清楚如何使用 python 来实现。

【问题讨论】:

  • 正如我所承诺的那样,它提供了我的答案,它甚至还有你需要在 python 中做的事情:-)

标签: python elasticsearch flask-sqlalchemy elasticsearch-query


【解决方案1】:

由于在numeric 数据类型上使用了fuzziness 参数,然后使用lenient true 使其工作,因为它在removes format-based errors, such as providing a text query value for a numeric field, are ignored. 链接中提到的removes format-based errors, such as providing a text query value for a numeric field, are ignored. 工作。

p>

以下是您在尝试对数字数据类型使用 fuzziness 时遇到的错误。

reason": "只能对关键字和文本字段使用模糊查询 - 不能 在 [age] 上,其类型为 [integer]"

当你添加"lenient" : true时,上面的错误就会消失,但不会返回任何文档。

要使其正常工作,只需从搜索查询中删除 fuzzinesslenient 参数,它应该可以正常工作,因为 Elasticsearch 会自动将有效的 string 转换为 numeric,反之亦然,如 coerce 文章中所述。

使用 REST API 展示它的工作示例

索引定义

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

索引示例文档

{
  "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"
        }
      }
    ]

数字格式的搜索查询

{
    "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"
        }
      }
    ]

如前所述,显示您的 fuzzinesslenient 不会带来任何结果。

搜索查询

{
    "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": []
    }
}

【讨论】:

  • 谢谢!做到了。我创建了两个搜索函数,一个用于纯数字搜索,一个用于其他所有搜索,我假设它是一个字符串。字符串 1 具有模糊性和宽大性,但数字没有。当我拿出宽大处理和模糊性时,搜索对字符串不起作用;只有数字,所以显然我无法将它们组合在一起......我不知道你是否知道如何做到这一点,哈哈
  • @PeterCharland,能否请您问一个详细的问题,在 cmets 中很难理解并提供答案,就像上次一样,当您提供问题链接时,请在此处发布,以便我稍后查看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-21
  • 2018-08-03
  • 1970-01-01
  • 2019-09-03
  • 1970-01-01
  • 2014-07-21
相关资源
最近更新 更多