views:

541

answers:

4

I'm aware of full text search applications like Django Solr and solango, etc.

What I'm looking to built is more akin to an advanced search for a real estate site. E.g they can choose location, price, etc. similar to www.viewr.com advanced search.

What I have done so far is this, under the models custom manager:

def advanced_search(self, district, location, type, facilities, features, not_permitted):
        q_objects = []
        l_objects = []
        t_objects = []
        fc_objects = []
        ft_objects = []
        np_objects = []

        if district:
            if location:
                for loc in location:
                    l_objects.append(Q(location__exact=loc))
            else:
                l_objects.append(Q(location__district=district))


        if type:    
            for ty in type:
                t_objects.append(Q(listing_type__exact=ty))

        if facilities:    
            for fc in facilities:
                fc_objects.append(Q(property_facilities__exact=fc))

        if features:
            for ft in features:
                ft_objects.append(Q(property_features__exact=ft))
                ft_objects.append(Q(community_features__exact=ft))

        if not_permitted:    
            for np in not_permitted:
                np_objects.append(Q(not_permitted__exact=np))                              

        # Start with a bare QuerySet
        qs = self.get_query_set()

        if location:
            qs = qs.filter(reduce(operator.or_, l_objects))
        if type:
            qs = qs.filter(reduce(operator.or_, t_objects))
        if facilities:
            qs = qs.filter(reduce(operator.or_, fc_objects))
        if features:
            qs = qs.filter(reduce(operator.or_, ft_objects))
        if not_permitted:
            qs = qs.filter(reduce(operator.or_, np_objects))
        # Use operator's or_ to string together all of your Q objects.
        return qs

Right now I'm not getting very predictable results. Is there something I might be doing wrong? Is there a better way of doing the various OR searches/joins?

+1  A: 

http://code.google.com/p/djapian/ can index the fields of your choice and allow you build complex searches though combination, boolean logic, etc.

danros
+1  A: 

Just a couple of ideas.

It is very useful to use kwargs unpacking to provide arguments to filter method in such cases. Something like this can make the code simplier:

kwargs = {'not_permitted':np,'property_features': ft}
return qs.filter(**kwargs)

Maybe you should take a look at django's filtering code in admin. In django admin parameters like not_permitted__exact are passed via GET. And then, after some filtering, the whole GET dict can be passed as unpacked kwargs argument to filter method. This make things really simple when you have a lot of filter options.

Mike Korobov
Hmm, i missed the fact you want OR operator, not the AND.In that case I think the best solution is to use somthing like Djapian and set weights for individual fields as you want.But as I can see www.viewr.com advanced search uses AND approach for their filters, not OR.
Mike Korobov
hi, see my comment below, yours is closes to the example snippet shown
Rasiel
+1  A: 

The closest I've come to this scenario is this post by Jacob:

http://www.djangosnippets.org/snippets/32/

What I've done is:

def advanced_search(self, form):
        # It's easier to store a dict of the possible lookups we want, where
        # the values are the keyword arguments for the actual query.
        qdict = {'district': 'location__district',
          'location': 'location',
          'property_type': 'listing_type',
          'max_price': 'price__lte',
          'property_features': 'property_features',
          'community_features': 'community_features',
        }

        # Then we can do this all in one step instead of needing to call
        # 'filter' and deal with intermediate data structures.
        q_objs = [Q(**{qdict[k]: form.cleaned_data[k]}) for k in qdict.keys() if form.cleaned_data.get(k, None)]        
        search_results = RealEstateListing.objects.select_related().filter(*q_objs)
        return search_results

It works fine when I pass single choices, but when passing multiple choices as for property facilities it chokes and says:

OperationalError: Subquery returns more than 1 row

Rasiel
i figured out how to do multiple choices its simple as adding a __in to the field for e.gproperty_features__in
Rasiel
The line with `q_objs = ...` looks like a lot of magic to me. Would you care to rewrite your example to more pythonic code?
Frank Malina