]> source.dussan.org Git - nextcloud-server.git/commitdiff
Fix pagination of contacts search 21221/head
authorJoas Schilling <coding@schilljs.com>
Thu, 4 Jun 2020 08:05:28 +0000 (10:05 +0200)
committerJoas Schilling <coding@schilljs.com>
Wed, 10 Jun 2020 07:38:59 +0000 (09:38 +0200)
Signed-off-by: Joas Schilling <coding@schilljs.com>
apps/dav/lib/CardDAV/CardDavBackend.php
apps/dav/tests/unit/CardDAV/CardDavBackendTest.php

index a5d507629223897846837e414d42780f9e540184..40b48f69ae028d079738d4fb479e476ee54cddd4 100644 (file)
@@ -944,39 +944,49 @@ class CardDavBackend implements BackendInterface, SyncSupport {
         * @param array $searchProperties defines the properties within the query pattern should match
         * @param array $options = array() to define the search behavior
         *      - 'escape_like_param' - If set to false wildcards _ and % are not escaped, otherwise they are
+        *      - 'limit' - Set a numeric limit for the search results
+        *      - 'offset' - Set the offset for the limited search results
         * @return array an array of contacts which are arrays of key-value-pairs
         */
        public function search($addressBookId, $pattern, $searchProperties, $options = []) {
-               $query = $this->db->getQueryBuilder();
                $query2 = $this->db->getQueryBuilder();
 
-               $query2->selectDistinct('cp.cardid')->from($this->dbCardsPropertiesTable, 'cp');
-               $query2->andWhere($query2->expr()->eq('cp.addressbookid', $query->createNamedParameter($addressBookId)));
+               $query2->selectDistinct('cp.cardid')
+                       ->from($this->dbCardsPropertiesTable, 'cp')
+                       ->andWhere($query2->expr()->eq('cp.addressbookid', $query2->createNamedParameter($addressBookId)));
                $or = $query2->expr()->orX();
                foreach ($searchProperties as $property) {
-                       $or->add($query2->expr()->eq('cp.name', $query->createNamedParameter($property)));
+                       $or->add($query2->expr()->eq('cp.name', $query2->createNamedParameter($property)));
                }
                $query2->andWhere($or);
 
                // No need for like when the pattern is empty
                if ('' !== $pattern) {
                        if (\array_key_exists('escape_like_param', $options) && $options['escape_like_param'] === false) {
-                               $query2->andWhere($query2->expr()->ilike('cp.value', $query->createNamedParameter($pattern)));
+                               $query2->andWhere($query2->expr()->ilike('cp.value', $query2->createNamedParameter($pattern)));
                        } else {
-                               $query2->andWhere($query2->expr()->ilike('cp.value', $query->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
+                               $query2->andWhere($query2->expr()->ilike('cp.value', $query2->createNamedParameter('%' . $this->db->escapeLikeParameter($pattern) . '%')));
                        }
                }
-//             // FIXME Broken on MySQL: SQLSTATE[42000]: Syntax error or access violation: 1235 This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
-//             // FIXME Should use 2 queries instead
-//             if (isset($options['limit'])) {
-//                     $query2->setMaxResults($options['limit']);
-//             }
-//             if (isset($options['offset'])) {
-//                     $query2->setFirstResult($options['offset']);
-//             }
-
-               $query->select('c.carddata', 'c.uri')->from($this->dbCardsTable, 'c')
-                       ->where($query->expr()->in('c.id', $query->createFunction($query2->getSQL())));
+
+               if (isset($options['limit'])) {
+                       $query2->setMaxResults($options['limit']);
+               }
+               if (isset($options['offset'])) {
+                       $query2->setFirstResult($options['offset']);
+               }
+
+               $result = $query2->execute();
+               $matches = $result->fetchAll();
+               $result->closeCursor();
+               $matches = array_map(function ($match) {
+                       return (int) $match['cardid'];
+               }, $matches);
+
+               $query = $this->db->getQueryBuilder();
+               $query->select('c.carddata', 'c.uri')
+                       ->from($this->dbCardsTable, 'c')
+                       ->where($query->expr()->in('c.id', $query->createNamedParameter($matches, IQueryBuilder::PARAM_INT_ARRAY)));
 
                $result = $query->execute();
                $cards = $result->fetchAll();
index 8bde309b5a7edcde793a5743e8639b47f1f140e3..27139dfd470118d35569b157d090a8887c6ddb18 100644 (file)
@@ -759,6 +759,8 @@ class CardDavBackendTest extends TestCase {
                        ['Do', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
                        'check if duplicates are handled correctly' => ['John', ['FN', 'CLOUD'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
                        'case insensitive' => ['john', ['FN'], [], [['uri0', 'John Doe'], ['uri1', 'John M. Doe']]],
+                       'limit' => ['john', ['FN'], ['limit' => 1], [['uri0', 'John Doe']]],
+                       'limit and offset' => ['john', ['FN'], ['limit' => 1, 'offset' => 1], [['uri1', 'John M. Doe']]],
                        'find "_" escaped' => ['_', ['CLOUD'], [], [['uri2', 'find without options']]],
                        'find not empty ClOUD' => ['%_%', ['CLOUD'], ['escape_like_param'=>false], [['uri0', 'John Doe'], ['uri2', 'find without options']]],
                ];