[VUFIND-177] More flexible facet behavior Created: 20/Nov/09  Updated: 17/Feb/15  Resolved: 01/Oct/13

Status: Resolved
Project: VuFind®
Components: Search
Affects versions: None
Fix versions: 2.2

Type: Improvement Priority: Major
Reporter: Demian Katz Assignee: Demian Katz
Resolution: Fixed Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original estimate: Not Specified

Attachments: Text File multiselect-facets.patch    
Issue links:
incorporate
incorporates VUFIND-616 ORed facets Resolved
incorporates VUFIND-45 Advanced Search Limit enhancement Resolved

 Description   
We should think about revamping our facet handling to allow more flexible behavior:

- EXCLUDING all items matching a particular facet from search results
- Matching multiple facets with an OR operator

Some discussion of the issue has already taken place in this vufind-tech thread:

http://sourceforge.net/mailarchive/message.php?msg_id=ABC31E122EEAC44897B337BDEA8977369EB8FD23BE%40VUEX2.vuad.villanova.edu

The "matching multiple facets" issue is also mentioned in VUFIND-45, which relates to the advanced search side of the problem. A bigger challenge is revamping the facet display on the search results screen to allow more sophisticated user selections without making things unnecessarily confusing.

 Comments   
Comment by Demian Katz [ 22/Dec/09 ]
In improving facets, we may want to draw some inspiration from Summon. Additionally, as we go forward, we should ideally keep the facet behavior in the Summon module as consistent as possible with regular VuFind search -- see VUFIND-202.
Comment by Demian Katz [ 16/Apr/10 ]
Another useful feature (though perhaps difficult to reconcile with some of the other functionality proposed here) would be nested/tiered facets, where selecting one facet value reveals more granular facets at a lower level. For example, this would be useful for call number faceting -- i.e. in Dewey, show the hundreds-level facets first, but then switch to the tens-level when a hundreds-level is selected, move to ones-level when a tens-level is selected, etc.
Comment by Demian Katz [ 30/Jun/10 ]
Other useful facet capabilities would be changing the sort order (i.e. alphabetical vs. relevance-based) and paging through more facets (a critical feature in order to make alphabetical sort useful).
Comment by Demian Katz [ 20/Apr/11 ]
(This comment is adapted from a message sent to the vufind-tech mailing list; I'm copying it here for future reference. Since the original question related specifically to VUFIND-45, my comments below do not account for the user interface complexities, which are another layer of complexity.)

I hope to be able to address these issues as part of the VuFind 2.0 redesign, but that's a large project that won't be completed for at least a year or so. In the meantime, if you want to try to work on this within the VuFind 1.x codebase, I'd be happy to help out as much as I can.

Essentially it boils down to three problems:

1.) Figure out how to make the behavior configurable in a logical fashion
2.) Figure out how to pass parameters in the URL to get the correct behavior
3.) Figure out how to achieve the behavior based on the passed parameters

Of course, if you're only interested in implementing this locally rather than making it something that's part of the standard VuFind distribution, you can probably get away with cutting some corners on the design of points 1 and 2. Even if you would like to share code, it may make sense to do a quick and dirty implementation first to prove the theory and then refine the design from there.

For point #1, we probably need new settings in web/conf/facets.ini... but you might be able to hard-code something in the advanced search template and worry about configuration later.

For point #2, you can take a look at how filters are handled in web/sys/SearchObject/Base.php. Particularly relevant are the renderSearchUrl() and initFilters() methods, which show how filters are read from the URL and how URLs are built using saved filter settings in the object. Perhaps we need to maintain multiple types of filter arrays with different parameter names in order to allow all the necessary functionality. Coming up with a good design here is probably the hardest part of the problem -- I haven't had a chance to sit down and really think about it yet myself, but I'd be happy to discuss it with you if you have some ideas.

For point #3, you should look at the processSearch method in web/sys/SearchObject/Solr.php, which shows how the object properties created in the base class are converted into actual Solr filter queries and passed along to the query.
Comment by Daniel Aineah [ 24/Aug/11 ]
I have managed to implement multiple selection of values under a single facet in what I believe is a step towards achieving more flexible facet behavior. My office wanted only one facet to be multi-select, so I configured that in facets.ini by adding this line:
        [Facet_Manipulation]
        multi_selection = collection_facet
