faceting on multiple multiple select facets

2,010 views
Skip to first unread message

hejsan

unread,
Mar 31, 2011, 11:33:33 AM3/31/11
to django-haystack
Hi.
I'm trying to implement a faceted search that works similar to this
one: http://autos.yahoo.com/carfinder/

Let's say I have these facets set up:
*make*
benz (8)
bentley (4)
suzuki (2)
...

*body_style*
Hatchback (4)
Sedan (4)
Coupe (2)
Convertible (2)
...

Using haystacks default faceting dictates that when I select f.ex.
make_exact:benz then the facet counts for all the other makes turn to
zero:
*make*
benz (8) - /selected/
bentley (0)
suzuki (0)
...

*body_style*
Hatchback (0)
Sedan (4)
Coupe (2)
Convertible (2)
...

Because of course there are no cars that are of make "Benz" and also
"Suzuki"

But what the user would expect in a multiselect scenario is that each
facet uses OR between selections within the same facet but AND between
selections from different facets, as can be seen on the yahoo
carfinder mentioned above.

Can this somehow be implemented with haystack?
Would I perhaps need to create different SearchQuerySets for each
facet where I exclude the search for that facet in order to display
correct facet counts?

Would it make sense to add a
SearchQuerySet.facet_multiple('something') function to the API to
allow for this more easily?

I would think this was a common problem.

Thanks.

Daniel Lindsley

unread,
Apr 3, 2011, 1:44:44 PM4/3/11
to django-...@googlegroups.com
Hejsan,


I guess I'm a little confused, as what you're describing is currently entirely doable in Haystack. I don't know what code you're using to present the facets, but drilling down with multiple makes looks like "SearchQuerySet().narrow("make_exact:(benz OR bentley)").facet("make").facet("body_style").facet_counts()". What gets put in the call to "narrow" makes the difference.


Daniel

> --
> You received this message because you are subscribed to the Google Groups "django-haystack" group.
> To post to this group, send email to django-...@googlegroups.com.
> To unsubscribe from this group, send email to django-haysta...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-haystack?hl=en.
>

hejsan

unread,
Apr 5, 2011, 1:32:35 PM4/5/11
to django-haystack
I know that I am able to narrow the search in this way, but I also
want to get the correct facet counts.
i.e. when I select make_exact:benz then I still want to get the counts
for the other makes because I'm implementing a multiselect:

*make*
[x] benz (8)
[ ] bentley (4)
[ ] suzuki (2)
...

*body_style*
[ ] Hatchback (0)
[ ] Sedan (4)
[ ] Coupe (2)
[ ] Convertible (2)


Upon reading the solr docs I have found out that as of solr 1.4
multiselect faceting is doable, as seen here:
http://wiki.apache.org/solr/SimpleFacetParameters#Multi-Select_Faceting_and_LocalParams

So I tried adding LocalParams directly to my faceting thus:

In my views.py:
sqs = SearchQuerySet().facet('{!ex=make_exact}make').facet('{!
ex=body_exact}body')

urlpatterns = patterns('haystack.views',
url(r'^$', FacetedSearchView(template='index.html',
load_all=False, form_class=MultiFacetedSearchForm,
searchqueryset=sqs), name="index"),
)

Then in my MultiFacetedSearchForm:
class MultiFacetedSearchForm(SearchForm):
def search(self):
sqs = super(MultiFacetedSearchForm, self).search()
if hasattr(self, 'data') and
self.data.has_key(u'selected_facets'):
for facet in self.data.getlist('selected_facets'):
facet_name, facet_value = facet.split(':')
narrow_query = u"{!tag=%s}%s" % (facet_name, facet)
sqs = sqs.narrow(narrow_query)
return sqs

def no_query_found(self):
return self.searchqueryset.all()


This works, but unfortunately in changes the facet values, i.e. it
splits on spaces and lowercases and shortens strings instead of
returning the original strings untouched.

Do you have any idea where this could be happening, that is the
splitting and lowercasing of the facet strings?

Thanks.


On Apr 3, 5:44 pm, Daniel Lindsley <polarc...@gmail.com> wrote:
> Hejsan,
>
>    I guess I'm a little confused, as what you're describing is currently entirely doable in Haystack. I don't know what code you're using to present the facets, but drilling down with multiple makes looks like "SearchQuerySet().narrow("make_exact:(benz OR bentley)").facet("make").facet("body_style").facet_counts()". What gets put in the call to "narrow" makes the difference.
>
> Daniel
>

hejsan

