Optimized LocalRepo::findFiles()
authorAaron Schulz <aschulz@wikimedia.org>
Wed, 27 Nov 2013 18:39:50 +0000 (10:39 -0800)
committerAaron Schulz <aschulz@wikimedia.org>
Wed, 4 Dec 2013 07:43:44 +0000 (23:43 -0800)
Change-Id: Icea9c416c973a6b83c53e5bc201aed4528e9dd34

includes/filerepo/FileRepo.php
includes/filerepo/LocalRepo.php

index 8611238..466abe8 100644 (file)
@@ -473,7 +473,9 @@ class FileRepo {
         *     $findItem = array( 'title' => $title, 'private' => true );
         *     $findBatch = array( $findItem );
         *     $repo->findFiles( $findBatch );
-        * @return array
+        *
+        *    No title should appear in $items twice, as the result use titles as keys
+        * @return array (Map of file names => File objects) for matches
         */
        public function findFiles( array $items ) {
                $result = array();
index 49c2b8f..4bbcc7d 100644 (file)
@@ -229,6 +229,90 @@ class LocalRepo extends FileRepo {
                return $id;
        }
 
+       public function findFiles( array $items ) {
+               $finalFiles = array(); // map of (DB key => corresponding File) for matches
+
+               $searchSet = array(); // map of (DB key => normalized search params)
+               foreach ( $items as $item ) {
+                       $title = is_array( $item )
+                               ? File::normalizeTitle( $item['title'] )
+                               : File::normalizeTitle( $item );
+                       if ( $title ) { // valid title
+                               $searchSet[$title->getDbKey()] = ( is_array( $item ) ? $item : array() );
+                       }
+               }
+
+               $fileMatchesSearch = function( File $file, array $search ) {
+                       // Note: file name comparison done elsewhere (to handle redirects)
+                       return (
+                               $file->exists() &&
+                               (
+                                       ( empty( $search['time'] ) && !$file->isOld() ) ||
+                                       ( !empty( $search['time'] ) && $search['time'] === $file->getTimestamp() )
+                               ) &&
+                               ( !empty( $search['private'] ) || !$file->isDeleted( File::DELETED_FILE ) ) &&
+                               $file->userCan( File::DELETED_FILE )
+                       );
+               };
+
+               $repo = $this;
+               $applyMatchingFiles = function( ResultWrapper $res, &$searchSet, &$finalFiles )
+                       use ( $repo, $fileMatchesSearch )
+               {
+                       foreach ( $res as $row ) {
+                               $possFile = $repo->newFileFromRow( $row );
+                               $dbKey = $possFile->getName();
+                               // There must have been a search for this DB Key
+                               if ( $fileMatchesSearch( $possFile, $searchSet[$dbKey] ) ) {
+                                       $finalFiles[$dbKey] = $possFile;
+                                       unset( $searchSet[$dbKey] );
+                               }
+                       }
+               };
+
+               $dbr = $this->getSlaveDB();
+
+               // Query image table
+               $imgNames = array_keys( $searchSet );
+               if ( count( $imgNames ) ) {
+                       $res = $dbr->select( 'image',
+                               LocalFile::selectFields(), array( 'img_name' => $imgNames ), __METHOD__ );
+                       $applyMatchingFiles( $res, $searchSet, $finalFiles );
+               }
+
+               // Query old image table
+               $oiConds = array(); // WHERE clause array for each file
+               foreach ( $searchSet as $dbKey => $search ) {
+                       if ( isset( $search['params']['time'] ) ) {
+                               $oiConds[] = $dbr->makeList( array( 'oi_name' => $dbKey,
+                                       'oi_timestamp' => $dbr->timestamp( $search['params']['time'] ) ), LIST_AND );
+                       }
+               }
+               if ( count( $oiConds ) ) {
+                       $res = $dbr->select( 'oldimage',
+                               OldLocalFile::selectFields(), $dbr->makeList( $oiConds, LIST_OR ), __METHOD__ );
+                       $applyMatchingFiles( $res, $searchSet, $finalFiles );
+               }
+
+               // Check for redirects...
+               foreach ( $searchSet as $dbKey => $search ) {
+                       if ( !empty( $search['ignoreRedirect'] ) ) {
+                               continue;
+                       }
+                       $title = File::normalizeTitle( $dbKey );
+                       $redir = $this->checkRedirect( $title ); // hopefully hits memcached
+                       if ( $redir && $redir->getNamespace() == NS_FILE ) {
+                               $possFile = $this->newFile( $redir );
+                               if ( $possFile && $fileMatchesSearch( $possFile, $search ) ) {
+                                       $possFile->redirectedFrom( $title->getDBkey() );
+                                       $finalFiles[$dbKey] = $possFile;
+                               }
+                       }
+               }
+
+               return $finalFiles;
+       }
+
        /**
         * Get an array or iterator of file objects for files that have a given
         * SHA-1 content hash.