public function searches()
 {
     if ($this->apiVersion < 2) {
         $this->e404();
     }
     // Check for general library access
     if (!$this->permissions->canAccess($this->objectLibraryID)) {
         $this->e403();
     }
     if ($this->isWriteMethod()) {
         // Check for library write access
         if (!$this->permissions->canWrite($this->objectLibraryID)) {
             $this->e403("Write access denied");
         }
         // Make sure library hasn't been modified
         if (!$this->singleObject) {
             $libraryTimestampChecked = $this->checkLibraryIfUnmodifiedSinceVersion();
         }
         Zotero_Libraries::updateVersionAndTimestamp($this->objectLibraryID);
     }
     $results = array();
     // Single search
     if ($this->singleObject) {
         $this->allowMethods(['HEAD', 'GET', 'PUT', 'PATCH', 'DELETE']);
         $search = Zotero_Searches::getByLibraryAndKey($this->objectLibraryID, $this->objectKey);
         if ($this->isWriteMethod()) {
             $search = $this->handleObjectWrite('search', $search ? $search : null);
             $this->e204();
         }
         if (!$search) {
             $this->e404("Search not found");
         }
         $this->libraryVersion = $search->version;
         if ($this->method == 'HEAD') {
             $this->end();
         }
         // Display search
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = $search->toAtom($this->queryParams);
                 break;
             case 'json':
                 $json = $search->toResponseJSON($this->queryParams, $this->permissions);
                 echo Zotero_Utilities::formatJSON($json);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     } else {
         $this->allowMethods(['HEAD', 'GET', 'POST', 'DELETE']);
         $this->libraryVersion = Zotero_Libraries::getUpdatedVersion($this->objectLibraryID);
         // Create a search
         if ($this->method == 'POST') {
             $this->queryParams['format'] = 'writereport';
             $obj = $this->jsonDecode($this->body);
             $results = Zotero_Searches::updateMultipleFromJSON($obj, $this->objectLibraryID, $this->queryParams, $this->userID, $libraryTimestampChecked ? 0 : 1, null);
             if ($cacheKey = $this->getWriteTokenCacheKey()) {
                 Z_Core::$MC->set($cacheKey, true, $this->writeTokenCacheTime);
             }
         } else {
             if ($this->method == 'DELETE') {
                 Zotero_DB::beginTransaction();
                 foreach ($this->queryParams['searchKey'] as $searchKey) {
                     Zotero_Searches::delete($this->objectLibraryID, $searchKey);
                 }
                 Zotero_DB::commit();
                 $this->e204();
             } else {
                 $title = "Searches";
                 $results = Zotero_Searches::search($this->objectLibraryID, $this->queryParams);
             }
         }
         $options = ['action' => $this->action, 'uri' => $this->uri, 'results' => $results, 'requestParams' => $this->queryParams, 'permissions' => $this->permissions, 'head' => $this->method == 'HEAD'];
         switch ($this->queryParams['format']) {
             case 'atom':
                 $this->responseXML = Zotero_API::multiResponse(array_merge($options, ['title' => $this->getFeedNamePrefix($this->objectLibraryID) . $title]));
                 break;
             case 'json':
             case 'keys':
             case 'versions':
             case 'writereport':
                 Zotero_API::multiResponse($options);
                 break;
             default:
                 throw new Exception("Unexpected format '" . $this->queryParams['format'] . "'");
         }
     }
     $this->end();
 }