索引搜索
搜索接口通过唯一标识符或索引定位所需的对象或对象组。
使用
_find()
函数通过唯一标识符(包括唯一哈希、oid 和 autoid)进行精确匹配查找,对于定位单个对象极其高效。SmartEDB 还提供了一套丰富的专用索引,这些索引使用游标在一组对象中导航,作为有序结果集(包括 Patricia、R树、KD树和 三元树)或无序对象序列(使用非唯一哈希或列表索引)。
C API 还允许自定义用户定义的索引。
查找
生成的 _find()
函数会在索引中查找精确匹配项。根据定义,在唯一索引上进行精确匹配查找会返回恰好一个结果,如果没有找到匹配项则返回零个结果。因此,对于 C 应用程序,_find()
函数不需要游标;该函数通过输出参数返回对象句柄。
这些 _find()
函数是为声明了一个或多个唯一哈希索引或一个或多个唯一树索引的类生成的。
注意,oid和autoid根据定义是唯一的。因此,为它们实现了内部管理的唯一哈希索引,并且只有精确匹配_find()
函数适合于oid和autoid对象查找。
对于每个唯一索引,生成如下接口:
MCO_RET classname_index_name_find( /*IN*/ mco_trans_h trans,
/*IN*/ <type> [*]param1,
[/*IN*/ uint2 len1,]
[/*IN*/ <type> [*]param2,
[/*IN*/ uint2 len2,] …]
/*OUT*/ classname *handle
);
精确匹配查找示例
要演示精确匹配查找,参考以下数据库模式:
class Employee
{
string name;
uint2 dept_no;
unique tree<name> Iname;
};
要通过唯一的树索引Iname
查找Employee对象,可以使用如下代码:
Employee emp;
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if (MCO_S_OK == rc)
{
rc = Employee_Iname_find(t, search_name, (uint2)strlen(search_name), &emp);
if ( MCO_S_OK == rc)
{
// 处理Employee对象
}
rc = mco_trans_commit(t);
}
OID索引查找
oid只能进行“精确匹配”搜索,这是使用生成的_oid_find()
函数执行的。
请参考以下代码片段:
declare database mydb;
// Define the OID struct
struct oid_struct
{
uint4 a;
int1 b;
};
declare oid oid_struct[OID_SIZE];
class anObject
{
uint4 value;
// Declare the OID index
oid;
};
将生成以下_oid_find()
函数:
MCO_RET anObject_oid_find(
mco_trans_h t,
const mydb_oid *id,
/*OUT*/ A *handle
);
下面的代码片段演示了如何使用_oid_find()
函数执行“精确匹配”搜索:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
anObject rec;
oid_struct findObj;
findObj.a = 1;
findObj.b = 99;
rc = anObject_oid_find(t, &findObj, &rec);
if ( MCO_S_OK == rc )
{
// 处理找到的对象
}
mco_trans_rollback(t);
}
autoid索引查找
只有使用生成的_autoid_find()
函数执行的autoid索引才能进行“精确匹配”搜索。
请参考以下代码片段:
class Department
{
autoid[1000];
string name;
unique tree<name> Iname;
};
class Employee
{
string name;
autoid_t dept;
unique tree<name> Iname;
};
注意,Department对象和Employee对象之间的一对多关系可以通过Employee类中autoid_t
类型的引用字段dept
来实现。下面的autoid_find()
函数是为Department类生成的:
MCO_RET Department_autoid_find(
mco_trans_h t,
autoid_t autoid_key_,
/*OUT*/ Department *handle_
);
如果每个Employee对象存储了它所属的Department对象的autoid,那么可以使用如下代码快速找到所有者Department对象:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
rc = Employee_Iname_find(t, search_name, (uint2)strlen(search_name), &emp);
if (MCO_S_OK == rc)
{
Employee_dept_get(&emp, &dept_id1);
// 通过其autoid查找Department对象
rc = Department_autoid_find(t, dept_id1, &dept1);
if (MCO_S_OK == rc)
{
// 处理Department对象
}
}
mco_trans_rollback(t);
}
搜索
游标本质上是结果集中对象集合的迭代器。对于模式定义中为类声明的每个索引,DDL编译器会生成函数来实例化游标、根据某些值对其进行定位,并从游标获取数据库对象的句柄。基于列表和非唯一哈希的游标允许按顺序遍历(从第一个到最后,或从最后到第一个),尽管序列的具体顺序未定义;它仅提供了一种遍历类的无序对象列表的机制。模式文件中的列表或非唯一散列声明将导致DDL编译器生成相应的_search()
函数。
用于树索引的_search()
函数适用于以下场景:
- 在已知起始值的排序列表中建立一个起始位置,并可选择以升序或降序检索后续结果。
- 当只有部分起始值已知时,在排序列表中建立一个起始位置,找到最接近的匹配项,并可选地以升序或降序检索后续结果。
- 如上所述建立一个起始位置,以升序或降序迭代排序列表,直到达到下限或上限,使用_compare()函数确定何时达到范围限制。
用于非唯一哈希索引(允许重复)的_search()
函数适用于以下场景:
- 用已知的起始值建立一个起始位置,并遍历列表以检索每个副本,使用
_compare()
函数确定最后一个副本的检索时间。
为了获取具有声明索引的列表或字段的游标,生成了以下两个函数:
MCO_RET classname_list_cursor( /*IN*/ mco_trans_h t, /*OUT*/ mco_cursor_h c );
MCO_RET classname_indexname_index_cursor( /*IN*/ mco_trans_h t, /*OUT*/ mco_cursor_h c );
请注意
在调用上述函数之一后,必须使用游标搜索或定位函数正确定位游标,以获取有效的数据库对象句柄。
为所有树索引生成的_search()
函数格式如下:
MCO_RET classname_indexname_search(
/*IN*/ mco_trans_h trans,
/*INOUT*/ mco_cursor_h cursor,
/*IN*/ MCO_OPCODE op, // 在游标操作符代码中定义的比较操作
/*IN*/ [const] <type> [*]param1,
[[/*IN*/ uint2 len1,]
[/*IN*/ [const] <type> [*]param2,
[/*IN*/ uint2 len2,] …]
);
为所有非唯一哈希索引生成的_search()
函数的形式如下:
MCO_RET classname_indexname_search(
/*IN*/ mco_trans_h trans,
/*INOUT*/ mco_cursor_h cursor,
/*IN*/ [const] <type> key
);
在这种情况下不需要 MCO_OPCODE
,因为哈希索引搜索始终是精确匹配。
游标导航
使用 _search()
函数或其中一个游标定位函数(mco_cursor_first()
、mco_cursor_last()
、mco_cursor_next()
、mco_cursor_prev()
)定位游标后,会使用生成的 _from_cursor()
函数获取对象的句柄,以便随后使用诸如 _get()
或 _put()
之类的对象接口函数:
MCO_RET classname_from_cursor(
/*IN*/ mco_trans_h t,
/*IN*/ mco_cursor_h c,
/*OUT*/ classname *object
);
_locate()
函数用于根据对象引用定位树索引游标。该游标必须先前已通过 _index_cursor()
函数实例化。_locate()
函数仅适用于基于树的游标(即不适用于列表或哈希游标):
MCO_RET classname_indexname_locate(
/*IN*/ mco_trans_h t,
/*INOUT*/ mco_cursor_h c,
/*IN*/ classname * handle
);
SmartEDB树索引是应用程序内存中的树状结构,其中包含一组键。键可以是简单的(基于单个字段)或复合的(包含多个字段)。树索引按排序顺序存储键。为了支持排序,树索引必须有一个比较函数。无论键类型是什么(简单键还是复合键),函数都必须能够比较两个键,并确定第一个键是小于(返回-1)、等于(返回0)还是大于(返回1)第二个键。树索引算法根据这种比较对键进行排序。
树索引算法根据键的相对权重(由比较函数结果确定)而不是键的值对键进行排序。
为每个树索引生成的_compare()
函数具有以下形式:
MCO_RET classname_indexname_compare(
/*IN*/ mco_trans_h trans,
/*IN*/ mco_cursor_h cursor,
/*IN*/ [const] <type> [*]param1,
[[/*IN*/ uint2 len1,]
[/*IN*/ [const] <type> [*]param2,
[/*IN*/ uint2 len2,] …],
/*OUT*/ int *result
);
B树索引搜索
搜索算法页面通过两个示例演示了键权值如何确定树索引搜索和光标导航操作的结果。为了在C语言中实现第一个示例,树索引将被定义为单个uint4 字段,并定义以下比较函数:
int compare_uint4( uint4 a, uint4 b)
{
if ( a == b ) return 0;
if ( a > b ) return 1;
return -1;
};
对于第二个示例,键被表示为包含两个字段uint4 和char[16] 的结构体:
typedef struct
{
uint4 f1;
char f2[16];
} the_key;
compare
函数将上面示例中的整数和作为ASCII字符的字符缓冲区进行比较:
int compare_compound_key ( the_key a, the_key b )
{
int i;
if ( a.f1 < b.f1 ) return -1;
if ( a.f2 > b.f2 ) return 1;
for ( i=0; i<sizeof(the_key.f2); i++ )
{
if ( a.f2[i] < b.f[2] ) return -1;
if ( a.f2[i] > b.f[2] ) return 1;
}
return 0;
};
注
请参阅搜索算法页面,了解调用_search()
函数后如何确定光标位置,以及使用每种可能的游标操作代码调用mco_cursor_prev()
和mco_cursor_next()
的结果。
示例
演示一个常见的树索引搜索场景,请参考以下数据库模式:
class anObject
{
uint4 value;
tree <value> Idx; // Equivalent to "nonunique tree<value> Idx;"
};
注意,没有显式唯一限定符的树声明默认为非唯一的;即允许重复。要对索引Idx
执行搜索,可以使用如下代码:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
rc = anObject_Idx_index_cursor(t, &csr);
if ( MCO_S_OK == rc )
{
printf("\nObjects with value == %d : ", search_value);
for (rc = anObject_Idx_search(t, &csr, MCO_EQ, search_value);
MCO_S_OK == rc; rc = mco_cursor_next(t, &csr))
{
int result = 0;
/* 使用 _compare() 在值不相等的对象前停止 */
anObject_Idx_compare(t, &csr, search_value, &result);
if (result) break;
anObject_from_cursor(t, &csr, &obj);
anObject_value_get(&obj, &value);
printf("(%d) ", value);
}
}
mco_trans_rollback(t);
}
请注意,当它返回 MCO_S_OK
时,生成的 _search()
函数会将游标定位在第一个与指定键(search_value
)匹配的索引值处。处理完第一个找到的对象后,会调用 mco_cursor_next()
来移动游标。在处理循环中,会调用生成的 _compare()
函数来确保当前对象的索引值仍等于指定的键(search_value
)。当比较函数返回非零值时,我们将退出处理循环。
常见错误
有时,C应用程序需要获取游标来遍历类并删除进程中的一些对象。游标包含对当前对象的引用。因此,如果当前对象被删除,游标将无法移动,数据库运行时将返回mcoe_cursor_invalid
错误码。要删除游标中的对象,应用程序需要首先移动游标,当游标不再指向目标对象时,删除该对象。请注意,无论游标对应的索引类型如何,都是如此。下面的伪代码演示了这种技术:
rc = open_cursor(cursor);
while (rc == MCO_S_OK && (rc = from_cursor(cursor,obj)) == MCO_S_OK)
{
rc = move_cursor(cursor);
delete_obj(obj);
}
模式搜索
如搜索页面所述,SmartEDB支持通配符模式匹配。为了说明生成的模式搜索函数的用法,请参考以下类定义:
class PatternTest
{
string key1;
char<20> key2;
int4 key3;
tree <key1,key2,key3> i1;
tree <key2,key3> i2;
};
模式编译器将为索引i1
生成以下函数:
MCO_RET PatternTest_i1_pattern_size(
const char *key1,
uint2 sizeof_key1,
const char *key2,
uint2 sizeof_key2,
int4 key3 /*OUT*/ uint4 *pattern_size);
MCO_RET PatternTest_i1_pattern_search(
mco_trans_h t,
void *allocated_pattern,
uint4 memsize,
PatternTest *obj,
const char *key1,
uint2 sizeof_key1,
const char *key2,
uint2 sizeof_key2,
int4 key3
);
MCO_RET PatternTest_i1_pattern_next(
mco_trans_h t,
void *allocated_pattern,
PatternTest *obj
);
使用i1
执行模式搜索,需要以下步骤:
首先,分配一个缓冲区,在模式搜索期间,SmartEDB运行时将其用作状态机。所需缓冲区的大小由 _pattern_size()
决定,例如:
char *key1 = "Gr?ve*";
char *key2 = S"*";
uint4 key3 = 46;
void *buf;
uint4 bsize;
PatternTest pat;
PatternTest_i1_pattern_size(key1, strlen(key1), key2, strlen(key2), key3, &bsize);
buf = malloc(bsize);
现在,检索与指定模式匹配的索引项的循环可以编码如下:
for( rc = PatternTest_i1_pattern_search( transaction, buf, bsize, &pat, key1,
strlen(key1), key2, strlen(key2), key3 );
rc == MCO_S_OK ;
rc = PatternTest_i1_pattern_next(transaction, buf, &pat)
{
...
}
free(buf);
模式搜索策略
模式搜索由运行时从mco_pattern_policy_t 结构中获得的信息控制,该结构可由应用程序自定义。以下函数使用模式策略结构来获取和设置模式匹配策略:
typedef struct mco_pattern_policy_t_
{
char ignore_other_fields; // default = MCO_NO
char any_char; // default = ‘*’
char one_char; // default = ‘?’
nchar_t any_nchar; // default = 0x002a
nchar_t one_nchar; // default = 0x003f
#ifdef MCO_CFG_WCHAR_SUPPORT
wchar_t any_wchar; // default = (wchar_t)0x002a
wchar_t one_wchar; // default = (wchar_t)0x003f
#endif
};
void mco_get_default_pattern_policy(mco_pattern_policy_t * p);
MCO_RET mco_get_pattern_policy(mco_trans_h t, mco_pattern_policy_t *p);
MCO_RET mco_set_pattern_policy(mco_trans_h t, const mco_pattern_policy_t *p);
ignore_other_fields
元素控制是否在模式匹配期间考虑参与索引的非字符字段。在本节开头的示例中,定义了一个由两个字符字段和一个4字节整数字段组成的索引。通过设置ignore_other_fields = MCO_YES
,该索引可以用于匹配两个字符字段上的模式,而不考虑整数字段的值。
下面的代码片段演示了如何更改模式匹配策略:
mco_pattern_policy_t p;
/* 更改策略操作必须在一个读写事务中执行 */
mco_get_pattern_policy(trn, &p);
p.ignore_other_fields = MCO_YES;
mco_set_pattern_policy(trn, &p);
Patricia索引搜索
Patricia索引只能在单个字段(而不是多个字段)上声明,并且可以是唯一的;如果没有unique
关键字,则默认允许重复。
对Patricia索引的搜索使用生成的函数longest_match()
、exact_match()
、prefix_match()
和next_match()
执行。
注
请参阅Patricia索引页,了解这些特定于Patricia的搜索操作如何工作的解释和示例。
为了演示Patricia C API的使用,请参阅两个SDK示例:
- samples/code/05_Indexes_Patricia_Character
- samples/code/05_Indexes_Patricia_Binary
下面使用第二个示例中的代码片段来说明不同类型的Patricia索引搜索。考虑具有如下位数组和字符串字段的 AreaCode 类:
class AreaCode
{
vector<boolean> areaCode;
char<4> strAreaCode
patricia<areaCode> IareaCode;
};
顺序搜索
若要使用 Patricia 索引 IareaCode
依次列出数据库的内容,我们可以使用如下代码:
rc = mco_trans_start(db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &trn);
if ( MCO_S_OK == rc )
{
/* 初始化游标 */
rc = AreaCodeBool_IareaCode_index_cursor(trn, &csr);
if ( MCO_S_OK == rc )
{
for (rc = mco_cursor_first(trn, &csr); MCO_S_OK == rc;
rc = mco_cursor_next(trn, &csr))
{
rc = AreaCodeBool_from_cursor(trn, &csr, &areaCode);
prnObject(&areaCode);
}
}
rc = mco_trans_commit(trn);
}
注意,这里不需要_search()
函数,因为光标只是使用mco_cursor_first()
进行定位,数据库对象的顺序导航是通过调用函数mco_cursor_next()
来执行的。
在接下来的搜索示例中,生成的_next_match()
函数将用于移动光标位置,以确保结果集中的下一个对象仍然匹配给定的键值AreaCode
。
注意光标的作用mco_cursor_next()
或mco_cursor_prev()
可以用,而不是Patricia特殊函数_next_match()
,以遍历结果集。但这需要调用光标
比较函数确定索引是否仍然与键匹配。
在下面的代码片段中,函数calcBitLen()
和make_mask()
结合工作来计算位数组的大小和给定AreaCode 的十六进制值;而prnObject()
只是显示当前对象的AreaCode
和strAreaCode
值。
请参阅SDK示例 samples/code/05_Indexes_Patricia_Binary 了解实现细节。
精确匹配搜索
要执行“精确匹配”搜索,我们可以使用如下代码:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &trn);
if ( MCO_S_OK == rc )
{
rc = AreaCodeBool_IareaCode_index_cursor(trn, &csr);
sz = calcBitLen(AreaCode);
rc = AreaCodeBool_IareaCode_exact_match(trn, &csr, (char*)make_mask(AreaCode, sz), sz);
if ( MCO_S_OK == rc )
{
printf("\tFound ExactMatch for key %x:\n", AreaCode);
while ( MCO_S_OK == rc )
{
rc = AreaCodeBool_from_cursor(trn, &csr, &areaCode);
prnObject(&areaCode);
rc = AreaCodeBool_IareaCode_next_match(trn, &csr, (char*)make_mask(AreaCode, sz), sz);
}
}
else
{
printf("\tExactMatch not found for key %x:\n", AreaCode);
}
rc = mco_trans_commit(trn);
}
前缀匹配搜索
要执行“前缀匹配”搜索,我们可以使用如下代码:
mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &trn);
if ( MCO_S_OK == rc )
{
rc = AreaCodeBool_IareaCode_index_cursor(trn, &csr);
sz = calcBitLen(AreaCodePref);
rc = AreaCodeBool_IareaCode_prefix_match(trn, &csr, (char*)make_mask(key, sz), sz);
if ( MCO_S_OK == rc )
{
int found = 0;
while ( MCO_S_OK == rc )
{
if (!found)
{
printf("\tFound PrefixMatch for key %x:\n", AreaCodePref);
}
found = 1;
rc = AreaCodeBool_from_cursor(trn, &csr, &areaCode);
prnObject(&areaCode);
rc = AreaCodeBool_IareaCode_next_match(trn, &csr, (char*)make_mask(key, sz), sz);
}
if (!found)
{
printf("\tPrefixMatch not found for key %x:\n", AreaCodePref);
}
}
else
{
printf("\tPrefixMatch not found for key %x:\n", AreaCodePref);
}
rc = mco_trans_commit(trn);
}
最长匹配搜索
要执行“最长匹配”搜索,我们可以使用如下代码:
mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &trn);
if ( MCO_S_OK == rc )
{
rc = AreaCodeBool_IareaCode_index_cursor(trn, &csr);
sz = calcBitLen(AreaCodePref);
rc = AreaCodeBool_IareaCode_longest_match(trn, &csr, (char*)make_mask(key, sz), sz);
if ( MCO_S_OK == rc )
{
int found = 0;
while ( MCO_S_OK == rc )
{
if (!found)
{
printf("\tFound LongestMatch for key %x:\n", AreaCodePref);
}
found = 1;
rc = AreaCodeBool_from_cursor(trn, &csr, &areaCode);
prnObject(&areaCode);
rc = AreaCodeBool_IareaCode_next_match(trn, &csr, (char*)make_mask(key, sz), sz);
}
if (!found)
{
printf("\tLongestMatch not found for key %x:\n", AreaCodePref);
}
}
else
{
printf("\tLongestMatch not found for key %x:\n", AreaCodePref);
}
rc = mco_trans_commit(trn);
}
R树索引搜索
R树索引通常用于加速空间搜索。对R树索引的搜索使用生成的_search()
函数执行,该函数带有四个mco_opcode
之一:MCO_EQUAL
、MCO_OVERLAP
、MCO_CONTAIN
或MCO_NEIGHBORHOOD
。
:::请参阅R树索引页面,了解这些特定于RTree的搜索操作是如何工作的解释和示例。
为了演示R树 C API的使用,提供了SDK示例 samples/code/05_Indexes_rtree。
:::
下面使用这个示例中的代码片段来说明不同类型的R树索引搜索。考虑类rtreree_class,其定义如下:
class rtree_class
{
int2 square[4];
rtree <square> ridx;
};
顺序搜索
要使用R树索引ridx
顺序列出游标的内容,可以使用如下代码:
int iterate_rects(mco_trans_h t, mco_cursor_t *c, mco_bool reverse_order)
{
MCO_RET rc;
int i;
rtree_class obj;
int2 square[4];
rc = (reverse_order == MCO_NO) ? mco_cursor_first(t, c) : mco_cursor_last(t, c);
for (i = 0; MCO_S_OK == rc; i++)
{
rc = rtree_class_from_cursor(t, c, &obj);
rc = rtree_class_square_get_range(&obj, 0, 4, (int2*)square);
printf("\t%d. (%d,%d) - (%d,%d)\n", i + 1, square[0], square[1], square[2], square[3]);
rc = (reverse_order == MCO_NO) ? mco_cursor_next(t, c) : mco_cursor_prev(t, c);
}
return i;
}
注意
这里不需要_search()
函数,因为光标只是根据标志reverse_order
的值使用mco_cursor_first()
或mco_cursor_last()
进行定位,数据库对象的顺序导航是通过调用函数mco_cursor_next()
或mco_cursor_prev()
来执行的。
精确匹配搜索
为了执行“精确匹配”搜索,我们使用操作码MCO_EQ
,代码如下:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
mco_cursor_t c;
int2 rect[4] = { 25, 25, 50, 35 };
rc = rtree_class_ridx_index_cursor(t, &c);
rc = rtree_class_ridx_search(t, MCO_EQ, &c, (int2*)rect);
if ( MCO_S_OK == rc )
{
// 处理找到的矩形
}
rc = mco_trans_commit(t);
}
重叠搜索
为了找到与指定矩形重叠的矩形并迭代它们,我们使用操作码MCO_OVERLAP
,代码如下:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
mco_cursor_t c;
int2 rect[4] = { 25, 25, 50, 35 };
rc = rtree_class_ridx_index_cursor(t, &c);
rc = rtree_class_ridx_search(t, MCO_OVERLAP, &c, (int2*)rect);
if ( MCO_S_OK == rc )
{
iterate_rects(t, &c, MCO_NO); // 按顺序列出游标内容的iterate_rects() 函数。
}
rc = mco_trans_commit(t);
}
包含搜索
要查找包含指定矩形并对其进行迭代,我们使用操作码 MCO_CONTAIN
,代码如下:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
mco_cursor_t c;
int2 rect[4] = { 25, 25, 50, 35 };
rc = rtree_class_ridx_index_cursor(t, &c);
rc = rtree_class_ridx_search(t, MCO_CONTAIN, &c, (int2*)rect);
if ( MCO_S_OK == rc )
{
iterate_rects(t, &c, MCO_NO); // 按顺序列出游标内容的iterate_rects() 函数。
}
rc = mco_trans_commit(t);
}
邻域搜索
要按与指定点(高度和宽度均为 0 的矩形)的距离顺序列出所有矩形,我们使用操作码 MCO_NEIGHBOURHOOD
,代码如下:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
mco_cursor_t c;
int2 point[4] = { 10, 10, 10, 10 };
rc = rtree_class_ridx_index_cursor(t, &c);
rc = rtree_class_ridx_search(t, MCO_NEIGHBOURHOOD, &c, (int2*)point);
if ( MCO_S_OK == rc )
{
iterate_rects(t, &c, MCO_NO); // 按顺序列出游标内容的iterate_rects() 函数。
}
rc = mco_trans_commit(t);
}
KD树索引搜索
KD树索引非常适合多维键值搜索。对KD树索引的搜索是通过使用生成的 _search()
函数以示例查询的方式进行的,以定位符合给定搜索条件的对象。
为了演示KD树C API 的使用,提供了 SDK 示例 samples/code/05_Indexes_kdtree。
下面使用此示例中的代码片段来说明示例查询搜索。 考虑KD树索引页面中给出的数据库模式:
class Car
{
string vendor;
string model;
string color;
uint4 year;
uint4 mileage;
boolean automatic;
boolean ac;
uint4 price;
char<3> state;
string description;
kdtree <year, mileage, color, model, vendor, automatic, ac, price> index;
};
要执行示例查询搜索,如果进行精确匹配搜索,我们临时插入一个“模式对象”;若要指定范围,则插入两个边界“模式对象”(从和至)。然后我们调用生成的 _search()
函数,并使用游标函数 mco_cursor_next()
或 mco_cursor_prev()
来遍历结果集。
例如:
/* 使用读写事务将边界模式存储到数据库中 */
rc = mco_trans_start(db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &t);
if (rc == MCO_S_OK)
{
Car_new(t, &from); /* Create low boundary pattern object */
Car_new(t, &to); /* Create high boundary pattern object */
Car_vendor_put(&from, "Ford", 4); Car_vendor_put(&to, "Ford", 4);
Car_price_put(&to, 30000);
Car_year_put(&from, 2000); Car_year_put(&to, 2006);
Car_mileage_put(&to, 100000);
printf("\n\n\tRange results for:");
print_car("\t(from", &from );
print_car("\t(to", &to );
printf("\n");
Car_index_index_cursor(t, &cursor);
rc = Car_index_search(t, &cursor, &from, &to);
for ( i=1; MCO_S_OK == rc; i++ )
{
Car choice;
Car_from_cursor(t, &cursor, &choice);
sprintf( str, "%d", i );
print_car( str, &choice);
rc = mco_cursor_next(t, &cursor);
}
Car_delete(&from); /* Delete pattern */
Car_delete(&to); /* Delete pattern */
mco_trans_commit(t);
}
三元组索引搜索
当无法精确知道目标对象的确切拼写时,三元组索引是文本搜索的理想选择。
了解详细内容,请参阅索引和游标页。
哈希索引搜索
哈希索引通常用于高效查找对象。正如上文“查找”部分所述,在模式文件中声明非唯一的哈希索引将导致 DDL 编译器生成一个游标 _search()
函数,该函数允许在某个类的无序对象列表中按顺序(从首到尾或从尾到首)进行导航。
注
为了演示哈希 C API 的使用,提供了 SDK 示例 samples/code/05_Indexes_hash。
下面将使用此示例中的代码片段来说明非唯一哈希搜索。考虑在“索引和游标”页面中介绍的数据库模式:
class Record
{
uint4 iIdx; /* 索引 */
uint4 iSeries; /* 测量集 */
hash <iIdx> I_Index[10000];
nonunique hash <iSeries> I_Series[10000];
};
下面的代码片段演示了如何使用非唯一哈希索引来搜索具有指定哈希表值的所有对象:
/* 显示非唯一索引中具有指定值的所有记录 */
printf("\n\n\tSearch for records with iSeries == %d :", findValue);
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
/* 打开游标 */
rc = Record_I_Series_index_cursor(t, &csr);
if ( MCO_S_OK == rc )
{
/* 在 iSeries 中查找具有指定值的记录 */
rc = Record_I_Series_search(t, &csr, findValue);
if ( MCO_S_OK == rc )
{
Record rec;
/* 移至第一项 */
rc = mco_cursor_first(t, &csr);
/* 显示游标中的所有记录 */
for (i = 0; i < nRecs && MCO_S_OK == rc; i++) {
/* 从游标获取记录 */
Record_from_cursor(t, &csr, &rec);
Record_iIdx_get(&rec, &idx);
Record_iSeries_get(&rec, &series);
printf("\n\tIndex %d Series %d", idx, series);
/* 转到下一项 */
rc = mco_cursor_next(t, &csr);
}
}
/* 关闭事务 */
mco_trans_rollback(t);
}
列表索引搜索
在模式文件中声明列表将创建一个内部哈希索引,该索引允许在无序对象列表中按顺序(从第一个到最后一个,或从最后一个到第一个)进行导航。 为了说明这一点,请参考以下代码片段:
class anObject
{
uint4 value;
list;
};
对于这个类,生成_cursor()
函数如下所示:
MCO_RET anObject_list_cursor( mco_trans_h t, /*OUT*/ mco_cursor_h c );
下面的代码片段可以用来滚动这个类的对象:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if ( MCO_S_OK == rc )
{
rc = anObject_list_cursor(t, &csr);
if ( MCO_S_OK == rc )
{
printf("All objects : ");
for (rc = mco_cursor_first(t, &csr);
rc == MCO_S_OK;
rc = mco_cursor_next(t, &csr))
{
anObject_from_cursor(t, &csr, &obj);
anObject_value_get(&obj, &value);
printf("(%d) ", value);
}
}
mco_trans_rollback(t);
}
定义索引搜索
C 应用程序可以声明用户自定义的哈希或树索引,并为它们定义自定义的比较函数。对于用户自定义的哈希索引,仅会生成一个 _find()
函数。因此,只有精确匹配搜索是合适的。
但对于用户自定义的树索引,可以实现精确匹配和索引搜索,这将使用开发人员定义的自定义比较函数。
在使用用户定义的索引函数之前,应用程序必须通过 mco_db_register_udf()
API 将自定义的比较和哈希函数注册到数据库运行时。
MCO_RET mco_db_register_udf(
const char * db_name,
mco_userdef_funcs_h udfs // 指向通过生成的 dbname_get_udfs() 函数获得的自定义函数表的指针
);
提醒
mco_db_register_udf()
必须在mco_runtime_start()
之后和在调用mco_db_connect()
(创建数据库连接)之前调用。
例如:
mco_runtime_start();
mco_db_open("MyDB", mydb_get_dictionary(), start_mem, DBSIZE, PAGESIZE);
mco_db_register_udf("MyDB", mydb_get_udfs());
mco_db_connect("MyDB", &db);
...<continue processing>...
如果为数据库声明了用户定义的索引,但未通过 mco_db_register_udf()
注册自定义函数,则调用 mco_db_connect()
将返回 MCO_E_NOUSERDEF_FUNCS
错误代码。
现在,可以像使用普通树索引一样使用用户定义的索引执行搜索操作。例如:
rc = mco_trans_start(db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t);
if (rc == MCO_S_OK)
{
/* 使用用户自定义树形索引 */
rc = Record_tudf_index_cursor(t, &c);
if (rc == MCO_S_OK) {
for (rc = mco_cursor_first(t, &c);
rc == MCO_S_OK;
rc = mco_cursor_next(t, &c))
{
Record_from_cursor(t, &c, &rec);
Record_name_get(&rec, buf, 11, &len);
Record_value_get(&rec, &value);
printf("\n\t%-15s, value = %d", buf, value);
}
rc = mco_trans_commit(t);
}