diff --git a/application/src/main/java/run/halo/app/extension/index/IndexEntryImpl.java b/application/src/main/java/run/halo/app/extension/index/IndexEntryImpl.java index b7ea812a17..185d46de4d 100644 --- a/application/src/main/java/run/halo/app/extension/index/IndexEntryImpl.java +++ b/application/src/main/java/run/halo/app/extension/index/IndexEntryImpl.java @@ -37,7 +37,7 @@ public IndexEntryImpl(IndexDescriptor indexDescriptor) { Comparator keyComparator() { var order = indexDescriptor.getSpec().getOrder(); if (IndexSpec.OrderType.ASC.equals(order)) { - return Comparator.naturalOrder(); + return KeyComparator.INSTANCE; } else if (IndexSpec.OrderType.DESC.equals(order)) { return Comparator.reverseOrder(); } else { @@ -45,6 +45,39 @@ Comparator keyComparator() { } } + static class KeyComparator implements Comparator { + public static final KeyComparator INSTANCE = new KeyComparator(); + + @Override + public int compare(String a, String b) { + int i = 0; + int j = 0; + while (i < a.length() && j < b.length()) { + if (Character.isDigit(a.charAt(i)) && Character.isDigit(b.charAt(j))) { + // handle number part + int num1 = 0; + int num2 = 0; + while (i < a.length() && Character.isDigit(a.charAt(i))) { + num1 = num1 * 10 + (a.charAt(i++) - '0'); + } + while (j < b.length() && Character.isDigit(b.charAt(j))) { + num2 = num2 * 10 + (b.charAt(j++) - '0'); + } + if (num1 != num2) { + return num1 - num2; + } + } else if (a.charAt(i) != b.charAt(j)) { + // handle non-number part + return a.charAt(i) - b.charAt(j); + } else { + i++; + j++; + } + } + return a.length() - b.length(); + } + } + @Override public void acquireReadLock() { this.rwl.readLock().lock(); diff --git a/application/src/test/java/run/halo/app/extension/index/IndexEntryImplTest.java b/application/src/test/java/run/halo/app/extension/index/IndexEntryImplTest.java index 479b9cf313..2b0f40605b 100644 --- a/application/src/test/java/run/halo/app/extension/index/IndexEntryImplTest.java +++ b/application/src/test/java/run/halo/app/extension/index/IndexEntryImplTest.java @@ -1,7 +1,10 @@ package run.halo.app.extension.index; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -105,4 +108,66 @@ void keyOrder() { Map.entry("slug-4", "fake-name-3")); assertThat(entry2.indexedKeys()).containsSequence("slug-1", "slug-2", "slug-3", "slug-4"); } + + @Test + void keyComparator() { + var comparator = IndexEntryImpl.KeyComparator.INSTANCE; + String[] strings = {"103", "101", "102", "1011", "1013", "1021", "1022", "1012", "1023"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo( + new String[] {"101", "102", "103", "1011", "1012", "1013", "1021", "1022", "1023"}); + + Arrays.sort(strings, comparator.reversed()); + assertThat(strings).isEqualTo( + new String[] {"1023", "1022", "1021", "1013", "1012", "1011", "103", "102", "101"}); + + // but if we use natural order, the result is: + Arrays.sort(strings, Comparator.naturalOrder()); + assertThat(strings).isEqualTo( + new String[] {"101", "1011", "1012", "1013", "102", "1021", "1022", "1023", "103"}); + } + + @Test + void keyComparator2() { + var comparator = IndexEntryImpl.KeyComparator.INSTANCE; + String[] strings = + {"moment-101", "moment-102", "moment-103", "moment-1011", "moment-1013", "moment-1021", + "moment-1022", "moment-1012", "moment-1023"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo(new String[] {"moment-101", "moment-102", "moment-103", + "moment-1011", "moment-1012", "moment-1013", "moment-1021", "moment-1022", + "moment-1023"}); + + // 测试日期排序 + strings = + new String[] {"2022-01-15", "2022-02-01", "2021-12-25", "2022-01-01", "2022-01-02"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo( + new String[] {"2021-12-25", "2022-01-01", "2022-01-02", "2022-01-15", "2022-02-01"}); + + // 测试字母和数字混合排序 + strings = new String[] {"abc123", "abc45", "abc9", "abc100", "abc20"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo( + new String[] {"abc9", "abc20", "abc45", "abc100", "abc123"}); + + // 测试纯字母排序 + strings = new String[] {"xyz", "abc", "def", "abcde", "xyzabc"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo(new String[] {"abc", "abcde", "def", "xyz", "xyzabc"}); + + // 测试空字符串 + strings = new String[] {"", "abc", "123", "xyz"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo(new String[] {"", "123", "abc", "xyz"}); + + // 测试相同字符串 + strings = new String[] {"abc", "abc", "abc", "abc"}; + Arrays.sort(strings, comparator); + assertThat(strings).isEqualTo(new String[] {"abc", "abc", "abc", "abc"}); + + // 测试 null 元素 + assertThatThrownBy(() -> Arrays.sort(new String[] {null, "abc", "123", "xyz"}, comparator)) + .isInstanceOf(NullPointerException.class); + } }