2013-10-31 45 views
1

我在我们的django应用程序中使用haystack进行搜索和搜索工作非常良好。但是我有一个关于reamtime搜索的问题。对于实时搜索,我使用haystack的默认RealTimeSignalProcessor(haystack.signals.RealtimeSignalProcessor)。我的模型包含一个多对多的领域。当数据只是为了多个字段而改变时,似乎realtimeignal处理器并没有正确更新索引数据。更新多对多数据后,我的搜索结果出错。Django Haystack索引不适用于模型中的很多很多领域

它在手动运行rebuild_index命令后工作。我认为rebuild_index是可行的,因为它首先进行清理,然后再建立索引数据。

有人可以提出一些解决方案的问题?

顺便下面的代码是围绕它的代码。

型号:

class Message_forum(models.Model): 
     message = models.ForeignKey(Message) 
     tags = models.ManyToManyField(Tag, blank=True, null=True) #this is many to many field 

search_index.py:

class Message_forumIndex(indexes.SearchIndex, indexes.Indexable): 
    text = indexes.EdgeNgramField(document=True, use_template=True) 
    message = indexes.CharField(model_attr='message', null=True) 
    tags = indexes.CharField(model_attr='tags', null=True) 

    def get_model(self): 
     return Message_forum 

    def index_queryset(self, using=None): 
     return self.get_model().objects.all() 

    def prepare_tags(self, obj): 
     return [tag.tag for tag in obj.tags.all()] 

指数模板:

{{ object.tags.tag }} 

settings.py:

HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' 

我有大海捞针的最新版本,并作为嗖后端。

回答

6

我深入挖掘干草堆的代码后发现了它。

在haystack默认RealTimeSignalProcessor中,它连接每个应用程序模型的post_save和post_delete信号。现在在handle_save方法中正在调用post_save和post_delete信号。在这种方法中,haystack正在验证发件人,在我的情况下是标记(多对多)字段,Message_forum_tag模型正在作为发件人传递。现在这个模型的索引不存在于我的search_index中,因为它不是我的应用程序模型,而是django生成的。所以在handle_save方法中,它绕过了对这个模型的任何改变,因此它并没有更新已改变对象的索引数据。

所以我想出了两个不同的解决方案来解决这个问题。

  1. 我可以创建在设置方法具体到我的模型Message_forum定制的实时信号处理,在此我可以在每个多到许多领域Message_forum与handle_save连接m2mchanged信号。同时,我可以将Message_forum作为发送者传递,这样干草堆将通过它的验证(不完全验证,但它试图获得它的索引obj),并将更新已更改对象的索引数据。

  2. 另一种方法是确保每当多对多字段发生更改时,都会调用其父级的保存方法(此处为Message_forum.save())。所以它总是会调用post_save信号,之后haystack将更新索引对象数据。

有花3小时左右弄明白。希望这会帮助有同样问题的人。

+0

你能显示代码吗?我也有同样的问题。尝试了一些东西,仍然没有功能工作。使用django-rq我知道了,但我不想使用第三方库来使它工作,如果它可以使用它自己的功能。 –

+0

我们通过采用上述第二种方法解决了这个问题。如果它不适用于你的情况,那么你应该创建一个定制的实时信号处理器。我也尝试了第一种方式。但是那段代码现在并不适合我。但是你可以在这里阅读更多关于它的信息.http://django-haystack.readthedocs.org/en/latest/signal_processors。html – nik

5

我有一个类似的问题,但我与混合Nikhil的1号和2号选项。

对于称为ContentItem并带有名为categories的m2m字段的模型,我创建了一个自定义信号处理器来扩展基本类。

所以,我实现了一个设置()从源复制,但添加了以下行:

models.signals.m2m_changed.connect(self.handle_save, sender=ContentItem.categories.through) 

,也与拆卸相同的(),但类似的断开连接线。我还扩展handle_save并改变了行:

index = self.connections[using].get_unified_index().get_index(sender) 

index = self.connections[using].get_unified_index().get_index(instance.__class__) 

这意味着该信号处理器被留意在管理表M2M变化为ContentItem类别,但是当一个M2M变化是所做的将传递正确的类的名称,即ContentItem而不是ContentItem.categories.through。

这似乎工作的大部分,但如果我删除类别m2m_changed不会触发,尽管关系被删除。 It looks like this might be a bug in django itself

所以我还添加以下行设置(和断开到拆卸):

models.signals.pre_delete.connect(self.handle_m2m_delete, sender=Category) 

而创建handle_save(handle_m2m_delete),其手工除去从通表中的关系的方法的重复,并保存在受影响的ContentItems(导致原始的handle_save被触发)。这至少意味着我不必记住保存父级以更新代码中任何其他位置的索引。

+0

非常感谢这个解决方案! – minder

+0

感谢您的解决方案,但您能否提供'handle_m2m_delete'的代码? 就像一张图片,一些代码告诉1000字 – straykiwi

+0

@straykiwi恐怕不是,我不再有权限访问 –

4

我可以提出一种替代解决方案,比试图观察所有正确信号并最终获得必须了解所有m2m关系的信号处理器的复杂性更简单。

它看起来像这样:

signals.py:

from collections import OrderedDict 

from haystack.signals import RealtimeSignalProcessor 


class BatchingSignalProcessor(RealtimeSignalProcessor): 
    """ 
    RealtimeSignalProcessor connects to Django model signals 
    we store them locally for processing later - must call 
    ``flush_changes`` from somewhere else (eg middleware) 
    """ 

    # Haystack instantiates this as a singleton 

    _change_list = OrderedDict() 

    def _add_change(self, method, sender, instance): 
     key = (sender, instance.pk) 
     if key in self._change_list: 
      del self._change_list[key] 
     self._change_list[key] = (method, instance) 

    def handle_save(self, sender, instance, created, raw, **kwargs): 
     method = super(BatchingSignalProcessor, self).handle_save 
     self._add_change(method, sender, instance) 

    def handle_delete(self, sender, instance, **kwargs): 
     method = super(BatchingSignalProcessor, self).handle_delete 
     self._add_change(method, sender, instance) 

    def flush_changes(self): 
     while True: 
      try: 
       (sender, pk), (method, instance) = self._change_list.popitem(last=False) 
      except KeyError: 
       break 
      else: 
       method(sender, instance) 

middleware.py:

from haystack import signal_processor 


class HaystackBatchFlushMiddleware(object): 
    """ 
    for use with our BatchingSignalProcessor 

    this should be placed *at the top* of MIDDLEWARE_CLASSES 
    (so that it runs last) 
    """ 
    def process_response(self, request, response): 
     try: 
      signal_processor.flush_changes() 
     except AttributeError: 
      # (in case we're not using our expected signal_processor) 
      pass 
     return response 

settings.py:

MIDDLEWARE_CLASSES = (
    'myproject.middleware.HaystackBatchFlushMiddleware', 
    ... 
) 

HAYSTACK_SIGNAL_PROCESSOR = 'myproject.signals.BatchingSignalProcessor' 

我试着在我的项目中,似乎工作正常。我欢迎任何意见或建议。

+1

谢谢,效果很好。 请注意,对于Django 1.9兼容性,您需要使用django-haystack版本> = 2.5.0,然后,由于django-haystack的更改,您需要在'middleware.py'中获取信号处理器,略有不同。 1.使用此导入:'from django.apps import apps',2.在try-except插入前'signal_processor = apps.get_app_config('haystack')。signal_processor' – bszom