gpt4 book ai didi

sqlite - 在移动设备上进行全文搜索?

转载 作者:行者123 更新时间:2023-12-03 16:15:10 24 4
gpt4 key购买 nike

我们很快就会着手开发新的移动应用程序。这个特定的应用程序将用于大量搜索基于文本的字段。对于哪种数据库引擎最适合在移动平台上进行这些类型的搜索,来自整个小组的任何建议?

具体包括 Windows Mobile 6,我们将使用 .Net CF。此外,一些基于文本的字段将在 35 到 500 个字符之间。该设备将以两种不同的方式运行,批处理和 WiFi。当然,对于 WiFi,我们可以将请求提交给成熟的数据库引擎,然后取回结果。这个问题围绕着“批处理”版本,该版本将包含一个数据库,其中包含有关设备闪存/可移动存储卡的信息。

无论如何,我知道 SQLCE 有一些基本的索引,但在您获得完整版本之前,您不会进入真正花哨的“全文”样式索引,当然在移动平台上不可用。

数据外观的示例:

“围裙木匠可调节皮革容器口袋腰部五金腰带”等。

我还没有开始评估任何其他特定选项,因为我想我会利用这个小组的经验来首先向我指出一些特定的途径。

任何建议/提示?

最佳答案

就在最近,我遇到了同样的问题。这是我所做的:

我创建了一个类来保存每个对象的 id 和文本(在我的例子中,我称之为 sku(项目编号)和描述)。这将创建一个使用较少内存的较小对象,因为它仅用于搜索。找到匹配项后,我仍会从数据库中获取完整的对象。

public class SmallItem
{
private int _sku;
public int Sku
{
get { return _sku; }
set { _sku = value; }
}

// Size of max description size + 1 for null terminator.
private char[] _description = new char[36];
public char[] Description
{
get { return _description; }
set { _description = value; }
}

public SmallItem()
{
}
}

创建此类后,您可以创建这些对象的数组(在本例中我实际上使用了 List),并使用它在整个应用程序中进行搜索。这个列表的初始化需要一些时间,但你只需要在启动时担心这个。基本上只需在您的数据库上运行查询并获取创建此列表所需的数据。

有了列表后,您可以快速浏览它,搜索您想要的任何单词。由于它是一个包含,它还必须在单词中查找单词(例如,drill 会返回drill、drillbit、drills 等)。为此,我们编写了一个自主开发的非托管 c# contains 函数。它接受一个单词字符串数组(因此您可以搜索多个单词……我们将它用于“AND”搜索……描述必须包含传入的所有单词……当前不支持“OR”在这个例子中)。当它搜索单词列表时,它会构建一个 ID 列表,然后将其传递回调用函数。获得 ID 列表后,您可以轻松地在数据库中运行快速查询,以根据快速索引的 ID 号返回完整的对象。我应该提到,我们还限制了返回结果的最大数量。这个可以取出来。如果有人输入诸如“e”之类的内容作为他们的搜索词,这会很方便。这将返回很多结果。