I extracted this setting in the constructor of web/sys/SearchObject/Solr.php by placing the multi-select facet into an array:
        $this->multiSelectFacet = array();
        $this->multiSelectFacet[] = $this->getFacetSetting(
        'Facet_Manipulation', 'multi_selection');

In the processSearch method of the same file, I modified the code handling filter queries so that filters corresponding to the multi-select facet could be stored in a separate array:
        foreach ($this->filterList as $field => $filter) {
            foreach ($filter as $value) {
                // Special case -- allow trailing wildcards and ranges:
                //Save filters containing the multiselect facet to a
                // different array
                if (substr($value, -1) == '*'|| preg_match('/\[[^\]]+\s+TO\s+[^\]]+\]/', $value)) {
                    if(in_array($field, $this->multiSelectFacet))
                           $multiFilterList[] = "$field:$value";
                     else
                           $filterQuery[] = "$field:$value";
                   } else {
                        if(in_array($field, $this->multiSelectFacet))
                                $multiFilterList[] = "$field:\"$value\"";
                        else
                                $filterQuery[] = "$field:\"$value\"";
                  }
              }
            }

Having collected the multi-select facet filters into an array, I joined them together using an OR to implement inclusive rather than exclusive search for each of the filters. Additionally, I tagged the now single filter so that it could be excluded later when faceting on the multi-select category. I then added the filter to the filterQuery array:
//If there are multiselect facet filters, join them with an OR
if(!empty($multiFilterList))
$filterQuery[]="{!tag=multiSelectFilter}".implode(" OR ", $multiFilterList);
Still in the same method, I modified the coding for facet options to exclude the multi-select constraints from multi-select faceting so as to prevent other facet values from disappearing whenever one of the values was selected:
           foreach ($this->facetConfig as $facetField => $facetName) {
               //if a multiselect facet, exclude the multiselect filters
               if(in_array($facetField, $this->multiSelectFacet))
                       $facetSet['field'][] = "{!ex=multiSelectFilter}".$facetField;
               else
                       $facetSet['field'][] = $facetField;

Thus, was multiple selection achieved. Although this approach is optimized for a single facet, it could be applied to more facets by adding the desired multi-select facets to facets.ini's [Facet_Manipulation]. Furthermore, it is possible to extend the behavior so that the search method implements an OR among values under a single facet and an AND across values of different facets.
Comment by Tuan Nguyen [ 18/Sep/11 ]
Following Daniel Aineah instructions for multi-selectable facets, I extend the code so that other facets can be multi-selectable. Attached is a patch in case others want to implement this right away.
Comment by Demian Katz [ 20/Sep/13 ]
I have begun work on more flexible facet behavior in this pull request:

https://github.com/vufind-org/vufind/pull/36

As of today, I have added the ability to exclude facet values and to use ORed facet values from advanced search (VUFIND-45); still to do: configurable ORed facets in the main side facet area.

For the moment I am focusing primarily on the backend implementation -- some UI refinement will probably be needed after that is complete.
Comment by Demian Katz [ 24/Sep/13 ]
As of today, the pull request has been updated for full AND/OR/NOT facet functionality. The only remaining step is to make the UI a bit more palatable; Chris is going to work on that part.
Comment by Chelsea Lobdell [ 06/Feb/15 ]
Hi Demian,

Any update on configurable ORed facets in the brief search side facet area? TriCo is interested in this functionality. Maybe it's something we can collaborate on?

- Chelsea
Comment by Demian Katz [ 06/Feb/15 ]
Chelsea,

The OR functionality has been in master for a while. You just need to turn it on with the orFacets setting in facets.ini. Is there an extension of the functionality that you need?

- Demian
Comment by Chelsea Lobdell [ 17/Feb/15 ]
Nope, this is exactly the functionality I was looking for. I just misread the comments and thought that it had only been applied to the advanced search and not yet to the brief search but you guys are way ahead of me! Thanks!

- Chelsea
Generated at Fri Apr 19 22:02:31 UTC 2024 using Jira 1001.0.0-SNAPSHOT#100250-rev:2b88e55752dc82be8616a67bc2b73a87c8e22b48.