在 Django 中找不到不是单词的字符串 Haystick/Elasticsearch

Can't find strings that aren't words in Django Haystick/Elasticsearch

我使用 Django Haystack 和 Elasticsearch 作为实时航班地图服务的后端。

我的所有搜索索引都已正确设置,但是,我在 return 搜索非完整单词(例如航空呼号,其中一些采用N346IF,其他包括完整的单词,例如 Speedbird 500)。 N346IF 查询样式不会产生任何结果,而对于后一个示例,我可以轻松地获得 return 结果。

我的查询如下:

queryResults = SearchQuerySet().filter(content=q) # where q is the query in string format

(请注意,过去我使用了 AutoQuery 查询集,但文档列出这仅跟踪单词,所以我现在传递的是原始字符串)。

我使用搜索模板将搜索索引字段设置为 EdgeNgramField

我有一个具有以下索引设置的自定义后端(以及 snowball 分析器和 pattern 分析器):

ELASTICSEARCH_INDEX_SETTINGS = {
    'settings': {
        "analysis": {
            "analyzer": {
                "ngram_analyzer": {
                    "type": "custom",
                    "tokenizer": "lowercase",
                    "filter": ["haystack_ngram"]
                },
                "edgengram_analyzer": {
                    "type": "custom",
                    "tokenizer": "lowercase",
                    "filter": ["haystack_edgengram"]
                }
            },
            "tokenizer": {
                "haystack_ngram_tokenizer": {
                    "type": "nGram",
                    "min_gram": 4,
                    "max_gram": 15,
                },
                "haystack_edgengram_tokenizer": {
                    "type": "edgeNGram",
                    "min_gram": 4,
                    "max_gram": 15,
                    "side": "front"
                }
            },
            "filter": {
                "haystack_ngram": {
                    "type": "nGram",
                    "min_gram": 4,
                    "max_gram": 15
                },
                "haystack_edgengram": {
                    "type": "edgeNGram",
                    "min_gram": 4,
                    "max_gram": 15
                }
            }
        }
    }
}

ELASTICSEARCH_DEFAULT_ANALYZER = "pattern"

我的后端配置为:

class ConfigurableElasticBackend(ElasticsearchSearchBackend):

    def __init__(self, connection_alias, **connection_options):
        super(ConfigurableElasticBackend, self).__init__(
                                connection_alias, **connection_options)
        user_settings = getattr(settings, 'ELASTICSEARCH_INDEX_SETTINGS')
        if user_settings:
            setattr(self, 'DEFAULT_SETTINGS', user_settings)

class ConfigurableElasticBackend(ElasticsearchSearchBackend):

    DEFAULT_ANALYZER = "pattern"

    def __init__(self, connection_alias, **connection_options):
        super(ConfigurableElasticBackend, self).__init__(
                                connection_alias, **connection_options)

        user_settings = getattr(settings, 'ELASTICSEARCH_INDEX_SETTINGS')
        user_analyzer = getattr(settings, 'ELASTICSEARCH_DEFAULT_ANALYZER')

        if user_settings:
            setattr(self, 'DEFAULT_SETTINGS', user_settings)
        if user_analyzer:
            setattr(self, 'DEFAULT_ANALYZER', user_analyzer)

    def build_schema(self, fields):
        content_field_name, mapping = super(ConfigurableElasticBackend,
                                              self).build_schema(fields)

        for field_name, field_class in fields.items():
            field_mapping = mapping[field_class.index_fieldname]

            if field_mapping['type'] == 'string' and field_class.indexed:
                if not hasattr(field_class, 'facet_for') and not \
                                  field_class.field_type in('ngram', 'edge_ngram'):
                    field_mapping['analyzer'] = self.DEFAULT_ANALYZER
            mapping.update({field_class.index_fieldname: field_mapping})
        return (content_field_name, mapping)

class ConfigurableElasticSearchEngine(ElasticsearchSearchEngine):
    backend = ConfigurableElasticBackend

正确的设置应该是什么才能成功地为同时为 and/or N346IF 风格字符串的搜索模式生成结果?

感谢任何意见,如果这与另一个问题相似(找不到与之相关的任何内容),我们深表歉意。


编辑: solarissmoke 请求,此模型的架构:

class FlightIndex(indexes.SearchIndex, indexes.Indexable):
    text = indexes.EdgeNgramField(document=True, use_template=True)
    flight = indexes.CharField(model_attr='flightID')
    callsign = indexes.CharField(model_attr='callsign')
    displayName = indexes.CharField(model_attr='displayName')
    session = indexes.CharField(model_attr='session')

    def prepare_session(self, obj):
        return obj.session.serverId

    def get_model(self):
        return Flight

文本索引为:

flight___{{ object.callsign }}___{{ object.displayName }}

它没有完全解释您所看到的行为,但我认为问题在于您如何为数据编制索引 - 特别是 text 字段(当您过滤 content).

以您提供的示例数据为例,呼号 N133TC,航班名称 Shahrul Nizam。此数据的 text 文档变为:

flight___N133TC___Shahrul Nizam

您已将此字段设置为 EdgeNgramField(最少 4 个字符,最多 15 个字符)。以下是索引此文档时生成的 ngram(为简单起见,我忽略了小写过滤器):

flig
fligh
flight
flight_
flight___
flight___N
flight___N1
flight___N13
flight___N133
flight___N133T
flight___N133TC
Niza
Nizam

请注意分词器不会在下划线处拆分。现在,如果您搜索 N133TC,上述标记中的 none 将匹配。 (我无法解释为什么 Shahrul 有效……它不应该,除非我遗漏了什么,或者该字段的开头有空格)。

如果您将 text 文档更改为:

flight N133TC Shahrul Nizam

那么索引标记将是:

flig
flight
N133
N133T
N133TC
Shah
Shahr
Shahru
Shahrul
Niza
Nizam

现在,搜索 N133TC 应该匹配。

另请注意,您文档中的 flight___ 字符串会生成一大堆(很可能)无用的标记 - 除非这是故意的,否则您最好不要使用它。

解决我自己的问题 - 感谢 solarissmoke 的输入,因为它帮助我找到了导致此问题的原因。

我的回答基于 Greg Baker 对问题的回答 ElasticSearch: EdgeNgrams and Numbers

问题似乎与搜索文本中数值的使用有关(在我的例子中,是 N133TC 模式)。请注意,我首先使用的是 snowball 分析器,然后才切换到 pattern - none 这些工作。

我在 settings.py 中调整了我的分析器设置:

"edgengram_analyzer": {
    "type": "custom",
    "tokenizer": "standard",
    "filter": ["haystack_edgengram"]
}

因此将 tokenizer 值从原来使用的 lowercase 分析器更改为 standard

然后我将后端使用的默认分析器设置为 edgengram_analyzer(也在 settings.py 上):

ELASTICSEARCH_DEFAULT_ANALYZER = "edgengram_analyzer"

这很管用!它仍然像 EdgeNgram 字段一样工作,但也允许正确返回我的数值。

我也听从了 solarismoke 的回答中的建议,并从我的索引文件中删除了所有下划线。