CRM-18539: Permit criteria for batch merging (other than group)
authoreileen <emcnaughton@wikimedia.org>
Tue, 17 May 2016 20:56:28 +0000 (08:56 +1200)
committereileen <emcnaughton@wikimedia.org>
Thu, 19 May 2016 03:26:15 +0000 (15:26 +1200)
CRM/Contact/Page/AJAX.php
CRM/Core/BAO/PrevNextCache.php
CRM/Dedupe/Merger.php
api/v3/Job.php
tests/phpunit/CRM/Dedupe/MergerTest.php
tests/phpunit/api/v3/JobTest.php

index 2983e97720b2b48cd52eb2b65aab70ebea6c5c67..c4a27de65b7b68d43cb8f78cf167b2c2f05159f1 100644 (file)
@@ -667,7 +667,7 @@ LIMIT {$offset}, {$rowCount}
       $contactType = CRM_Core_DAO::getFieldValue('CRM_Dedupe_DAO_RuleGroup', $rgid, 'contact_type');
     }
 
-    $cacheKeyString   = "merge {$contactType}_{$rgid}_{$gid}";
+    $cacheKeyString   = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid);
     $searchRows       = array();
     $selectorElements = array('is_selected', 'is_selected_input', 'src_image', 'src', 'src_email', 'src_street', 'src_postcode', 'dst_image', 'dst', 'dst_email', 'dst_street', 'dst_postcode', 'conflicts', 'weight', 'actions');
 
index a78e0f3c592990aefd25bc5a4eddd601f019561a..8cacb0d0fee9ab0cfb32a7f086f27d4e3ebe1047 100644 (file)
@@ -336,15 +336,19 @@ WHERE (pn.cacheKey $op %1 OR pn.cacheKey $op %2)
   }
 
   /**
+   * Repopulate the cache of merge prospects.
+   *
    * @param int $rgid
    * @param int $gid
    * @param NULL $cacheKeyString
+   * @param array $criteria
+   *   Additional criteria to filter by.
    *
    * @return bool
    */
