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. 按日期排序¶
将日期属性存储在人类可读的格式(即作为 字符串)中,但仍然按日期排序可能很方便。这可以通过在 emit()
函数中将日期转换为 数字 来完成。例如,给定一个具有 'Wed Jul 23 16:29:21 +0100 2013'
的 created_at 属性的文档,以下 emit 函数将按日期排序
emit(Date.parse(doc.created_at).getTime(), null);
或者,如果您使用按字典顺序排序的日期格式,例如 "2013/06/09 13:52:11 +0000"
,您可以直接
emit(doc.created_at, null);
并避免转换。作为奖励,此日期格式与 JavaScript 日期解析器兼容,因此您可以在客户端 JavaScript 中使用 new Date(doc.created_at)
使浏览器中的日期排序变得容易。
3.2.2.4. 字符串范围¶
如果您需要包含具有给定前缀的所有字符串的 start 和 end 键,最好使用高值 Unicode 字符,而不是使用 'ZZZZ'
后缀。
也就是说,而不是
startkey="abc"&endkey="abcZZZZZZZZZ"
您应该使用
startkey="abc"&endkey="abc\ufff0"
3.2.2.5. 排序规范¶
本节基于 view_collation.js 中的 view_collation 函数。
// 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 排序算法,对键进行字典排序。如果您期望 ASCII 排序,这可能会产生令人惊讶的结果。请注意
所有符号都排在数字和字母之前(即使是“高”符号,如波浪号,
0x7e
)不考虑大小写比较不同的字母序列,因此
a < aa
但也有A < aa
和a < 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 视图是一个特例,因为它对文档 ID 使用 ASCII 排序,而不是 UCA
startkey="_design/"&endkey="_design/ZZZZZZZZ"
将找不到 _design/abc
,因为在 ASCII 序列中 ‘Z’ 位于 ‘a’ 之前。更好的解决方案是
startkey="_design/"&endkey="_design0"
3.2.2.7. 原生排序¶
为了从视图中挤出更多性能,您可以为原生 Erlang 排序指定 "options":{"collation":"raw"}
,尤其是在您不需要 UCA 的情况下。这将提供不同的排序顺序
1
false
null
true
{"a":"a"},
["a"]
"a"
请注意,{}
不再是合适的“高”键哨兵值。请改用类似 "\ufff0"
的字符串。