3.2.2. 视图排序

3.2.2.1. 基础

视图函数指定要为每行返回的键和值。CouchDB按此键整理视图行。在下面的示例中 LastName 属性作为键,因此结果将按 LastName

function(doc) {
    if (doc.Type == "customer") {
        emit(doc.LastName, {FirstName: doc.FirstName, Address: doc.Address});
    }
}

CouchDB允许将任意JSON结构用作键。可以使用JSON数组作为键对排序和分组进行细粒度控制。

3.2.2.2. 实例

下面的巧妙技巧将同时返回客户和订单文档。密钥由一个客户组成 _id 还有一个分类标志。因为订单文档的键以 _id 对于客户文档,所有订单将按客户排序。因为客户的排序令牌低于订单的排序令牌,所以客户文档将在关联的订单之前出现。排序标记的值0和1是任意的。

function(doc) {
    if (doc.Type == "customer") {
        emit([doc._id, 0], null);
    } else if (doc.Type == "order") {
        emit([doc.customer_id, 1], null);
    }
}

列出特定客户 _id XYZ以及该客户的所有订单都将startkey和endkey的范围限制为仅覆盖该客户的文档 _id ::

startkey=["XYZ"]&endkey=["XYZ", {}]

不建议在视图中发出文档本身。相反,要在请求视图时包含文档主体,请使用 ?include_docs=true .

3.2.2.3. 按日期排序

以人类可读的格式存储日期属性可能比较方便(例如,作为 string ),但仍按日期排序。这可以通过将日期转换为 numberemit() 功能。例如,给定一个具有created_at属性的文档 'Wed Jul 23 16:29:21 +0100 2013' ,以下emit函数将按日期排序:

emit(Date.parse(doc.created_at).getTime(), null);

或者,如果使用按字典顺序排序的日期格式,例如 "2013/06/09 13:52:11 +0000" 你可以

emit(doc.created_at, null);

避免转换。另外,这个日期格式与JavaScript日期解析器兼容,因此您可以使用 new Date(doc.created_at) 在客户端JavaScript中,使日期排序在浏览器中更容易。

3.2.2.4. 字符串范围

如果需要用给定前缀包含每个字符串的起始键和结束键,则最好使用高值Unicode字符,而不是使用 'ZZZZ' 后缀。

也就是说,而不是:

startkey="abc"&endkey="abcZZZZZZZZZ"

你应该使用:

startkey="abc"&endkey="abc\ufff0"

3.2.2.5. 校对规范

本节基于中的view_collation函数 view_collation.js

// special values sort before all other types
null
false
true

// then numbers
1
2
3.0
4

// then text, case sensitive
"a"
"A"
"aa"
"b"
"B"
"ba"
"bb"

// then arrays. compared element by element until different.
// Longer arrays sort after their prefixes
["a"]
["b"]
["b","c"]
["b","c", "a"]
["b","d"]
["b","d", "e"]

// then object, compares each key value in the list until different.
// larger objects sort after their subset objects.
{a:1}
{a:2}
{b:1}
{b:2}
{b:2, a:1} // Member order does matter for collation.
           // CouchDB preserves member order
           // but doesn't require that clients will.
           // this test might fail if used with a js engine
           // that doesn't preserve order
{b:2, c:2}

字符串的比较使用 ICU 它实现了 Unicode Collation Algorithm ,给字典排序键。如果您希望使用ASCII排序,这可能会产生令人惊讶的结果。请注意:

  • 所有符号都在数字和字母之前排序(即使是像颚化符这样的“高位”符号, 0x7e
  • 不同字母序列的比较不考虑大小写,因此 a < aa 而且 A < aaa < AA
  • 相同的字母序列是按大小写比较的,大写之前是小写,所以 a < A

您可以演示7位ASCII字符的排序顺序,如下所示:

require 'rubygems'
require 'restclient'
require 'json'

DB="http://127.0.0.1:5984/collator"

RestClient.delete DB rescue nil
RestClient.put "#{DB}",""

(32..126).each do |c|
    RestClient.put "#{DB}/#{c.to_s(16)}", {"x"=>c.chr}.to_json
end

RestClient.put "#{DB}/_design/test", <<EOS
{
    "views":{
        "one":{
            "map":"function (doc) { emit(doc.x,null); }"
        }
    }
}
EOS

puts RestClient.get("#{DB}/_design/test/_view/one")

这将显示排序规则序列:

` ^ _ - , ; : ! ? . ' " ( ) [ ] { } @ * / \ & # % + < = > | ~ $ 0 1 2 3 4 5 6 7 8 9
a A b B c C d D e E f F g G h H i I j J k K l L m M n N o O p P q Q r R s S t T u U v V w W x X y Y z Z

3.2.2.5.1. 关键范围

查询键范围时要特别小心。例如:查询:

startkey="Abc"&endkey="AbcZZZZ"

将匹配“ABC”和“abc1”,但不匹配“ABC”。这是因为UCA的分类如下:

abc < Abc < ABC < abc1 < AbcZZZZZ

对于大多数应用程序,为了避免问题,应该将 startkey ::

startkey="abc"&endkey="abcZZZZZZZZ"

将匹配以开头的所有键 [aA][bB][cC]

3.2.2.5.2. 复杂键

质询 startkey=["foo"]&endkey=["foo",{{}}] 将大多数数组键与第一个元素中的“foo”匹配,例如 ["foo","bar"]["foo",["bar","baz"]] . 但是它不匹配 ["foo",{{"an":"object"}}]

3.2.2.6. _all_docs

这个 _all_docs 视图是一种特殊情况,因为它对doc id使用ASCII排序规则,而不是UCA::

startkey="_design/"&endkey="_design/ZZZZZZZZ"

找不到 _design/abc 因为 'Z' 在前面 'a' 在ASCII序列中。更好的解决方案是:

startkey="_design/"&endkey="_design0"

3.2.2.7. 原始排序规则

要从视图中挤出更多的性能,可以指定 "options":{{"collation":"raw"}} 在本地Erlang排序规则的视图定义中,尤其是在不需要UCA的情况下。这将提供不同的排序顺序:

1
false
null
true
{"a":"a"},
["a"]
"a"

当心那个 {{}} 不再是一个合适的“高”键哨兵值。使用一个字符串 "\ufff0" 相反。