【问题标题】:Cloud Firestore Case Insensitive Sorting Using QueryCloud Firestore 使用查询进行不区分大小写的排序
【发布时间】:2018-06-14 05:32:09
【问题描述】:

我尝试使用 OrderBy 从 Cloud Firestore 读取排序数据。 Firestore 按以下顺序返回数据:

AAA
BBB
aaa
bbb

现在,我想要的是如下内容:

AAA
aaa
BBB
bbb

我希望这个结果只使用 OrderBy 而不是手动排序。
有没有办法在 Firestore 中进行这样的排序?


请为此提供解决方案。

提前致谢。

【问题讨论】:

    标签: javascript firebase google-cloud-firestore


    【解决方案1】:

    Cloud Firestore 中的排序区分大小写。没有标志可以使排序忽略大小写。

    实现您的用例的唯一方法是将该字段存储两次。

    假设您存储“AAA”和“aaa”的字段名为myData。在您的客户端代码中,您需要存储名为 myData_insensitive 的第二个字段,您可以在其中存储不区分大小写的数据副本。

    DocA:
    -> myData = 'AAA'
    -> myData_insensitive = 'AAA'
    
    DocB:
    -> myData = 'aaa'
    -> myData_insensitive = 'AAA'
    
    DocC:
    -> myData = 'BBB'
    -> myData_insensitive = 'BBB'
    
    DocD:
    -> myData = 'bbb'
    -> myData_insensitive = 'BBB'
    

    现在您可以通过myData_insensitive 查询和/或订购,但显示myData

    关于这个领域的两个有趣的事情是:

    1. 使用 Unicode,删除大小写比 'toLowerCase' 更复杂
    2. 不同的人类语言会对相同的字符进行不同的排序

    在不为每个排序规则创建单独的索引来解决 (2) 的情况下,处理 (1) 的一种实现方法是通过大小写折叠。如果你只想支持现代浏览器版本,那么下面给你一个 JavaScript 例子:

    caseFoldNormalize = function (s){
      return s.normalize('NFKC').toLowerCase().toUpperCase().toLowerCase()
    };
    caseFoldDoc = function(doc, field_options) {
      // Case fold desired document fields
      if (field_options != null) {
        for (var field in field_options) {
          if (field_options.hasOwnProperty(field)) {
            switch(field_options[field]) {
              case 'case_fold':
                if (doc.hasOwnProperty(field) && Object.prototype.toString.call(doc[field]) === "[object String]") {
                  doc[field.concat("_insensitive")] = caseFoldNormalize(doc[field])
                }
                break;
            }
          }
        }
      }
      return doc;
    }
    
    var raw_document = {
      name: "Los Angeles",
      state: "CA",
      country: "USA",
      structure: 'Waſſerſchloß',
      message: 'quıt quit' // Notice the different i's
    };
    
    var field_options = {
      name: 'case_fold',
      country: 'case_fold',
      structure: 'case_fold',
      message: 'case_fold'
    }
    
    var firestore_document = caseFoldDoc(raw_document, field_options);
    
    db.collection("cities").doc("LA").set(firestore_document).then(function() {
      console.log("Document successfully written!");
    }).catch(function(error) {
      console.error("Error writing document: ", error);
    });
    

    这将在 Cloud Firestore 中为您提供一个包含以下字段的文档:

    { 
     "name": "Los Angeles", 
     "state": "CA", 
     "country": "USA", 
     "structure": "Waſſerſchloß", 
     "message": "quıt quit", 
     "name_casefold": "los angeles", 
     "country_casefold": "usa", 
     "structure_casefold": "wasserschloss", 
     "message_casefold": "quit quit"
    }
    

    要处理旧版浏览器,您可以在How do I make toLowerCase() and toUpperCase() consistent across browsers 中看到一种解决方案

    【讨论】:

    • 越用firebase越不想用,这么简单的东西怎么又这么没必要复杂了
    • 简直难以置信!
    • 如果我们想添加对现有集合中包含大量文档的字段的查询或排序的支持呢????所以我们需要扫描所有文档并添加小写或每个大写的额外字段?
    【解决方案2】:

    您也可以在获得结果后手动进行:

    docArray.sort((a, b) => {
      if (a.myData.toLowerCase() < b.myData.toLowerCase()) {
        return -1;
      }
      if (a.myData.toLowerCase() > b.myData.toLowerCase()) {
        return 1;
      }
      return 0;
    });
    

    【讨论】:

    • 甚至更短:docArray.sort((a, b) =&gt; { return a.myData.localeCompare(b.myData) })
    • 这适用于小型集合,但如果您有很多并且在查询中使用分页,则应在返回给客户端之前对结果进行排序。
    猜你喜欢
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 2015-10-29
    • 2016-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多