这是自定义包含函数的示例:
public static int[] Contains(string[] descriptionTerms, int maxResults, List<SmallItem> itemList)
{
// Don't allow more than the maximum allowable results constant.
int[] matchingSkus = new int[maxResults];

// Indexes and counters.
int matchNumber = 0;
int currentWord = 0;
int totalWords = descriptionTerms.Count() - 1; // - 1 because it will be used with 0 based array indexes

bool matchedWord;

try
{
/* Character array of character arrays. Each array is a word we want to match.
* We need the + 1 because totalWords had - 1 (We are setting a size/length here,
* so it is not 0 based... we used - 1 on totalWords because it is used for 0
* based index referencing.)
* */
char[][] allWordsToMatch = new char[totalWords + 1][];

// Character array to hold the current word to match.
char[] wordToMatch = new char[36]; // Max allowable word size + null terminator... I just picked 36 to be consistent with max description size.

// Loop through the original string array or words to match and create the character arrays.
for (currentWord = 0; currentWord <= totalWords; currentWord++)
{
char[] desc = new char[descriptionTerms[currentWord].Length + 1];
Array.Copy(descriptionTerms[currentWord].ToUpper().ToCharArray(), desc, descriptionTerms[currentWord].Length);
allWordsToMatch[currentWord] = desc;
}

// Offsets for description and filter(word to match) pointers.
int descriptionOffset = 0, filterOffset = 0;

// Loop through the list of items trying to find matching words.
foreach (SmallItem i in itemList)
{
// If we have reached our maximum allowable matches, we should stop searching and just return the results.
if (matchNumber == maxResults)
break;

// Loop through the "words to match" filter list.
for (currentWord = 0; currentWord <= totalWords; currentWord++)
{
// Reset our match flag and current word to match.
matchedWord = false;
wordToMatch = allWordsToMatch[currentWord];

// Delving into unmanaged code for SCREAMING performance ;)
unsafe
{
// Pointer to the description of the current item on the list (starting at first char).
fixed (char* pdesc = &i.Description[0])
{
// Pointer to the current word we are trying to match (starting at first char).
fixed (char* pfilter = &wordToMatch[0])
{
// Reset the description offset.
descriptionOffset = 0;

// Continue our search on the current word until we hit a null terminator for the char array.
while (*(pdesc + descriptionOffset) != '\0')
{
// We've matched the first character of the word we're trying to match.
if (*(pdesc + descriptionOffset) == *pfilter)
{
// Reset the filter offset.
filterOffset = 0;

/* Keep moving the offsets together while we have consecutive character matches. Once we hit a non-match
* or a null terminator, we need to jump out of this loop.
* */
while (*(pfilter + filterOffset) != '\0' && *(pfilter + filterOffset) == *(pdesc + descriptionOffset))
{
// Increase the offsets together to the next character.
++filterOffset;
++descriptionOffset;
}

// We hit matches all the way to the null terminator. The entire word was a match.
if (*(pfilter + filterOffset) == '\0')
{
// If our current word matched is the last word on the match list, we have matched all words.
if (currentWord == totalWords)
{
// Add the sku as a match.
matchingSkus[matchNumber] = i.Sku.ToString();
matchNumber++;

/* Break out of this item description. We have matched all needed words and can move to
* the next item.
* */
break;
}

/* We've matched a word, but still have more words left in our list of words to match.
* Set our match flag to true, which will mean we continue continue to search for the
* next word on the list.
* */
matchedWord = true;
}
}

// No match on the current character. Move to next one.
descriptionOffset++;
}

/* The current word had no match, so no sense in looking for the rest of the words. Break to the
* next item description.
* */
if (!matchedWord)
break;
}
}
}
}
};

// We have our list of matching skus. We'll resize the array and pass it back.
Array.Resize(ref matchingSkus, matchNumber);
return matchingSkus;
}
catch (Exception ex)
{
// Handle the exception
}
}

获得匹配的 skus 列表后,您可以遍历数组并构建仅返回匹配 skus 的查询命令。

对于性能的想法,这是我们发现的(执行以下步骤):
  • 搜索 ~171,000 项
  • 创建所有匹配项目的列表
  • 查询数据库,只返回匹配项
  • 构建成熟的项目(类似于 SmallItem 类,但有更多的字段)
  • 使用完整的项目对象填充数据网格。

  • 在我们的移动设备上,整个过程需要 2-4 秒(如果我们在搜索所有项目之前达到匹配限制,则需要 2 秒……如果我们必须扫描每个项目,则需要 4 秒)。

    我也试过在没有非托管代码的情况下使用 String.IndexOf (并尝试过 String.Contains ... 与 IndexOf 具有相同的性能)。这种方式要慢得多......大约25秒。

    我还尝试使用 StreamReader 和包含 [Sku Number]|[Description] 行的文件。该代码类似于非托管代码示例。这种方式整个扫描大约需要 15 秒。速度不算太差,但不是很好。与我向您展示的方式相比,文件和 StreamReader 方法有一个优势。该文件可以提前创建。我向您展示的方式需要内存和应用程序启动时加载列表的初始时间。对于我们的 171,000 个项目,这大约需要 2 分钟。如果您有能力在每次应用程序启动时等待初始加载(当然可以在单独的线程上完成),那么以这种方式搜索是最快的方式(至少我已经找到了)。

    希望有帮助。

    PS - 感谢 Dolch 帮助处理一些非托管代码。

    关于sqlite - 在移动设备上进行全文搜索?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/276489/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com