自定义排序
C 和 C++ 应用程序支持自定义排序规则,这为处理多字符组合提供了便利。例如,在丹麦语和挪威语中,“AE”代表“labor lapsus”,在古英语中表示“ash”,而在德语中,“OE”是“O-umlaut”的另一种形式。这些多字符组合在某些字符集中被视为单个字母,因此在对包含这些字符的字符串进行排序时,可能会遇到一些挑战。为了确保排序准确,算法需要同时比较多个字符。
此外,大小写也是一个需要注意的方面。有时我们需要区分大小写,比如小写字母“a-z”会排在大写字母“Z”之后;而更多时候,我们希望不区分大小写,即“a”跟在“A”之后,“b”跟在“B”之后。为了实现这一点,我们可以将大写和小写字母视为等效,或者在比较之前统一转换为同一格式,也可以通过指定字符集来实现这一目标。
SmartEDB 支持多种排序规则,允许在同一数据库中混合使用不同字符集或排序规则的字符串和字符数组。开发者可以在应用程序级别指定所需的字符集和排序规则,以满足不同的需求。SmartEDB 的数据定义语言(DDL)还为字符串字段上的索引提供了灵活的排序规则声明,方便用户根据具体需求进行配置。
[unique] tree<string_field_name_1 [collate C1]
[, string_field_name_2 [collate C2]], …> index_name;
hash<string_field_name_1 [collate C1]
[, string_field_name_2 [collate C2]], …> index_name;
如果未为索引组件显式指定排序规则,则使用默认排序规则。根据 DDL 声明,对于每个排序规则,DDL 编译器将为使用此排序规则的树索引和/或哈希索引生成以下比较函数占位符:
int2 collation_name_collation_compare ( mco_collate_h c1, uint2 len1,
mco_collate_h c2, uint2 len2 );
{
/* TODO: add your implementation here */
return 0;
}
mco_hash_counter_t collation_name_collation_hash (mco_collate_h c, uint2 len)
{
/* TODO: add your implementation here */
return (mco_hash_counter_t)0;
}
对于每个已定义的排序规则,都会生成一个单独的 API。比较函数的实际实现,包括字符集的定义,是应用程序的责任。为了便于比较函数的实现,SmartEDB 提供了以下一组函数:
mco_collate_get_char(mco_collate_h s, char *buf, uint2 len);
mco_collate_get_nchar(mco_collate_h s, nchar_t *buf, uint2 len);
mco_collate_get_wchar(mco_collate_h s, wchar_t *buf, uint2 len);
mco_collate_get_char_range(mco_collate_h s, char *buf,
uint2 from, uint2 len);
mco_collate_get_nchar_range(mco_collate_h s, nchar_t *buf,
uint2 from, uint2 len);
mco_collate_get_wchar_range(mco_collate_h s, wchar_t *buf,
uint2 from, uint2 len);
请注意,需要三个不同版本的 mco_collate_get_*char()
和 mco_collate_get_*char_range()
函数,这是因为为了使用相同的排序规则,参数必须与所访问字段的类型相对应。换句话说:对于类型为 string 和 char<n> 的字段,将调用 *char
版本的 mco_collate_get_char()
;对于类型为 nstring 和 nchar<n> 的字段,将调用 *nchar
版本;对于类型为 wstring 和 wchar<n> 的字段,将调用 *wchar()
版本。
C/C++ 应用程序通过以下函数注册用户定义的排序规则:
mco_db_register_collations(dbname, mydb_get_collations());
在使用 mco_db_connect()
或 mco_db_connect_ctx()
进行数据库连接之前,请务必先调用此函数。每个访问共享内存数据库的进程都需要调用一次该函数,以确保正确初始化。mydb_get_collations()
函数是特定于数据库的,类似于由 DDL 编译器生成的 mydb_get_dictionary()
函数,可以在 mydb.h 和 mydb.c 文件中找到。此外,DDL 编译器还会在 mydb_coll.c 文件中生成排序规则比较函数的模板。(请注意,如果 mydb_coll.c 文件已存在,编译器会显示警告并生成一个名为 mydb_coll.c.new 的新文件。)
温馨提示:用户定义的索引和排序规则有什么区别呢?
用户定义的索引通过自定义的比较和哈希函数实现,这些函数接收对象,并可以根据需要灵活地比较键字段。相比之下,排序规则仅适用于字符字段(如字符串、char<>、nstring、nchar<>、wstring 和 wchar<>),并且键段按照模式中定义的顺序进行比较。
从实现角度来看,排序规则要简单得多。用户定义的索引需要为树形索引实现对象到对象以及对象到键的函数,为哈希索引实现 hash_object
和 hash_external_key
函数,而排序规则只需为每个排序规则实现一个比较函数即可。此外,相同的排序规则可以在不同的类和索引中重复使用。例如,对于不区分大小写的排序规则,您只需实现一个函数即可。
排序示例
示例1
schema1.mco:
declare database mydb;
class A
{
string name;
tree <name collate Cname> tname;
};
键值 collate 声明要在字符串字段 name 上生成树索引 tname,并使用排序规则 Cname。此 DDL 指示数据库运行时使用名为 Cname 的自定义规则来比较字符串字段 name。请注意,相同的排序规则(规则)可以在同一索引中多次使用,在同一类中的不同索引中使用,或者在不同类中使用。
示例2
schema2.mco:
declare database mydb;
class A
{
string s;
char<20> c;
tree <s collate C1> sidx;
hash <c collate C1> cidx[1000]; /* CORRECT: string and char<20> can be
used with the same collation C1 */
};
class B
{
string s;
nchar<20> nc;
tree<s collate C1> sidx;
// tree<nc collate C1> ncidx; /* INCORRECT: string and nchar<N> can’t
be used with the same collation */
tree<nc collate C2> ncidx2; /* CORRECT – different collation, C2 */
}
请注意,在 A 类中,相同的排序规则(C1)可以在树索引和哈希索引中使用,而在 B 类中,由于其基字段 nc 的类型为 nchar,因此必须定义一个新的排序规则(C2)。若要在树索引中使用排序规则 C1,应用程序必须实现具有以下签名的比较函数: 类型定义为指向函数的指针,该函数接受四个参数:mco_collate_h 类型的 c1 和 c2 以及 uint2 类型的 len1 和 len2,并返回 int2 类型的值。
typedef int2 (*mco_compare_collation_f) ( mco_collate_h c1, uint2 len1,
mco_collate_h c2, uint2 len2);
参数为排序描述符(作为字符串)c1 和 c2 以及它们的长度(符号数量)len1 和 len2。比较函数必须返回一个整数值,以指示字符串的比较结果:如果 c1 < c2 则返回负数,如果 c1 == c2 则返回零,如果 c1 > c2 则返回正数。
此函数由运行时调用,用于比较两个对象中的字段值以及将字段值与外部键值进行比较。如果在哈希索引中使用排序规则,如类 A 中的 C1,应用程序必须实现具有以下签名的哈希函数:
typedef uint4 (*mco_hash_collation_f) ( mco_collate_h c, uint2 len);
参数为描述符 c(作为字符串)及其长度(符号数量)len。该函数必须返回字符串的整数哈希码。
请注意,如果比较函数对两个字符串 X 和 Y 返回零,即 X 等于 Y,则哈希函数必须为 X 和 Y 生成相同的哈希码。
对于示例模式 schema2.mco,DDL 编译器在 mydb_coll.c 中生成了这些比较函数的存根:
/* collation compare function */
int2 C1_collation_compare ( mco_collate_h c1, uint2 len1,
mco_collate_h c2, uint2 len2)
{
/* TODO: add your implementation here */
return 0;
}
uint4 C1_collation_hash (mco_collate_h c, uint2 len)
{
/* TODO: add your implementation here */
return 0;
}
/* collation compare function */
int2 C2_collation_compare ( mco_collate_h c1, uint2 len1,
mco_collate_h c2, uint2 len2)
{
/* TODO: add your implementation here */
return 0;
}
DDL 编译器还会生成函数应用,这些函数将用于在 mydb.h 和 mydb.c 中将指定的排序规则注册到 SmartEDB 数据库运行时:
mco_collation_funcs_h mydb_get_collations(void);
示例3
使用“不区分大小写”排序树索引的 C/C++ 示例:
schema3.mco:
declare database colldb;
class Record
{
string name;
unsigned<4> value;
unique tree <name> tstd;
unique tree <name collate C1> tcoll;
};
应用程序代码片段:
char * fruits[] = {
"banana", "PEAR", "plum", "Peach", "apricot", "Kiwi",
"QUINCE", "pineapple", "Lemon", "orange", "apple",
"pawpaw", "Fig", "mango", "MANDARIN", "Persimmon",
"Grapefruit", 0
};
/* collation compare function */
int2 C1_collation_compare ( mco_collate_h c1, uint2 len1,
mco_collate_h c2, uint2 len2)
{
char buf1[16], buf2[16];
mco_collate_get_char(c1, buf1, sizeof(buf1));
mco_collate_get_char(c2, buf2, sizeof(buf2));
// perform case-insensitive compare
return stricmp(buf1, buf2);
}
int main(void)
{
MCO_RET rc;
mco_db_h db = 0;
mco_trans_h t;
mco_cursor_t c;
uint2 len;
char buf[16];
...
/* open the database */
/* register the custom compare & hash functions */
mco_db_register_collations(db_name, colldb_get_collations());
/* connect to database */
rc = mco_db_connect(db_name, &db);
if ( MCO_S_OK == rc )
{
/* fill database with records setting field s to fruit names */
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND,&t);
if (rc == MCO_S_OK)
{
/* using custom collate tree index iterate through the cursor */
rc = Record_tcoll_index_cursor(t, &c);
if (rc == MCO_S_OK)
{
for (rc = mco_cursor_first(t, &c);
MCO_S_OK == rc;
rc = mco_cursor_next(t, &c))
{
Record_from_cursor(t, &c, &rec);
Record_s_get(&rec, buf, 11, &len);
printf("\n\t%-15s", buf);
}
rc = mco_trans_commit(t);
}
}
...
}
}
请注意,主应用程序要实现专门的字符串排序规则,只需在连接数据库之前注册该排序规则即可。排序逻辑由排序规则的比较函数处理。在这种情况下,比较逻辑只是返回不区分大小写的 C 运行时函数 stricmp() 的返回值。