From e8712447046ea5cddd82d13b9ab28a79d7c206ab Mon Sep 17 00:00:00 2001
From: Yannick ROGER <yannick.roger@ez.no>
Date: Thu, 27 Nov 2014 15:29:46 +0100
Subject: [PATCH] Fix EZP-23681: Delayed indexing of subtree on move (cherry
 picked from commit 9a15f89355acd095e858e1687ca91efe9a002d1f)

---
 cronjobs/indexcontent.php                          | 57 +++++++++++++++++++++-
 .../classes/ezcontentobjecttreenodeoperations.php  |  4 +-
 kernel/classes/ezsearch.php                        |  5 +-
 kernel/content/ezcontentoperationcollection.php    |  8 ++-
 .../ezcontentoperationdelete_regression.php        |  1 +
 5 files changed, 67 insertions(+), 8 deletions(-)

diff --git a/cronjobs/indexcontent.php b/cronjobs/indexcontent.php
index d2f483e..7893843 100644
--- a/cronjobs/indexcontent.php
+++ b/cronjobs/indexcontent.php
@@ -29,7 +29,7 @@
 while( true )
 {
     $entries = $db->arrayQuery(
-        "SELECT param FROM ezpending_actions WHERE action = 'index_object' GROUP BY param ORDER BY min(created)",
+        "SELECT param, action FROM ezpending_actions WHERE action = 'index_object' OR action = 'index_moved_node' GROUP BY param ORDER BY min(created)",
         array( 'limit' => $limit, 'offset' => $offset )
     );
 
@@ -38,6 +38,7 @@
         foreach ( $entries as $entry )
         {
             $objectID = (int)$entry['param'];
+            $action = $entry['action'];
 
             $cli->output( "\tIndexing object ID #$objectID" );
             $db->begin();
@@ -49,12 +50,64 @@
                 {
                     $searchEngine->removeObject( $object, false );
                 }
+
                 $removeFromPendingActions = $searchEngine->addObject( $object, false );
+
+                // When moving content (and only, because of performances), reindex the subtree
+                if ( $action == 'index_moved_node' )
+                {
+                    $nodeId = $object->attribute( 'main_node_id' );
+                    $node = eZContentObjectTreeNode::fetch( $nodeId );
+
+                    if ( !( $node instanceof eZContentObjectTreeNode ) )
+                    {
+                        $cli->error( "An error occured while trying fetching node $nodeId" );
+                    }
+
+                    $offset = 0;
+                    $limit = 50;
+
+                    $params = array( 'Limitation' => array(), 'MainNodeOnly' => true );
+
+                    $subtreeCount = $node->subTreeCount( $params );
+
+                    while ( $offset < $subtreeCount )
+                    {
+                        $subTree = $node->subTree(
+                            array_merge(
+                                $params,
+                                array( 'Offset' => $offset, 'Limit' => $limit, 'SortBy' => array() )
+                            )
+                        );
+
+                        if ( !empty( $subTree ) )
+                        {
+                            foreach ( $subTree as $innerNode )
+                            {
+                                /** @var $innerNode eZContentObjectTreeNode */
+                                $childObject = $innerNode->attribute( 'object' );
+                                if ( !$childObject )
+                                {
+                                    continue;
+                                }
+
+                                $searchEngine->addObject( $childObject, false );
+                            }
+                        }
+
+                        $offset += $limit;
+
+                        if ( $offset >= $subtreeCount )
+                        {
+                            break;
+                        }
+                    }
+                }
             }
 
             if ( $removeFromPendingActions )
             {
-                $db->query( "DELETE FROM ezpending_actions WHERE action = 'index_object' AND param = '$objectID'" );
+                $db->query( "DELETE FROM ezpending_actions WHERE action = '$action' AND param = '$objectID'" );
             }
             else
             {
diff --git a/kernel/classes/ezcontentobjecttreenodeoperations.php b/kernel/classes/ezcontentobjecttreenodeoperations.php
index 7e557e0..b8e91a5 100644
--- a/kernel/classes/ezcontentobjecttreenodeoperations.php
+++ b/kernel/classes/ezcontentobjecttreenodeoperations.php
@@ -104,10 +104,10 @@ static function move( $nodeID, $newParentNodeID )
                 $nodeAssignment->setAttribute( 'op_code', eZNodeAssignment::OP_CODE_MOVE );
                 $nodeAssignment->store();
 
-                // update search index
+                // update search index specifying we are doing a move operation
                 $nodeIDList = array( $nodeID );
                 eZSearch::removeNodeAssignment( $node->attribute( 'main_node_id' ), $newNode->attribute( 'main_node_id' ), $object->attribute( 'id' ), $nodeIDList );
-                eZSearch::addNodeAssignment( $newNode->attribute( 'main_node_id' ), $object->attribute( 'id' ), $nodeIDList );
+                eZSearch::addNodeAssignment( $newNode->attribute( 'main_node_id' ), $object->attribute( 'id' ), $nodeIDList, true );
             }
 
             $result = true;
diff --git a/kernel/classes/ezsearch.php b/kernel/classes/ezsearch.php
index 14008f8..a579840 100644
--- a/kernel/classes/ezsearch.php
+++ b/kernel/classes/ezsearch.php
@@ -544,15 +544,16 @@ public static function updateNodeVisibility( $nodeID, $action )
      * @param int $mainNodeID
      * @param int $objectID
      * @param array $nodeAssignmentIDList
+     * @param bool $isMoved true if node is being moved
      * @return false|mixed False in case method is undefined, otherwise return the result of the search engine call
      */
-    public static function addNodeAssignment( $mainNodeID, $objectID, $nodeAssignmentIDList )
+    public static function addNodeAssignment( $mainNodeID, $objectID, $nodeAssignmentIDList, $isMoved = false )
     {
         $searchEngine = eZSearch::getEngine();
 
         if ( $searchEngine instanceof ezpSearchEngine && method_exists( $searchEngine, 'addNodeAssignment' ) )
         {
-            return $searchEngine->addNodeAssignment( $mainNodeID, $objectID, $nodeAssignmentIDList );
+            return $searchEngine->addNodeAssignment( $mainNodeID, $objectID, $nodeAssignmentIDList, $isMoved );
         }
 
         return false;
diff --git a/kernel/content/ezcontentoperationcollection.php b/kernel/content/ezcontentoperationcollection.php
index e34ec3c..7548bb8 100644
--- a/kernel/content/ezcontentoperationcollection.php
+++ b/kernel/content/ezcontentoperationcollection.php
@@ -550,8 +550,10 @@ static public function resetNodeassignmentOpcodes( $objectID, $versionNum )
      *       the calls within a db transaction; thus within db->begin and db->commit.
      *
      * @param int $objectID Id of the object.
+     * @param int $version Operation collection passes this default param. Not used in the method
+     * @param bool $isMoved true if node is being moved
      */
-    static public function registerSearchObject( $objectID )
+    static public function registerSearchObject( $objectID, $version = null, $isMoved = false )
     {
         $objectID = (int)$objectID;
         eZDebug::createAccumulatorGroup( 'search_total', 'Search Total' );
@@ -576,7 +578,9 @@ static public function registerSearchObject( $objectID )
 
         if ( $insertPendingAction )
         {
-            eZDB::instance()->query( "INSERT INTO ezpending_actions( action, param ) VALUES ( 'index_object', '$objectID' )" );
+            $action = $isMoved ? 'index_moved_node' : 'index_object';
+
+            eZDB::instance()->query( "INSERT INTO ezpending_actions( action, param ) VALUES ( '$action', '$objectID' )" );
             return;
         }
