Merge "Add 'has:hashtag' search operator"
diff --git a/Documentation/user-search.txt b/Documentation/user-search.txt
index 8197a96..5d40e2d 100644
--- a/Documentation/user-search.txt
+++ b/Documentation/user-search.txt
@@ -498,6 +498,10 @@
 +
 True if the change has attention by the current user.
 
+has:hashtag::
++
+True if the change has at least one hashtag.
+
 
 [[is]]
 [[is-starred]]
diff --git a/java/com/google/gerrit/server/query/change/ChangePredicates.java b/java/com/google/gerrit/server/query/change/ChangePredicates.java
index d5f9c5b..fdaa061 100644
--- a/java/com/google/gerrit/server/query/change/ChangePredicates.java
+++ b/java/com/google/gerrit/server/query/change/ChangePredicates.java
@@ -249,6 +249,11 @@
         ChangeField.FUZZY_HASHTAG, HashtagsUtil.cleanupHashtag(hashtag).toLowerCase(Locale.US));
   }
 
+  /** Returns a predicate that matches changes that are tagged with at least one hashtag. */
+  public static Predicate<ChangeData> hasHashtag() {
+    return new ChangeIndexPredicate(ChangeField.PREFIX_HASHTAG, "");
+  }
+
   /**
    * Returns a predicate that matches changes in the provided {@code hashtag}. Used with prefixes
    */
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index 704f8ed..6311c65 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -719,6 +719,11 @@
       return new IsUnresolvedPredicate();
     }
 
+    if ("hashtag".equalsIgnoreCase(value)) {
+      checkOperatorAvailable(ChangeField.PREFIX_HASHTAG, "has:hashtag");
+      return ChangePredicates.hasHashtag();
+    }
+
     // for plugins the value will be operandName_pluginName
     List<String> names = PLUGIN_SPLITTER.splitToList(value);
     if (names.size() == 2) {
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 0f09496..8d1e3dd 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -2871,6 +2871,23 @@
   }
 
   @Test
+  public void byHasHashtag() throws Exception {
+    assume().that(getSchema().hasField(ChangeField.PREFIX_HASHTAG)).isTrue();
+
+    Project.NameKey project = Project.nameKey("repo");
+    repo = createAndOpenProject(project);
+    Change change1 = insert(project, newChange(repo));
+    Change change2 = insert(project, newChange(repo));
+    Change change3 = insert(project, newChange(repo));
+
+    addHashtags(change1, "foo");
+    addHashtags(change2, "foo", "bar");
+
+    assertQuery("has:hashtag", change2, change1);
+    assertQuery("-has:hashtag", change3);
+  }
+
+  @Test
   public void byHashtagFullText() throws Exception {
     assume().that(getSchema().hasField(ChangeField.FUZZY_HASHTAG)).isTrue();
     ImmutableList<Change> changes = setUpHashtagChanges();
diff --git a/polygerrit-ui/app/elements/core/gr-search-autocomplete/gr-search-autocomplete.ts b/polygerrit-ui/app/elements/core/gr-search-autocomplete/gr-search-autocomplete.ts
index 852e549..8b9676e 100644
--- a/polygerrit-ui/app/elements/core/gr-search-autocomplete/gr-search-autocomplete.ts
+++ b/polygerrit-ui/app/elements/core/gr-search-autocomplete/gr-search-autocomplete.ts
@@ -61,6 +61,7 @@
   'has:attention',
   'has:draft',
   'has:edit',
+  'has:hashtag',
   'has:star',
   'has:unresolved',
   'hasfooter:',