From 99c3731ea7d206554cc105441805080d4dffebc5 Mon Sep 17 00:00:00 2001
From: Ere Maijala <ere.maijala@helsinki.fi>
Date: Fri, 20 Sep 2019 16:10:13 +0300
Subject: [PATCH] Add exclude and or-facet support to Primo. Expose exclude
 support. (#1437)

---
 config/vufind/Primo.ini                       |  4 +++
 .../VuFind/src/VuFind/Search/Primo/Params.php | 33 ++++++++++++++++---
 .../src/VuFind/Search/Primo/Results.php       |  4 ++-
 .../VuFindSearch/Backend/Primo/Connector.php  | 25 +++++++++++---
 4 files changed, 56 insertions(+), 10 deletions(-)

diff --git a/config/vufind/Primo.ini b/config/vufind/Primo.ini
index 6f826a17edb..6823989cbd4 100644
--- a/config/vufind/Primo.ini
+++ b/config/vufind/Primo.ini
@@ -130,6 +130,10 @@ top_rows = 2
 top_cols = 3
 ; Do we want any facets to be collapsed by default?
 ;collapsedFacets = *
+; Should we show "exclude" links for some or all of the facets? Set to * for
+; all facets, use a comma-separated list to show for some of the facets, set
+; to false or omit to disable "exclude" links
+;exclude = *
 
 ; These settings affect the way the facets are displayed
 [Facet_Settings]
diff --git a/module/VuFind/src/VuFind/Search/Primo/Params.php b/module/VuFind/src/VuFind/Search/Primo/Params.php
index f730978fc24..b5643ebd84c 100644
--- a/module/VuFind/src/VuFind/Search/Primo/Params.php
+++ b/module/VuFind/src/VuFind/Search/Primo/Params.php
@@ -71,11 +71,7 @@ class Params extends \VuFind\Search\Base\Params
         $sort = $this->getSort();
         $finalSort = ($sort == 'relevance') ? null : $sort;
         $backendParams->set('sort', $finalSort);
-        $filterList = array_merge(
-            $this->getHiddenFilters(),
-            $this->filterList
-        );
-        $backendParams->set('filterList', $filterList);
+        $backendParams->set('filterList', $this->getFilterSettings());
 
         return $backendParams;
     }
@@ -116,4 +112,31 @@ class Params extends \VuFind\Search\Base\Params
         }
         return ucwords(str_replace('_', ' ', $str));
     }
+
+    /**
+     * Return the current filters as an array
+     *
+     * @return array
+     */
+    public function getFilterSettings()
+    {
+        $result = [];
+        $filterList = array_merge(
+            $this->getHiddenFilters(),
+            $this->filterList
+        );
+        foreach ($filterList as $field => $filter) {
+            $facetOp = 'AND';
+            $prefix = substr($field, 0, 1);
+            if ('~' === $prefix || '-' === $prefix) {
+                $facetOp = '~' === $prefix ? 'OR' : 'NOT';
+                $field = substr($field, 1);
+            }
+            $result[$field] = [
+                'facetOp' => $facetOp,
+                'values' => $filter
+            ];
+        }
+        return $result;
+    }
 }
diff --git a/module/VuFind/src/VuFind/Search/Primo/Results.php b/module/VuFind/src/VuFind/Search/Primo/Results.php
index 6565d7100f0..3ebb7b57244 100644
--- a/module/VuFind/src/VuFind/Search/Primo/Results.php
+++ b/module/VuFind/src/VuFind/Search/Primo/Results.php
@@ -100,7 +100,9 @@ class Results extends \VuFind\Search\Base\Results
                             'displayText' => $displayText,
                             'isApplied' =>
                                 $this->getParams()->hasFilter("$field:" . $value),
-                            'operator' => 'AND', 'count' => $count
+                            'operator' =>
+                                $this->getParams()->getFacetOperator($field),
+                            'count' => $count
                         ];
                     }
 
diff --git a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
index 56d22647e0c..fe85b838359 100644
--- a/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
+++ b/module/VuFindSearch/src/VuFindSearch/Backend/Primo/Connector.php
@@ -284,10 +284,27 @@ class Connector implements \Zend\Log\LoggerAwareInterface
             //     range facet control in the interface. look for injectPubDate
             if (!empty($args["filterList"])) {
                 foreach ($args["filterList"] as $facet => $values) {
-                    foreach ($values as $value) {
-                        $thisValue = preg_replace('/,/', '+', $value);
-                        $qs[] = "query=facet_" . $facet . ",exact,"
-                            . urlencode($thisValue);
+                    $facetOp = 'AND';
+                    if (isset($values['values'])) {
+                        $facetOp = $values['facetOp'];
+                        $values = $values['values'];
+                    }
+                    array_map(
+                        function ($value) {
+                            return urlencode(preg_replace('/,/', '+', $value));
+                        },
+                        $values
+                    );
+                    if ('OR' === $facetOp) {
+                        $qs[] = "query_inc=facet_$facet,exact," .
+                            implode(',', $values);
+                    } elseif ('NOT' === $facetOp) {
+                        $qs[] = "query_exc=facet_$facet,exact," .
+                            implode(',', $values);
+                    } else {
+                        foreach ($values as $value) {
+                            $qs[] = "query_inc=facet_$facet,exact,$value";
+                        }
                     }
                 }
             }
-- 
GitLab