在 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 的回答中的建议,并从我的索引文件中删除了所有下划线。
我使用 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 的回答中的建议,并从我的索引文件中删除了所有下划线。