Django搜索栈中Elasticsearch MLT搜索失效:如何修改min_term_freq和min_doc_freq?
Hey there! Let's sort out your More Like This (MLT) search issue in your Django project—since you're using Elasticsearch 2, django-haystack, and drf-haystack, here's exactly how to tweak those min_term_freq and min_doc_freq defaults, plus some extra tips to get MLT working smoothly:
You have two main ways to override these default values, depending on whether you want a global change or per-request control:
Option 1: Set Parameters Per Request (in your drf-haystack View)
If you want to adjust MLT parameters dynamically for specific API calls, modify the SearchQuerySet directly in your view. Here's how to do it:
from haystack.query import SearchQuerySet from drf_haystack.viewsets import HaystackViewSet from .serializers import YourSearchSerializer class YourSearchViewSet(HaystackViewSet): serializer_class = YourSearchSerializer index_models = [YourModel] # Your model linked to the Haystack index def get_queryset(self): sqs = super().get_queryset() # Check if the request is asking for MLT results if self.request.query_params.get('mlt') and self.request.query_params.get('doc_id'): target_doc = SearchQuerySet().get(id=self.request.query_params['doc_id']) # Override MLT parameters here sqs = sqs.more_like_this( target_doc, min_term_freq=1, # Set your desired minimum term frequency min_doc_freq=1 # Set your desired minimum document frequency # Add other MLT params here if needed, like max_query_terms=10 ) return sqs
Trigger MLT via an API request like GET /search/?mlt=true&doc_id=your-document-id.
Option 2: Global Override via Custom Elasticsearch Backend
For a project-wide default change, create a custom Elasticsearch backend that replaces the default MLT parameters:
First, create a haystack_backends.py file in your project directory:
from haystack.backends.elasticsearch_backend import ElasticsearchSearchBackend, ElasticsearchSearchEngine class CustomElasticsearchBackend(ElasticsearchSearchBackend): def __init__(self, connection_alias, **connection_options): super().__init__(connection_alias, **connection_options) # Override the default MLT parameters here self.default_mlt_params = { 'min_term_freq': 1, 'min_doc_freq': 1, # You can add other defaults here, e.g., max_query_terms=15, stop_words="_english_" } class CustomElasticsearchSearchEngine(ElasticsearchSearchEngine): backend = CustomElasticsearchBackend
Then update your settings.py to use this custom backend:
HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': 'your_project_name.haystack_backends.CustomElasticsearchSearchEngine', 'URL': 'http://localhost:9200/', 'INDEX_NAME': 'your_index_name', }, }
Double-Check Your Haystack Index: Ensure your
SearchIndexincludes all fields you want MLT to analyze. Use a template for thedocument=Truefield to combine multiple model fields:from haystack import indexes from .models import YourModel class YourModelIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) def get_model(self): return YourModelCreate a template file at
templates/search/indexes/your_app/yourmodel_text.txtthat lists the fields to include:{{ object.title }} {{ object.description }} {{ object.content }}Test MLT Directly in Elasticsearch: Rule out Haystack/drf-haystack issues by running an MLT query directly against Elasticsearch with
curl:curl -X GET "http://your-es-host:9200/your_index/_search" -d '{ "query": { "more_like_this": { "fields": ["text"], "like": {"_id": "your-document-id"}, "min_term_freq": 1, "min_doc_freq": 1 } } }'If this returns results, the problem is in your Django setup; if not, adjust parameters or check your index data.
Verify Elasticsearch Field Types: In Elasticsearch 2.x, fields used for MLT should be of type
string(notkeyword). Haystack usually handles this automatically, but if you have custom mappings, confirm they're set correctly.Tweak Other MLT Parameters: If results are still off, experiment with additional parameters like
max_query_terms(limits the number of terms used in the MLT query),stop_words(exclude common words), ormin_word_length(ignore short words). You can add these to either the per-requestmore_like_thiscall or the globaldefault_mlt_params.
内容的提问来源于stack exchange,提问作者ehsan ahmadi