unread,
Apr 5, 2011, 2:04:44 PM4/5/11
to django-haystack
Ok, to answer myself:
My mistake was in my views.py:
instead of
sqs = SearchQuerySet().facet('{!ex=make_exact}make').facet('{!
ex=body_exact}body')

I should have done:
sqs = SearchQuerySet().facet('{!ex=make_exact}make_exact').facet('{!
ex=body_exact}body_exact')


It would be great if haystack somehow made this functionality abstract
so I wouldn't have to manually add the !tag and !ex LocalParams.


On Apr 5, 5:32 pm, hejsan <hr.bja...@gmail.com> wrote:
> I know that I am able to narrow the search in this way, but I also
> want to get the correct facet counts.
> i.e. when I select make_exact:benz then I still want to get the counts
> for the other makes because I'm implementing a multiselect:
>
> *make*
> [x] benz (8)
> [ ] bentley (4)
> [ ] suzuki (2)
> ...
>
> *body_style*
> [ ] Hatchback (0)
> [ ] Sedan (4)
> [ ] Coupe (2)
> [ ] Convertible (2)
>
> Upon reading the solr docs I have found out that as of solr 1.4
> multiselect faceting is doable, as seen here:http://wiki.apache.org/solr/SimpleFacetParameters#Multi-Select_Faceti...

zVictor

unread,
Jul 29, 2011, 1:20:51 PM7/29/11
to django-...@googlegroups.com
Hello there,

This is my first post here and I want to share my solution for this old topic.
First of all, my congratulations for haystack team for this wonderful work.
I was having the same mentioned problems, and this topic helped me a lot, so I guess there is no problem in UP it.
I would appreciate any comments about it, because, as I said, I am new here, and I want to learn more and more =]

urls.py (1 - why would it be views.py?):
from haystack.query import SearchQuerySet
from haystack.views import FacetedSearchView
from forms import MultiFacetedSearchForm

sqs = SearchQuerySet().facet('modalidade').facet('{!ex=categoria_exact}categoria_exact').facet('{!ex=qualidade_exact}qualidade_exact')


urlpatterns = patterns('haystack.views',
    url(r'^search/$', FacetedSearchView(form_class=MultiFacetedSearchForm, searchqueryset=sqs), name='haystack_search'),
)

forms.py:
from haystack.forms import FacetedSearchForm

class MultiFacetedSearchForm(FacetedSearchForm): # 2 - SearchForm doesn't work here. Is there any problem?
    def search(self):
        list = {}

        sqs = super(MultiFacetedSearchForm, self).search()
        if hasattr(self, 'data') and self.data.has_key(u'selected_facets'):
            for facet in self.data.getlist('selected_facets'):
                facet_name, facet_value = facet.split(':')
                list[facet_name] = list[facet_name] if list.has_key(facet_name) else []
                list[facet_name].append(facet_value)
                facet = facet_name+':"'+facet_value+'"' # 3 - added quotes to don't split it in spaces. Any problem?

               
                narrow_query = u"{!tag=%s}%s" % (facet_name, facet)
                sqs = sqs.narrow(narrow_query)
        self.selected_facets = list # added a list of selected facets to use in template's checkboxes

        return sqs
   
    def no_query_found(self):
        return self.searchqueryset.all()

4 - How could I order facets results alphabetically? [('apple', 2), (u'orange', 4)] instead of [('orange', 4), (u'apple', 2)].
5 - I just copied some things. Where could I learn what means 'tag' and 'ex'?

thanks and regards,
zVictor.



Em terça-feira, 5 de abril de 2011 15h04min44s UTC-3, hejsan escreveu:

    Ok, to answer myself:
    My mistake was in my views.py:
    instead of
    sqs = SearchQuerySet().facet('{!ex=make_exact}make').facet('{!
    ex=body_exact}body')

    I should have done:
    sqs = SearchQuerySet().facet('{!ex=make_exact}make_exact').facet('{!
    ex=body_exact}body_exact')


    It would be great if haystack somehow made this functionality abstract
    so I wouldn't have to manually add the !tag and !ex LocalParams.


Daniel Lindsley

unread,
Aug 15, 2011, 1:35:14 AM8/15/11
to django-...@googlegroups.com
zVictor,


> urls.py (1 - why would it be views.py?):
> from haystack.query import SearchQuerySet
> from haystack.views import FacetedSearchView
> from forms import MultiFacetedSearchForm
>
> sqs =
> SearchQuerySet().facet('modalidade').facet('{!ex=categoria_exact}categoria_exact').facet('{!ex=qualidade_exact}qualidade_exact')
>
> urlpatterns = patterns('haystack.views',
>     url(r'^search/$', FacetedSearchView(form_class=MultiFacetedSearchForm,
> searchqueryset=sqs), name='haystack_search'),
> )

