索引搜索
搜索接口通过唯一标识符或索引定位所需的对象或对象组。通过唯一标识符(包括哈希表和自动 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
public String code;
public String name;
}
[Persistent]
class Employee
{
[Indexable(Type = Database.IndexType.BTree, Unique = true)] // Declare unique tree index by "name" field
public String name;
[References(typeof(Department))]
public 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, "name");
Employee 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 d = cursor2.Find(emp.dept);
Console.Write(d.name + " are:\n");
con.CommitTransaction();
cursor.Close();
cursor2.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)]
public String name;
public int dept_no;
}
要通过 BTree 索引名称查找 Employee 对象,我们可以使用如下代码:
Employee emp;
String search_name = "William";
con.StartTransaction(Database.TransactionType.ReadOnly);
// 1. Find the Employee object by name
Cursor<Employee> cursor = new Cursor<Employee>(con, "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 will be stored in SmartEDB database
class AreaCode
{
[Indexable(Type=Database.IndexType.Patricia)] // Declare patricia index by "areaCode" field
public String areaCode;
[Dimension(4)]
public String strAreaCode;
}
顺序搜索
若要使用 Patricia 索引 areaCode 依次列出数据库的内容,我们可以使用如下代码:
con.StartTransaction(Database.TransactionType.ReadOnly);
Cursor<AreaCode> cursor = new Cursor<AreaCode>(con, "areaCode");
foreach (AreaCode ac in cursor)
{
Console.WriteLine(ac.ToString());
}
cursor.Close();
con.CommitTransaction();
请注意,这里不需要使用 search() 方法,因为只是简单地实例化了游标,而对数据库对象的顺序导航是通过标准的 Java 游标迭代来完成的。
精确匹配、前缀匹配和最佳匹配搜索
要执行“精确匹配”搜索,我们可以使用如下代码:
con.StartTransaction(Database.TransactionType.ReadOnly);
Cursor<AreaCode> cursor = new Cursor<AreaCode> (con, "areaCode");
if (cursor.Search(Operation.ExactMatch, strAreaCode))
{
Console.WriteLine("\tFound " + op + " for key " + strAreaCode);
do
{
cursor.MoveNext();
Console.WriteLine(cursor.Current.ToString());
} while (cursor.Search(Operation.NextMatch, strAreaCode));
}
else
{
Console.WriteLine("\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 树索引按顺序列出游标的内容,我们可以使用如下代码:
static int IterateRects(Cursor<Rect> cursor, bool reverseOrder)
{
int i = 0;
for (bool hasNext = reverseOrder ? cursor.MoveLast() : cursor.MoveFirst();
hasNext = reverseOrder ? cursor.MovePrev() : cursor.MoveNext())
{
if (i++ < SHOW_FIRST)
{
Console.WriteLine("\t" + i + "." + cursor.Current);
}
}
if (i > SHOW_FIRST)
{
Console.WriteLine("\t...");
}
return i;
}
请注意,这里不需要使用 search() 方法,因为游标仅通过 first() 或 last() 方法定位,具体取决于 reverse_order 的值,而数据库对象的顺序导航则通过调用 next() 或 prev() 方法来实现。
精确匹配、重叠、包含和邻域搜索
要执行“精确匹配”搜索,我们使用“等于”操作,代码如下:
con.StartTransaction(Database.TransactionType.ReadWrite);
// Create cursor
cursor = new Cursor<Rect> (con, "square");
Console.WriteLine("\n\n\tSearch(Operation.Equals, '" + rect3 + "');");
if (cursor.Search(Operation.Equals, rect3.square))
{
i = IterateRects(cursor, false);
Console.WriteLine("\tFound " + i + " total rects");
}
cursor.Close();
con.CommitTransaction();
要执行“重叠”搜索,我们可以使用上述代码,只需将搜索()操作替换为 Cursor.Operation.Overlaps 即可。同样,将 Cursor.Operation.Contains 替换为“包含”搜索。若要按每个对象与某点的距离顺序列出 Rect 类的全部内容,我们只需在 rect3.square 中指定该点的坐标,并使用搜索()操作 Cursor.Operation.Neighbourhood 即可。
KD 树索引搜索
正如“索引和游标”页面中所解释的那样,kdtree 索引非常适合多维键值搜索。对 kdtree 索引的搜索是通过使用生成的 _search() 函数以示例查询的方式进行的,以定位符合给定搜索条件的对象。 有关此特定于 KDTree 的搜索的工作原理及示例,请参阅 KDTree 索引页面。为了演示 KDTree C# API 的使用,提供了 SDK 示例 KDTree。下面使用此示例中的代码片段来说明示例查询搜索。 请考虑以下类定义,它是 KDTree 索引页面中所展示的数据库模式的 C# 对应版本:
[Persistent] // store class in SmartEDB database
[Index("idx", Type=Database.IndexType.KDTree, Keys=new string[]{"Year", "Milage",
"Color", "Model", "Vendor", "Automatic", "AC", "Price"})]
public interface Car
{
string Vendor{get; set;}
string Model{get; set;}
string Color{get; set;}
uint Year{get; set;}
uint Milage{get; set;}
bool Automatic{get; set;}
bool AC{get; set;}
uint Price{get; set;}
[Dimension(2)]
string State{get; set;}
}
要执行示例查询搜索,如果进行精确匹配搜索,我们临时插入一个“模式对象”;若要指定范围,则插入两个边界“模式对象”(从和至)。然后我们调用游标方法 QueryByExample,并使用游标方法 MoveNext() 或 MovePrev() 在结果集中前进。例如:
/* Use read-write transaction to store boundary patterns in the database */
con.StartTransaction(Database.TransactionType.ReadWrite);
Car from = con.Create<Car>();
Car till = con.Create<Car>();
from.Vendor = till.Vendor = "Ford";
till.Price = 30000;
from.Year = 2000;
till.Year = 2006;
till.Milage = 100000;
Console.WriteLine("*** Range query");
Console.WriteLine("From:");
PrintCar(from);
Console.WriteLine("Till:");
PrintCar(till);
Console.WriteLine("Results:");
Cursor<Car> cursor = new Cursor<Car>(con, "idx");
if (cursor.QueryByExample(from, till))
{
foreach (Car car in cursor)
{
PrintCar(car);
}
}
cursor.Close();
con.RollbackTransaction();
三元索引搜索
正如“索引和游标”页面中所解释的那样,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
public int iIdx;
[Indexable(Type=Database.IndexType.Hashtable, Unique=false, InitSize=10000)] // Declare non-unique hash index
public int iSeries;
}
在此类中,唯一索引 iIdx 仅可用于通过 Cursor 方法 find() 进行精确查找。例如:
int findValue = 10;
Record rec;
con.StartTransaction(Database.TransactionType.ReadOnly);
cursor = new Cursor<Record>(con, "iIdx");
// Get found record - or not
rec = cursor.Find(findValue);
if (rec != null ) {
string[] array = rec.Consumers.ToArray();
Console.WriteLine("\tIndex " + rec.iIdx + " Series " + rec.iSeries + " Consumers no0 " + array.GetValue(0));
}
else
{
Console.WriteLine("\tnot found");
}
cursor.Close();
con.RollbackTransaction();
以下代码片段演示了如何使用 iSeries 中的非唯一哈希表索引来查找具有指定值的所有对象:
int findValue = 10;
Record rec;
// Show all records with specified value in non-unique index
Console.WriteLine("\n\n\tSearch for records with iSeries == " + findValue);
con.StartTransaction(Database.TransactionType.ReadOnly);
cursor = new Cursor<Record>(con, "iSeries");
// Search for records with specified value for iSeries */
if (cursor.Search(Operation.Equals, findValue))
{
// Show all records in cursor
bool hasNext = cursor.MoveFirst();
for (i = 0; hasNext && i < nRecs; ++i)
{
rec = cursor.Current;
string[] array = rec.Consumers.ToArray();
Console.WriteLine("\tIndex " + rec.iIdx + " Series " + rec.iSeries + " Consumers no0 " + array.GetValue(0));
hasNext = cursor.MoveNext();
}
}
else
{
Console.WriteLine("\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
public short[] square;
}
请注意 @Persistent(list=true) 这个注解。它会导致内部维护一个列表索引,作为非唯一的哈希表索引。
顺序搜索
要按顺序使用列表索引列出游标的各项内容,我们可以使用如下代码:
con.StartTransaction(Database.TransactionType.ReadWrite);
// Create List cursor
Cursor cursor = new Cursor<Rect> (con);
Console.WriteLine("\n\tIterate through cursor with no search condition : ");
i = IterateRects(cursor, false);
Console.WriteLine("\tFound " + i + " total rects");
cursor.Close();
con.RollbackTransaction();
请注意,这里在类 Rect 上实例化游标时未指定索引,这意味着使用类列表的索引。然后调用方法 iterateRects() 来执行与 RTree 示例中上述相同的数据库对象的顺序导航。