-  public static function refillCache($rgid = NULL, $gid = NULL, $cacheKeyString = NULL) {
+  public static function refillCache($rgid = NULL, $gid = NULL, $cacheKeyString = NULL, $criteria = array()) {
     if (!$cacheKeyString && $rgid) {
-      $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid);
+      $cacheKeyString = CRM_Dedupe_Merger::getMergeCacheKeyString($rgid, $gid, $criteria);
     }
 
     if (!$cacheKeyString) {
@@ -364,7 +368,12 @@ WHERE (pn.cacheKey $op %1 OR pn.cacheKey $op %2)
       $foundDupes = CRM_Dedupe_Finder::dupesInGroup($rgid, $gid);
     }
     elseif ($rgid) {
-      $foundDupes = CRM_Dedupe_Finder::dupes($rgid);
+      $contactIDs = array();
+      if (!empty($criteria)) {
+        $contacts = civicrm_api3('Contact', 'get', array_merge(array('options' => array('limit' => 0), 'return' => 'id'), $criteria['contact']));
+        $contactIDs = array_keys($contacts['values']);
+      }
+      $foundDupes = CRM_Dedupe_Finder::dupes($rgid, $contactIDs);
     }
 
     if (!empty($foundDupes)) {
index 7a4381848f8e5a5dd00b276646496aef36aa9713..46ac4761923d9a9da4143bf67546df11ba378a41 100644 (file)
@@ -590,15 +590,18 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * @param int $batchLimit number of merges to carry out in one batch.
    * @param int $isSelected if records with is_selected column needs to be processed.
    *
+   * @param array $criteria
+   *   Criteria to use in the filter.
+   *
    * @return array|bool
    */
-  public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2) {
+  public static function batchMerge($rgid, $gid = NULL, $mode = 'safe', $autoFlip = TRUE, $batchLimit = 1, $isSelected = 2, $criteria = array()) {
     $redirectForPerformance = ($batchLimit > 1) ? TRUE : FALSE;
     $reloadCacheIfEmpty = (!$redirectForPerformance && $isSelected == 2);
-    $dupePairs = self::getDuplicatePairs($rgid, $gid, $reloadCacheIfEmpty, $batchLimit, $isSelected, '', ($mode == 'aggressive'));
+    $dupePairs = self::getDuplicatePairs($rgid, $gid, $reloadCacheIfEmpty, $batchLimit, $isSelected, '', ($mode == 'aggressive'), $criteria);
 
     $cacheParams = array(
-      'cache_key_string' => self::getMergeCacheKeyString($rgid, $gid),
+      'cache_key_string' => self::getMergeCacheKeyString($rgid, $gid, $criteria),
       // @todo stop passing these parameters in & instead calculate them in the merge function based
       // on the 'real' params like $isRespectExclusions $batchLimit and $isSelected.
       'join' => self::getJoinOnDedupeTable(),
@@ -1984,20 +1987,22 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    * @param bool $isSelected
    * @param array $orderByClause
    * @param bool $includeConflicts
+   * @param array $criteria
+   *   Additional criteria to narrow down the merge group.
    *
    * @return array
    *   Array of matches meeting the criteria.
    */
-  public static function getDuplicatePairs($rule_group_id, $group_id, $reloadCacheIfEmpty, $batchLimit, $isSelected, $orderByClause = '', $includeConflicts = TRUE) {
+  public static function getDuplicatePairs($rule_group_id, $group_id, $reloadCacheIfEmpty, $batchLimit, $isSelected, $orderByClause = '', $includeConflicts = TRUE, $criteria = array()) {
     $where = self::getWhereString($batchLimit, $isSelected);
-    $cacheKeyString = self::getMergeCacheKeyString($rule_group_id, $group_id, $includeConflicts);
+    $cacheKeyString = self::getMergeCacheKeyString($rule_group_id, $group_id, $criteria);
     $join = self::getJoinOnDedupeTable();
     $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where, 0, 0, array(), $orderByClause, $includeConflicts);
     if (empty($dupePairs) && $reloadCacheIfEmpty) {
       // If we haven't found any dupes, probably cache is empty.
       // Try filling cache and give another try. We don't need to specify include conflicts here are there will not be any
       // until we have done some processing.
-      CRM_Core_BAO_PrevNextCache::refillCache($rule_group_id, $group_id, $cacheKeyString);
+      CRM_Core_BAO_PrevNextCache::refillCache($rule_group_id, $group_id, $cacheKeyString, $criteria);
       $dupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, $join, $where, 0, 0, array(), $orderByClause, $includeConflicts);
       return $dupePairs;
     }
@@ -2009,14 +2014,18 @@ INNER JOIN  civicrm_membership membership2 ON membership1.membership_type_id = m
    *
    * @param int $rule_group_id
    * @param int $group_id
+   * @param array $criteria
+   *   Additional criteria to narrow down the merge group.
+   *   Currently we are only supporting the key 'contact' within it.
    *
    * @return string
    */
-  public static function getMergeCacheKeyString($rule_group_id, $group_id) {
+  public static function getMergeCacheKeyString($rule_group_id, $group_id, $criteria = array()) {
     $contactType = CRM_Dedupe_BAO_RuleGroup::getContactTypeForRuleGroup($rule_group_id);
     $cacheKeyString = "merge {$contactType}";
     $cacheKeyString .= $rule_group_id ? "_{$rule_group_id}" : '_0';
     $cacheKeyString .= $group_id ? "_{$group_id}" : '_0';
+    $cacheKeyString .= !empty($criteria) ? serialize($criteria) : '_0';
     return $cacheKeyString;
   }
 
index 33605856c270e2b3c9ddcaa77deb4094b236d83c..38a08fb65b8e3c37cacefe8054377cb6ba3f798c 100644 (file)
@@ -490,7 +490,7 @@ function civicrm_api3_job_process_batch_merge($params) {
   $mode = CRM_Utils_Array::value('mode', $params, 'safe');
   $autoFlip = CRM_Utils_Array::value('auto_flip', $params, TRUE);
 
-  $result = CRM_Dedupe_Merger::batchMerge($rule_group_id, $gid, $mode, $autoFlip);
+  $result = CRM_Dedupe_Merger::batchMerge($rule_group_id, $gid, $mode, $autoFlip, 1, 2, CRM_Utils_Array::value('criteria', $params, array()));
 
   return civicrm_api3_create_success($result, $params);
 }
index 73b9bab41192e3f284885da7c4b55b9da26ae208..4a5ecc0d2af0af0f92d39f0ce019364672ecf5b1 100644 (file)
@@ -163,7 +163,7 @@ class CRM_Dedupe_MergerTest extends CiviUnitTestCase {
 
     // Retrieve pairs from prev next cache table
     $select = array('pn.is_selected' => 'is_selected');
-    $cacheKeyString = "merge Individual_{$dao->id}_{$this->_groupId}";
+    $cacheKeyString = "merge Individual_{$dao->id}_{$this->_groupId}_0";
     $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
 
     $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.');
@@ -226,7 +226,7 @@ class CRM_Dedupe_MergerTest extends CiviUnitTestCase {
 
     // Retrieve pairs from prev next cache table
     $select = array('pn.is_selected' => 'is_selected');
-    $cacheKeyString = "merge Individual_{$dao->id}_{$this->_groupId}";
+    $cacheKeyString = "merge Individual_{$dao->id}_{$this->_groupId}_0";
     $pnDupePairs = CRM_Core_BAO_PrevNextCache::retrieve($cacheKeyString, NULL, NULL, 0, 0, $select);
 
     $this->assertEquals(count($foundDupes), count($pnDupePairs), 'Check number of dupe pairs in prev next cache.');
index ddc56e97fddefbf4ab2902fca7ae011cdfafb96b..63de71320322f5f3abe9567b98f5109c7c6149f3 100644 (file)
@@ -353,6 +353,39 @@ class api_v3_JobTest extends CiviUnitTestCase {
     ), 4);
   }
 
+  /**
+   * Test the batch merge by id range.
+   *
+   * We have 2 sets of 5 matches & set the merge only to merge the lower set.
+   */
+  public function testBatchMergeIDRange() {
+    for ($x = 0; $x <= 4; $x++) {
+      $id = $this->individualCreate(array('email' => 'batman@gotham.met'));
+    }
+    for ($x = 0; $x <= 4; $x++) {
+      $this->individualCreate(array('email' => 'robin@gotham.met'));
+    }
+    $result = $this->callAPISuccess('Job', 'process_batch_merge', array('criteria' => array('contact' => array('id' => array('<' => $id)))));
+    $this->assertEquals(4, count($result['values']['merged']));
+    $this->callAPISuccessGetCount('Contact', array('email' => 'batman@gotham.met'), 1);
+    $this->callAPISuccessGetCount('Contact', array('email' => 'robin@gotham.met'), 5);
+    $contacts = $this->callAPISuccess('Contact', 'get', array('is_deleted' => 0));
+    $deletedContacts = $this->callAPISuccess('Contact', 'get', array('is_deleted' => 0));
+    $this->callAPISuccessGetCount('Email', array(
+      'email' => 'batman@gotham.met',
+      'contact_id' => array('IN' => array_keys($contacts['values'])),
+    ), 1);
+    $this->callAPISuccessGetCount('Email', array(
+      'email' => 'batman@gotham.met',
+      'contact_id' => array('IN' => array_keys($deletedContacts['values'])),
+    ), 1);
+    $this->callAPISuccessGetCount('Email', array(
+      'email' => 'robin@gotham.met',
+      'contact_id' => array('IN' => array_keys($contacts['values'])),
+    ), 5);
+
+  }
+
   /**
    * Get data for batch merge.
    */