diff --git a/src/Plugin/search_api/processor/ContentAccess.php b/src/Plugin/search_api/processor/ContentAccess.php
index ba74dd3..bae30b5 100644
--- a/src/Plugin/search_api/processor/ContentAccess.php
+++ b/src/Plugin/search_api/processor/ContentAccess.php
@@ -258,8 +258,7 @@ protected function addNodeAccess(QueryInterface $query, AccountInterface $accoun
     //     [grants view access to one of the user's gid/realm combinations]
     //   )
     // If there are no "other" datasources, we don't need the nested OR,
-    // however, and can add the "ADD".
-    // @todo Add a filter tag, once they are implemented.
+    // however, and can add the inner conditions directly to the query.
     if ($unaffected_datasources) {
       $outer_conditions = $query->createConditionGroup('OR', array('content_access'));
       $query->addConditionGroup($outer_conditions);
@@ -287,10 +286,7 @@ protected function addNodeAccess(QueryInterface $query, AccountInterface $accoun
       // remove all results of node or comment datasources. Otherwise, we should
       // not return any results at all.
       if (!$unaffected_datasources) {
-        // @todo More elegant way to return no results?
-        // @todo Now that field IDs can be picked freely, this can theoretically
-        //   even fail! Needs to be fixed!
-        $query->addCondition('search_api_language', '');
+        $query->abort($this->t('You have no access to any results in this search.'));
       }
       return;
     }
diff --git a/src/Plugin/views/query/SearchApiQuery.php b/src/Plugin/views/query/SearchApiQuery.php
index e58b1b2..805d345 100644
--- a/src/Plugin/views/query/SearchApiQuery.php
+++ b/src/Plugin/views/query/SearchApiQuery.php
@@ -491,7 +491,7 @@ public function execute(ViewExecutable $view) {
    * Used by handlers to flag a fatal error which shouldn't be displayed but
    * still lead to the view returning empty and the search not being executed.
    *
-   * @param string|null $msg
+   * @param \Drupal\Component\Render\MarkupInterface|string|null $msg
    *   Optionally, a translated, unescaped error message to display.
    */
   public function abort($msg = NULL) {
@@ -499,6 +499,9 @@ public function abort($msg = NULL) {
       $this->errors[] = $msg;
     }
     $this->abort = TRUE;
+    if (isset($this->query)) {
+      $this->query->abort($msg);
+    }
   }
 
   /**
diff --git a/src/Query/Query.php b/src/Query/Query.php
index 387be4d..109c7e8 100644
--- a/src/Query/Query.php
+++ b/src/Query/Query.php
@@ -6,6 +6,7 @@
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\search_api\IndexInterface;
 use Drupal\search_api\SearchApiException;
+use Drupal\search_api\Utility;
 
 /**
  * Provides a standard implementation for a Search API query.
@@ -84,6 +85,13 @@ class Query implements QueryInterface {
   protected $sorts = array();
 
   /**
+   * Information about whether the query has been aborted or not.
+   *
+   * @var \Drupal\Component\Render\MarkupInterface|string|true|null
+   */
+  protected $aborted;
+
+  /**
    * Options configuring this query.
    *
    * @var array
@@ -306,23 +314,65 @@ public function range($offset = NULL, $limit = NULL) {
   /**
    * {@inheritdoc}
    */
+  public function abort($error_message = NULL) {
+    $this->aborted = isset($error_message) ? $error_message : TRUE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function wasAborted() {
+    return $this->aborted !== NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getAbortMessage() {
+    return is_bool($this->aborted) ? $this->aborted : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function execute() {
+    // Check for aborted status both before and after calling preExecute().
+    $response = $this->getAbortResults();
+    if ($response) {
+      return $response;
+    }
+
     // Prepare the query for execution by the server.
     $this->preExecute();
 
+    $response = $this->getAbortResults();
+    if ($response) {
+      return $response;
+    }
+
     // Execute query.
     $response = $this->index->getServerInstance()->search($this);
 
     // Postprocess the search results.
     $this->postExecute($response);
 
-    // Store search for later retrieval for facets, etc.
-    // @todo Figure out how to store the executed searches for the request.
-    // search_api_current_search(NULL, $this, $response);
     return $response;
   }
 
   /**
+   * Creates and returns an empty result set if the query was aborted.
+   *
+   * @return \Drupal\search_api\Query\ResultSetInterface|null
+   *   An empty result set if the query was aborted, NULL otherwise.
+   */
+  protected function getAbortResults() {
+    if (!$this->wasAborted()) {
+      return NULL;
+    }
+    return Utility::createSearchResultSet($this);
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function preExecute() {
diff --git a/src/Query/QueryInterface.php b/src/Query/QueryInterface.php
index c4d9407..5d130f0 100644
--- a/src/Query/QueryInterface.php
+++ b/src/Query/QueryInterface.php
@@ -146,6 +146,37 @@ public function sort($field, $order = self::SORT_ASC);
   public function range($offset = NULL, $limit = NULL);
 
   /**
+   * Aborts this query.
+   *
+   * This will mean that, while the query object otherwise acts normally, it
+   * won't be passed to the server and won't return any results.
+   *
+   * @param \Drupal\Component\Render\MarkupInterface|string|null $error_message
+   *   (optional) A translated error message explaining the reason why the
+   *   query was aborted.
+   *
+   * @return $this
+   */
+  public function abort($error_message = NULL);
+
+  /**
+   * Determines whether this query was aborted.
+   *
+   * @return bool
+   *   TRUE if the query was aborted, FALSE otherwise.
+   */
+  public function wasAborted();
+
+  /**
+   * Retrieves the error message explaining why this query was aborted, if any.
+   *
+   * @return \Drupal\Component\Render\MarkupInterface|string|null
+   *   An error message, if set, or NULL if none was set. Please be aware that
+   *   a NULL message does not have to mean that the query was not aborted.
+   */
+  public function getAbortMessage();
+
+  /**
    * Executes this search query.
    *
    * @return \Drupal\search_api\Query\ResultSetInterface
diff --git a/src/Query/ResultSet.php b/src/Query/ResultSet.php
index 2d1ff29..075698a 100644
--- a/src/Query/ResultSet.php
+++ b/src/Query/ResultSet.php
@@ -21,7 +21,7 @@ class ResultSet implements \IteratorAggregate, ResultSetInterface {
    *
    * @var int
    */
-  protected $resultCount;
+  protected $resultCount = 0;
 
   /**
    * The result items.