Not sure what your question is here. Can you be more specific?

> class MultiFacetedSearchForm(FacetedSearchForm): # 2 - SearchForm doesn't
> work here. Is there any problem?
>     def search(self):
>         list = {}
>         sqs = super(MultiFacetedSearchForm, self).search()

This call to ``super`` is needed to go to ``FacetedSearchForm``, which
has additional code beyond the standard ``SearchForm`` to do the
faceting.


>                 facet = facet_name+':"'+facet_value+'"' # 3 - added quotes
> to don't split it in spaces. Any problem?

This was just example code AFAIK. Production code would have to be sturdier.


> 4 - How could I order facets results alphabetically? [('apple', 2),
> (u'orange', 4)] instead of [('orange', 4), (u'apple', 2)].

The only solution here is to post-process the order of them. Perhaps
creating a template tag to do it or doing it in the view & sorting it
there.


> 5 - I just copied some things. Where could I learn what means 'tag' and
> 'ex'?

Those are function queries being passed to Solr. See
http://wiki.apache.org/solr/FunctionQuery for an overview & usage.


Daniel

Paul Bormans

unread,
Aug 31, 2013, 5:10:38 PM8/31/13
to django-...@googlegroups.com
It took me some time to understand how to specify the filtering and have multi-select facet support. Just in two lines of code it comes down to the following:

sqs = sqs.narrow('{!tag=brand_tag}brand:("value1" OR "value2" OR "value3")')
sqs = sqs.facet('{!ex=brand_tag}brand')

Paul

Ricardo Silva

unread,
Jun 9, 2014, 7:28:10 AM6/9/14
to django-...@googlegroups.com
Hi,

The {!facet_name=facet}face_name syntax is SOLR exclusive ? Someone know how can I achieve the same on Elasticsearch ?

There is an open question on Stack Overflow asking it:


Regards,
Ricardo.

Paul Bormans

unread,
Jun 12, 2014, 2:47:48 AM6/12/14
to django-...@googlegroups.com
Ricardo,

Since i switched from solr to ES i was faced with the same issue. Please see http://demo.fullscale.co/multiselect/ for a working demo.

It is not supported by haystack currently but i'm extending it to make it work for ES.

Paul




Op maandag 9 juni 2014 13:28:10 UTC+2 schreef Ricardo Silva:

Ricardo Silva

unread,
Jun 16, 2014, 5:13:34 PM6/16/14
to django-...@googlegroups.com
Hi Paul,

   Yeah, apparently there is no easy way to achieve it directly on haystack. This demo really help me a lot. There is also a Gist from Matt Weber with the Curl commands to run it:



Regards.
Ricardo.

YusufSalahAdDin

unread,
Oct 28, 2014, 3:15:28 AM10/28/14
to django-...@googlegroups.com
Hey men, this works for me, only have changed this line: 
if hasattr(self, 'data') and self.selected_facets:
 And works in python3.4.

Thank you very much


El lunes, 17 de diciembre de 2012 10:15:33 UTC-5, Sudarshan Rao escribió:
Hi,

Iam a Django,Haystack newbie...

Had the same problem as mentioned in above post
...Multiselect within the same facet would still not work
...broke my head for couple of hours before finding the solution
... Modified the MultiFacetedSearchForm to add a "OR" operator between facets of the same field.
... example code below:

class MultiFacetedSearchForm(SearchForm):

    def __init__(self, *args, **kwargs):

        self.selected_facets = kwargs.pop("selected_facets", [])

        super(MultiFacetedSearchForm, self).__init__(*args, **kwargs)


    def search(self):

        sqs = super(MultiFacetedSearchForm, self).search()

        search_facets = {} 

        if hasattr(self, 'data') and self.data.has_key(u'selected_facets'):

            for facet in self.data.getlist('selected_facets'):

                facet_name, facet_value = facet.split(':')

                search_facets[facet_name] = facet_value


            for facet_search in search_facets.keys():

                search_query = None

                for facet in self.data.getlist('selected_facets'):

                    facet_name, facet_value = facet.split(':')

                    if facet_search == facet_name:

                        if search_query:

                            search_query += u' OR '

                        else:                      

                            search_query = u'('

                        search_query += u'"'+facet_value+'"'

                search_query +=  u')'      

                facet = facet_search+':'+search_query      

                narrow_query = u"{!tag=%s}%s" % (facet_search, facet)   

Reply all
Reply to author
Forward
0 new messages