索引搜索
搜索接口通过唯一标识符或索引定位所需的对象或对象组。通过唯一标识符(包括哈希表和自动 ID)进行精确匹配查找使用游标方法 find() 来定位单个对象,其效率极高。SmartEDB 还提供了一套丰富的专用索引,这些索引使用游标来导航一组对象,作为有序的结果集(包括 Patricia 树、R 树和三元组树),或者作为未排序的对象序列(哈希表索引声明为 unique=false 或列表索引)。
Find
Cursor 方法 find() 会在索引中搜索精确匹配项。根据定义,在唯一索引上进行精确匹配查找会返回恰好一个结果,如果没有找到匹配项则返回零个结果。请注意,自定义 ID 从定义上讲是唯一的。因此,为其实现了一个内部管理的唯一哈希表索引,并且只有精确匹配的 find() 方法适用于自定义 ID 对象的查找。
Autoid 查找
正如“索引和游标”页面中所解释的那样,对于自动 ID 索引,只能使用游标方法 find() 进行“精确匹配”搜索。为了说明这一点,请考虑以下模式:
@Persistent(autoid = true)
class Department
{
@Indexable(type=Database.IndexType.BTree, unique=true) // Declare unique tree index by "code" field
String code;
String name;
}
@Persistent()
class Employee
{
@Indexable(type=Database.IndexType.BTree, unique=true) // Declare unique tree index by "name" field
String name;
long dept;
}
请注意,可以通过在“Employee”类中类型为“long”的“dept”引用字段来实现“Department”对象与“Employee”对象之间的一对多关系。如果每个“Employee”对象都存储其所属“Department”对象的“autoid”值,那么可以通过如下代码快速找到所属的“Department”对象:
con.startTransaction(Database.TransactionType.ReadOnly);
// 1. Find the Employee object by name
Cursor<Employee> cursor = new Cursor<Employee>(con, Employee.class, "name");
Employee emp;
emp = cursor.find(search_name);
// 2. Find the Department object by its autoid and display the Department name
Cursor<Department> cursor2 = new Cursor<Department>(con, Department.class);
Department d = cursor2.find(emp1.dept);
System.out.println("\n\nFind " + search_name + "'s co-workers in " + d.name + " :\n");
con.commitTransaction();
cursor.close();
请注意,此处的 Cursor cursor2 是在 Department 类上实例化的,且未指定索引,这意味着“使用类的自动 ID 索引”。
Search
游标本质上是对结果集中对象集合的迭代器。对于为类声明的每个非唯一索引,都可以实例化一个游标。游标的 search() 方法根据某些值定位游标,而 current() 方法提供对当前位置数据库对象的句柄。大多数索引类型提供有序的结果集;基于列表和非唯一哈希表的游标允许按顺序(从头到尾或从尾到头)导航,尽管序列的顺序未定义;它只是遍历无序对象列表的一种机制。
游标导航
使用 search() 方法或其中一个游标定位函数(moveFirst()、moveLast()、moveNext() 或 movePrev())定位游标后,可以使用 current() 方法获取对象的句柄,以便随后使用对象接口方法。
B 树索引搜索
为了演示常见的 B 树索引搜索场景,请考虑以下数据库类定义:
@Persistent()
class Employee
{
@Indexable(type=Database.IndexType.BTree, unique=true) // Declare unique tree index by "name" field
String name;
int dept_no;
}
要通过 BTree 索引名称查找 Employee 对象,我们可以使用如下代码:
Employee emp;
String search_name = "William";
con.startTransaction(Database.TransactionType.ReadOnly);
Cursor<Employee> cursor = new Cursor<Employee>(con, Employee.class, "name");
emp = cursor.find(search_name);
con.commitTransaction();
cursor.close();
Patricia 索引搜索
正如“索引和游标”页面中所解释的那样,Patricia 索引可以声明为唯一;若未使用 unique 关键字,则默认允许重复项。 对 Patricia 索引的搜索是通过游标操作 ExactMatch、BestMatch、PrefixMatch 和 NextMatch 来执行的。 有关这些特定于 Patricia 的搜索操作的工作原理及示例,请参阅 Patricia 索引页面。为了演示如何使用 Patricia Java API,提供了 SDK 示例 Indexes_Patricia。下面将使用此示例中的代码片段来说明不同类型的 Patricia 索引搜索。
考虑具有如下字符串字段的 AreaCode 类:
@Persistent
class AreaCode
{
@Indexable(type=Database.IndexType.Patricia) // Declare patricia index by "areaCode" field
String areaCode;
@Dimension(4)
String strAreaCode;
}
顺序搜索
若要使用 Patricia 索引 areaCode 依次列出数据库的内容,我们可以使用如下代码:
con.startTransaction(Database.TransactionType.ReadOnly);
Cursor<AreaCode> cursor = new Cursor<AreaCode>(con, AreaCode.class, "areaCode");
for (AreaCode ac : cursor)
{
System.out.println(ac.toString());
}
cursor.close();
con.commitTransaction();
请注意,这里不需要使用 search() 方法,因为只是简单地实例化了游标,而对数据库对象的顺序导航是通过标准的 Java 游标迭代来完成的。
精确匹配、前缀匹配和最佳匹配搜索
要执行“精确匹配”搜索,我们可以使用如下代码:
con.startTransaction(Database.TransactionType.ReadOnly);
Cursor<AreaCode> cursor = new Cursor<AreaCode> (con, AreaCode.class, "areaCode");
if (cursor.search(Cursor.Operation.ExactMatch, strAreaCode))
{
System.out.println("\tFound " + op + " for key " + strAreaCode);
do
{
System.out.println(cursor.next().toString());
} while (cursor.search(Cursor.Operation.NextMatch, strAreaCode));
}
else
{
System.out.println("\t" + op + " not found for key " + strAreaCode);
}
cursor.close();
con.commitTransaction();
要执行“前缀匹配”搜索,我们可以使用上述代码,只需将搜索()操作替换为 Cursor.Operation.PrefixMatch 即可。同样,将 Cursor.Operation.BestMatch 用于“最佳匹配”搜索。请注意,要移动游标,应使用操作 Cursor.Operation.NextMatch 。
R 树索引搜索
正如“索引和游标”页面中所解释的那样,RTree 索引通常用于加快空间搜索的速度。对 RTree 索引的搜索是通过游标方法 search() 并使用四种操作之一(等于、重叠、包含或邻域)来执行的。
有关这些 R 树特定搜索操作的工作原理的解释和示例,请参阅 R 树索引页面。为了演示 R 树 Java API 的使用,提供了 SDK 示例 Indexes_RTree。下面使用此示例中的代码片段来说明不同类型的 R 树索引搜索。
考虑具有如下定义的 Rect 类:
@Persistent(list=true) // Store class in SmartEDB database, declare list index
class Rect
{
@Dimension(4)
@Indexable(type=Database.IndexType.RTree) // Declare rtree index on "square" field
public short[] square;
}
顺序搜索
要使用 R 树索引按顺序列出游标的内容,我们可以使用如下代码:
// Print out objects in direct or reverse order
static int iterateRects(Cursor<Rect> cursor, boolean reverse_order)
{
int i = 0;
for (Rect rect = (reverse_order) ? cursor.last() : cursor.first();
rect != null;
rect = (reverse_order) ? cursor.prev() : cursor.next())
{
if (i++ < SHOW_FIRST)
{
System.out.println("\t" + i + "." + rect);
}
}
if (i > SHOW_FIRST)
{
System.out.println("\t...");
}
return i;
}
请注意,这里不需要使用 search() 方法,因为游标仅通过 first() 或 last() 方法定位,具体取决于 reverse_order 的值,而数据库对象的顺序导航则通过调用 next() 或 prev() 方法来实现。
精确匹配、重叠、包含和邻域搜索
要执行“精确匹配”搜索,我们使用“等于”操作,代码如下:
con.startTransaction(Database.TransactionType.ReadWrite);
cursor = new Cursor<Rect> (con, Rect.class, "square");
System.out.println("\n\n\tSearch(Operation.Equals, '" + rect3 + "');");
if (cursor.search(Cursor.Operation.Equals, rect3.square))
{
i = iterateRects(cursor, false);
System.out.println("\tFound " + i + " rect(s)");
}
cursor.close();
con.commitTransaction();
要执行“重叠”搜索,我们可以使用上述代码,只需将搜索()操作替换为 Cursor.Operation.Overlaps 即可。同样,将 Cursor.Operation.Contains 替换为“包含”搜索。若要按每个对象与某点的距离顺序列出 Rect 类的全部内容,我们只需在 rect3.square 中指定该点的坐标,并使用搜索()操作 Cursor.Operation.Neighbourhood 即可。
三元索引搜索
正如“索引和游标”页面中所解释的那样,RTree 索引通常用于加快空间搜索的速度。对 RTree 索引的搜索是通过游标方法 search() 并使用四种操作之一(等于、重叠、包含或邻域)来执行的。
有关这些 R 树特定搜索操作的工作原理的解释和示例,请参阅 R 树索引页面。为了演示 R 树 Java API 的使用,提供了 SDK 示例 Indexes_RTree。下面使用此示例中的代码片段来说明不同类型的 R 树索引搜索。
考虑具有如下定义的 Rect 类:
@Persistent
class TrigrmObj
{
@Indexable(type=Database.IndexType.Trigram)
String carid;
}
要使用三元语法索引执行 search() 操作,我们可能会使用如下代码:
String[] search_pattern = { "768", " 77", "4pi", "8cc", "7u7", " 77a474ko" };
for (String ptrn : search_pattern)
{
con.startTransaction(Database.TransactionType.ReadOnly);
cursor = new Cursor<TrigrmObj>(con, TrigrmObj.class, "carid");
System.out.println("\nObjects with pattern (" + ptrn + "):");
if (cursor.search(Cursor.Operation.Contains, ptrn))
{
for (TrigrmObj o : cursor)
{
System.out.println("\t(" + o.carid + ") ");
}
}
cursor.close();
con.rollbackTransaction();
}
哈希表索引搜索
正如“索引和游标”页面中所解释的,哈希表索引通常用于高效的对象查找。此外,正如上文“搜索”部分所述,声明为 unique=false 的哈希表索引允许在类的对象无序列表中按顺序(从头到尾或从尾到头)进行游标导航。为了演示哈希表 Java API 的使用,提供了 SDK 示例 Indexes_HashTable。下面使用了此示例中的代码片段。 考虑“索引和游标”页面中给出的数据库模式:
@Persistent
class Record
{
@Indexable(type=Database.IndexType.Hashtable, unique=true, initSize=10000) // Declare unique hash index
int iIdx;
@Indexable(type=Database.IndexType.Hashtable, unique=false, initSize=10000) // Declare non-unique hash index
int iSeries;
}
在此类中,唯一索引 iIdx 仅可用于通过 Cursor 方法 find() 进行精确查找。例如:
int findValue = 10;
Record rec;
// Find a specific value in unique index
System.out.println("\n\n\tFind record with index == " + findValue);
Connection con = new Connection(db);
con.startTransaction(Database.TransactionType.ReadOnly);
// Open the cursor
Cursor cursor = new Cursor<Record>(con, Record.class, "iIdx");
rec = cursor.find(findValue);
if ( rec != null )
{
System.out.println("\tIndex " + rec.iIdx + " Series " + rec.iSeries);
}
else
{
System.out.println("\tnot found");
}
cursor.close();
con.rollbackTransaction();
以下代码片段演示了如何使用 iSeries 中的非唯一哈希表索引来查找具有指定值的所有对象:
con.startTransaction(Database.TransactionType.ReadOnly);
cursor = new Cursor<Record>(con, Record.class, "iSeries");
// Search for records with specified value for iSeries */
if (cursor.search(Cursor.Operation.Equals, findValue))
{
// Show all records in cursor
rec = cursor.first();
for (i = 0; i < nRecs && rec != null; ++i)
{
System.out.println("\tIndex " + rec.iIdx + " Series " + rec.iSeries);
rec = cursor.next();
}
}
else
{
System.out.println("\tno records found.");
}
cursor.close();
con.rollbackTransaction();
列表索引搜索
正如“索引和游标”页面中所解释的那样,列表索引与非唯一的哈希表类似,允许在无序对象列表中按顺序(从第一个到最后一个,或从最后一个到第一个)进行导航。考虑上面用于 R 树示例的 Rect 类:
@Persistent(list=true) // Store class in SmartEDB database, declare list index
class Rect
{
@Dimension(4)
@Indexable(type=Database.IndexType.RTree) // Declare rtree index on "square" field
public short[] square;
}
请注意 @Persistent(list=true) 这个注解。它会导致内部维护一个列表索引,作为非唯一的哈希表索引。
顺序搜索
要按顺序使用列表索引列出游标的各项内容,我们可以使用如下代码:
con.StartTransaction(Database.TransactionType.ReadWrite);
// Create List cursor
Cursor cursor = new Cursor<Rect> (con, Rect.class, "square");
System.out.println("\n\tIterate through cursor with no search condition : ");
i = iterateRects(cursor, false);
System.out.println("\tFound " + i + " rect(s)");
cursor.close();
con.rollbackTransaction();
请注意,这里在类 Rect 上实例化游标时未指定索引,这意味着使用类列表的索引。然后调用方法 iterateRects() 来执行与 RTree 示例中上述相同的数据库对象的顺序导航。