diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java index 3193c5ef78..1f1c80a819 100644 --- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java +++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/rest/change/HashtagsIT.java @@ -237,6 +237,14 @@ public class HashtagsIT extends AbstractDaemonTest { assertMessage(r, "Hashtag removed: tag3"); } + @Test + public void testHashtagWithMixedCase() throws Exception { + PushOneCommit.Result r = createChange(); + addHashtags(r, "MyHashtag"); + assertThatGet(r).containsExactly("MyHashtag"); + assertMessage(r, "Hashtag added: MyHashtag"); + } + private IterableSubject< ? extends IterableSubject>, String, Iterable> diff --git a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java index 840f8b4690..f4fa0cbc93 100644 --- a/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java +++ b/gerrit-lucene/src/main/java/com/google/gerrit/lucene/LuceneChangeIndex.java @@ -114,6 +114,8 @@ public class LuceneChangeIndex implements ChangeIndex { private static final String PATCH_SET_FIELD = ChangeField.PATCH_SET.getName(); private static final String REVIEWEDBY_FIELD = ChangeField.REVIEWEDBY.getName(); + private static final String HASHTAG_FIELD = + ChangeField.HASHTAG_CASE_AWARE.getName(); private static final String STARREDBY_FIELD = ChangeField.STARREDBY.getName(); static Term idTerm(ChangeData cd) { @@ -414,6 +416,9 @@ public class LuceneChangeIndex implements ChangeIndex { if (fields.contains(REVIEWEDBY_FIELD)) { decodeReviewedBy(doc, cd); } + if (fields.contains(HASHTAG_FIELD)) { + decodeHashtags(doc, cd); + } if (fields.contains(STARREDBY_FIELD)) { decodeStarredBy(doc, cd); } @@ -470,6 +475,15 @@ public class LuceneChangeIndex implements ChangeIndex { } } + private void decodeHashtags(Document doc, ChangeData cd) { + IndexableField[] hashtag = doc.getFields(HASHTAG_FIELD); + Set hashtags = Sets.newHashSetWithExpectedSize(hashtag.length); + for (IndexableField r : hashtag) { + hashtags.add(r.binaryValue().utf8ToString()); + } + cd.setHashtags(hashtags); + } + private void decodeStarredBy(Document doc, ChangeData cd) { IndexableField[] starredBy = doc.getFields(STARREDBY_FIELD); Set accounts = diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java index a0d3fac3ba..bcbcff8d51 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/change/ChangeJson.java @@ -394,7 +394,7 @@ public class ChangeJson { out.project = in.getProject().get(); out.branch = in.getDest().getShortName(); out.topic = in.getTopic(); - out.hashtags = ctl.getNotes().load().getHashtags(); + out.hashtags = cd.hashtags(); out.changeId = in.getKey().get(); if (in.getStatus() != Change.Status.MERGED) { SubmitTypeRecord str = cd.submitTypeRecord(); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java index 67694ac63c..e62e66567e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeField.java @@ -15,6 +15,7 @@ package com.google.gerrit.server.index.change; import static com.google.common.base.MoreObjects.firstNonNull; +import static java.nio.charset.StandardCharsets.UTF_8; import com.google.common.base.Function; import com.google.common.base.Splitter; @@ -237,14 +238,29 @@ public class ChangeField { @Override public Iterable get(ChangeData input, FillArgs args) throws OrmException { - return ImmutableSet.copyOf(Iterables.transform(input.notes().load() - .getHashtags(), new Function() { - + return ImmutableSet.copyOf(Iterables.transform(input.hashtags(), + new Function() { @Override public String apply(String input) { return input.toLowerCase(); } + })); + } + }; + /** Hashtags with original case. */ + public static final FieldDef> HASHTAG_CASE_AWARE = + new FieldDef.Repeatable( + "_hashtag", FieldType.STORED_ONLY, true) { + @Override + public Iterable get(ChangeData input, FillArgs args) + throws OrmException { + return ImmutableSet.copyOf(Iterables.transform(input.hashtags(), + new Function() { + @Override + public byte[] apply(String hashtag) { + return hashtag.getBytes(UTF_8); + } })); } }; diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java index 9fbe51fb74..8fb900069a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/index/change/ChangeSchemaDefinitions.java @@ -64,8 +64,12 @@ public class ChangeSchemaDefinitions extends SchemaDefinitions { @Deprecated static final Schema V27 = schema(V26.getFields().values()); + @Deprecated static final Schema V28 = schema(V27, ChangeField.STARREDBY); + static final Schema V29 = + schema(V28, ChangeField.HASHTAG_CASE_AWARE); + public static final ChangeSchemaDefinitions INSTANCE = new ChangeSchemaDefinitions(); diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java index 452d51fa69..4d94828c93 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeData.java @@ -337,6 +337,7 @@ public class ChangeData { private ChangedLines changedLines; private SubmitTypeRecord submitTypeRecord; private Boolean mergeable; + private Set hashtags; private Set editsByUser; private Set reviewedBy; private Set draftsByUser; @@ -1018,6 +1019,17 @@ public class ChangeData { this.reviewedBy = reviewedBy; } + public Set hashtags() throws OrmException { + if (hashtags == null) { + hashtags = notes().getHashtags(); + } + return hashtags; + } + + public void setHashtags(Set hashtags) { + this.hashtags = hashtags; + } + public Set starredBy() throws OrmException { if (starredByUser == null) { starredByUser = starredChangesUtil.byChange(legacyId);