【问题标题】:How can I build a comparator that sorts Strings the same way that PostgreSQL does?如何构建一个与 PostgreSQL 一样对字符串进行排序的比较器?
【发布时间】:2018-07-09 17:34:47
【问题描述】:

我正在编写一个集成测试,它将复杂的order by 传递给 PostgreSQL,然后检查数据是否以正确的顺序返回。我正在用 Java 编写这个集成测试,它的 String.compareTo 方法似乎对事物的排序与 PostgreSQL 不同。我在我的 PostgreSQL 数据库上运行了这个:

SELECT regexp_split_to_table('D d a A c b', ' ') ORDER BY 1;

它是这样回应的:

a
A
b
c
d
D

然后我创建了这个单元测试,以将其与 Java 对事物进行排序的方式进行比较:

import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import org.junit.Test;

import java.util.List;

import static junit.framework.Assert.assertEquals;

public class PostgresqlSortOrderTest {

    @Test
    public void whenJavaSortsStringsThenItIsTheSameAsWhenPostgresqlSortsStrings() {
        List<String> postgresqlOrder = Lists.newArrayList("a", "A", "b", "c", "d", "D");
        Ordering<String> ordering = new Ordering<String>() {
            @Override
            public int compare(String left, String right) {

                return left.compareTo(right);
            }
        };
        List<String> javaOrdering = ordering.sortedCopy(postgresqlOrder);
        assertEquals(postgresqlOrder, javaOrdering);
    }

}

此输出失败:

Expected :[a, A, b, c, d, D]  //postgresql
Actual   :[A, D, a, b, c, d]  //java

我对这里的术语一无所知。我想知道这些不同字符串类型的名称,以便更好地交流。但更重要的是,我怎样才能让 Java 像 PostgreSQL 一样排序呢?

【问题讨论】:

  • Java 以 ASCII 顺序执行。 PG 似乎按字母顺序排列,同一字母的小写和大写按该顺序排序(小写
  • @DaveNewton 是的,但是如果那里有一个已经有这样一个比较器的库,我宁愿使用它而不是自己编写。你知道一个吗?
  • 是的,String.CASE_INSENSITIVE_ORDER,在标准 API 中。

标签: java string postgresql sorting


【解决方案1】:

迟到会给出答案,但恐怕一个简单的不区分大小写的搜索不一定能达到你想要的效果。

您在搜索中想要的关键字是collation(更广泛地说是locales),PostgreSQL 依赖底层操作系统来提供支持。排序很少是简单的逐个字符比较。例如,在许多语言环境中,空格会被忽略(在 en_GB 中肯定是这种情况)。

此外,这意味着您最终可能会在不同平台上获得不同的排序顺序(取决于 Apple 或 Microsoft 是否同意 Linus 对您所在国家/地区的默认排序)。

关于包含 BSD 许可的库以提供跨平台的一致排序集是否有意义,已经进行了一些讨论。但是,这是大量工作,这意味着您最终可能会在数据库中与操作系统的其余部分进行不同的排序。虽然不同的供应商在如何处理这个问题上存在分歧,但恐怕没有一个简单的解决方案。

您可能想研究“传统”排序的“C”排序规则。恐怕我无法评论 Java 对正确区域设置排序的处理——不是我的领域。

【讨论】:

  • +1 就像一个仅供参考的 Java 以完全相同的方式处理它。它是基于语言环境的,您可以使用 Collator 类。
  • @BrianRoach 我想我需要那个。我的集成测试在本地通过String.CASE_INSENSITIVE_ORDER,但是一旦它在我们的 CI(不同的操作系统和可能的语言环境)上运行,它就失败了。不过,我不知道如何为默认语言环境获取不区分大小写的整理器。你知道怎么做吗?
  • @tieTYT - 这就是问题所在;您正试图依赖两个不同系统的行为,这些行为是基于区域设置的,跨平台。您要么需要明确地将其管理到它们匹配的位置,要么选择一端并让它完成工作。如果是我,并且我需要在查询后在数据库之外进行插入排序(我认为这就是问题所在),我只会在 Java 端进行排序。这里有一个使用 Java 排序规则的教程:docs.oracle.com/javase/tutorial/i18n/text/collationintro.html 另一种选择可能是重新考虑解决问题的方法。
【解决方案2】:

使用比较器:String.CASE_INSENSITIVE_ORDERStrings 中的任何Collection 进行排序。它已经在String 类中实现。

查看字段摘要http://docs.oracle.com/javase/6/docs/api/java/lang/String.html

【讨论】:

  • 值得注意的是,postgres 中的 ORDER BY 依赖于语言环境,就像 Java 一样。例如,我的机器上的 Postgres 9.3 响应 [A, D, a, b, c, d],如果你这样做 SELECT regexp_split_to_table('D d a A c b', ' ') ORDER BY 1;
【解决方案3】:

这是一个使用 en_GB 语言环境匹配 PostgreSQL 排序的比较器:

Comparator<String> comparator = (left, right) -> {
    Collator collator = Collator.getInstance(Locale.UK);
    collator.setStrength(Collator.PRIMARY);
    return collator.compare(left.replaceAll("\\p{Punct}", ""), right.replaceAll("\\p{Punct}", ""));
};

【讨论】:

    猜你喜欢
    • 2022-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-01
    • 2011-10-05
    • 1970-01-01
    相关资源
    最近更新 更多