Как оптимизировать Базовый Запрос данных для полнотекстового поиска

Человек делает у меня есть опыт с этим. Я работаю над проектом, где мы первоначально хранили все наши данные с помощью XML, затем перемещенного в sqlite. Существует много за и против к каждой технологии, но это была производительность, которая вызвала переключение. Вот то, что мы наблюдали.

Для маленьких баз данных (несколько meg или меньший), XML был намного быстрее, и легче иметь дело с. Наши данные были естественно в древовидном формате, который сделал XML намного более привлекательный, и XPath позволил нам делать много запросов в одной простой строке вместо того, чтобы иметь необходимость спуститься с дерева родословной.

Мы программировали в среде Win32 и пользовались стандартной библиотекой Microsoft DOM. Мы загрузили бы все данные в память, проанализировали бы его в dom дерево и искали бы, добавить, изменить на в копии памяти. Мы периодически сохраняли бы данные и должны были повернуть копии в случае, если машина, разрушенная посреди записи.

Мы также должны были создать некоторые "индексы" рукой с помощью карт дерева C++. Это, конечно, было бы тривиально, чтобы сделать с sql.

Примечание, что размер данных по файловой системе был фактором 2-4 меньших, чем "в памяти" dom дерево.

К тому времени, когда данные добрались до размера 10M-100M, мы начали иметь настоящие проблемы. Интересно достаточно, во всех размерах данных, обработка XML была намного быстрее, чем sqlite оказался (потому что это было в памяти, не на жестком диске)! Проблема была на самом деле двукратной - сначала, loadup время действительно начал становиться длинным. Мы должны были бы ожидать приблизительно одна минута, прежде чем данные были в памяти, и карты были созданы. Конечно, когда-то загруженный программа была очень быстра. Вторая проблема состояла в том, что вся эта память была занята все время. Системы только с несколькими сотнями meg были бы безразличны в других приложениях даже при том, что мы работали очень быстро.

Мы на самом деле изучающий использование файловой системы базирующаяся xml база данных. Существует пара открытых полученных версий xml базы данных, мы судили их. Я никогда не пытался использовать коммерческую xml базу данных, таким образом, я не могу прокомментировать их. К сожалению, мы никогда не могли заставлять xml базы данных работать хорошо вообще. Даже действие заполнения базы данных с сотнями meg xml заняло часы.... Возможно, мы использовали его неправильно. Другая проблема состояла в том, что эти базы данных были довольно тяжелы. Они потребовали Java и имели всю клиент-серверную архитектуру. Мы разочаровались в этой идее.

Мы нашли sqlite тогда. Это решило наши проблемы, но по цене. Когда мы первоначально включили sqlite, память и проблемы времени загрузки закончились. К сожалению, так как вся обработка была теперь сделана на жестком диске, загрузка фоновой обработки пошла путем. В то время как ранее мы даже не заметили загрузку ЦП, теперь использование процессора было путем. Мы должны были оптимизировать код, и все еще должны были сохранить некоторые данные в памяти. Мы также должны были переписать много простых запросов XPath как сложные алгоритмы мультизапроса.

, Таким образом, вот сводка того, что мы изучили.

  1. Для древовидных данных, XML намного легче запросить и изменить использование XPath.

  2. Для небольших наборов данных (меньше, чем 10M), XML сдул sqlite в производительности.

  3. Для больших наборов данных (больше, чем 10M-100M), время загрузки XML и использование памяти стали большой проблемой, до такой степени, что некоторые компьютеры становятся неприменимыми.

  4. Мы не могли заставить открытый исходный код xml база данных решать проблемы, связанные с большими наборами данных.

  5. SQLITE не имеет проблем памяти XML dom, но это обычно медленнее в обработке данных (это находится на жестком диске, не в памяти). (отметьте - sqlite, таблицы могут быть сохранены в памяти, возможно, это сделало бы ее как быстро.... Мы не попробовали это, потому что мы хотели вытащить данные из памяти.)

  6. Хранение и запросы древовидных данных в таблице не приятны. Однако руководящие транзакции и индексирующий частично восполняют его.

13
задан Peter Hosey 21 November 2009 в 11:03
поделиться

4 ответа

Я нашел обходное решение. Думаю, это похоже на этот пост . Я добавил исходный код объединения в свой проект Core Data, а затем создал класс полнотекстового поиска, который не был подклассом управляемого объекта. В классе FTS I #import "sqlite3.h" (исходный файл) вместо платформы sqlite. Класс FTS сохраняет в файл .sqlite, отличный от постоянного хранилища Core Data.

Когда я импортирую свои данные, объект Core Data сохраняет идентификатор строки связанного объекта FTS как целочисленный атрибут. У меня есть статический набор данных, поэтому я не беспокоюсь о ссылочной целостности, но код для поддержания целостности должен быть тривиальным.

Для выполнения FTS я MATCH запрашиваю класс FTS, возвращая набор идентификаторов строк. В моем классе управляемых объектов я запрашиваю соответствующие объекты с помощью [NSPredicate predicateWithFormat: @ "rowid IN% @", rowids] . Таким образом я избегаю прохождения любых отношений «многие-ко-многим».

Производительность заметно улучшилась. Мой набор данных составляет 142287 строк, включая 194 МБ (основные данные) и 92 МБ (FTS с удаленными стоп-словами). В зависимости от частоты поискового запроса мои поисковые запросы увеличились с нескольких секунд до 0,1 секунды для нечастых запросов (<100 совпадений) и 0,2 секунды для частых запросов (> 2000 совпадений).

Я уверен, что у моего подхода есть множество проблем (раздувание кода, возможные конфликты пространств имен, потеря некоторых функций Core Data), но, похоже, он работает.

10
ответ дан 1 December 2019 в 23:47
поделиться

Погрузитесь в дело.

Вот один способ решить эту проблему:

  1. Поместите свои записи в постоянное хранилище Core Data
  2. Используйте NSFetchedResultsController для управления набор результатов для объектов Word (эквивалент Core Data с таблицей «слов» SQL)
  3. Используйте UISearchDisplayController , чтобы применить NSPredicate к набору результатов в реальном time

Если у вас есть набор результатов через NSFetchedResultsController , довольно легко применить предикат. По моему опыту, он тоже будет отзывчивым. Например:

if ([self.searchBar.text length]) {
    _predicate = [NSPredicate predicateWithFormat:[NSString stringWithFormat:@"(word contains[cd] '%@')", self.searchBar.text]];
    [self.fetchedResultsController.fetchRequest setPredicate:_predicate];
}

NSError *error;
if (![self.fetchedResultsController performFetch:&error]) {
    // handle error...
}
NSLog(@"filtered results: %@", [self.fetchedResultsController fetchedObjects]);

отфильтрует набор результатов [self.fetchedResultsController fetchedObjects] на лету, выполняя поиск без учета регистра по слову .

2
ответ дан 1 December 2019 в 23:47
поделиться

Чтобы ответить на этот вопрос, я обнаружил, что при использовании Core Data запросы выполняются очень медленно. Я ломал голову над этим в течение многих часов.

Как и в примере SQL в моем вопросе, есть две сущности: textTable и слова, где слова содержат каждое слово, оно индексируется, и есть много-к -многие отношения между textTable и словами. Я заполнил базу данных всего 4000 слов и 360 объектов textTable. Предположим, что отношение textTable к объекту слов называется searchWords, тогда я могу использовать предикат для объекта textTable, который выглядит как

predicate = [NSPredicate predicateWithFormat:@"ANY searchWords.word BEGINSWITH %@", query];

(я могу добавить соединения этого предиката для нескольких условий запроса).

На iPhone этот запрос занимает несколько секунд. Ответ на мой код SQL, написанный вручную с использованием большего набора тестов, был мгновенным.

Но это еще не конец. У NSPredicate есть ограничения, которые делают довольно простые запросы медленными и сложными. Например, представьте, что в приведенном выше примере вы хотите отфильтровать с помощью кнопки области. Предположим, что объект слов содержит все слова во всех текстовых полях, но область действия ограничивает его словами из определенных полей. Таким образом, слова могут иметь атрибут «источник» (например, заголовок и тело сообщения электронной почты).

Естественно, тогда полный текст будет игнорировать атрибут источника, как в приведенном выше примере, но отфильтрованный запрос ограничит поиск определенным исходным значением. Это, казалось бы, простое изменение требует SUBQUERY. Например, это не работает:

ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3

, потому что сущности, которые истинны для двух выражений, могут быть разными. Вместо этого вы должны сделать что-то вроде:

SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3).@count > 0

Я обнаружил, что эти подзапросы, что, возможно, неудивительно, медленнее, чем предикаты, использующие «ANY».

На данный момент мне очень любопытно, как программисты Cocoa эффективно используют Core Данные для полнотекстового поиска, потому что меня разочаровывают как скорость вычисления предикатов, так и выразимость NSPredicates. Я наткнулся на стену.

t работа:

ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3

потому что сущности, которые истинны для двух выражений, могут быть разными. Вместо этого вы должны сделать что-то вроде:

SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3).@count > 0

Я обнаружил, что эти подзапросы, что неудивительно, медленнее, чем предикаты, использующие «ANY».

На данный момент мне очень любопытно, как программисты Cocoa эффективно используют Core Данные для полнотекстового поиска, потому что меня разочаровывают как скорость оценки предикатов, так и выразимость NSPredicates. Я наткнулся на стену.

t работа:

ANY searchWords.word BEGINSWITH "foo" AND ANY searchWords.source = 3

, потому что сущности, которые истинны для двух выражений, могут быть разными. Вместо этого вы должны сделать что-то вроде:

SUBQUERY(searchWords, $x, $x.word BEGINSWITH "foo" AND $x.source = 3).@count > 0

Я обнаружил, что эти подзапросы, что, возможно, неудивительно, медленнее, чем предикаты, использующие «ANY».

На данный момент мне очень любопытно, как программисты Cocoa эффективно используют Core Данные для полнотекстового поиска, потому что меня разочаровывают как скорость вычисления предикатов, так и выразимость NSPredicates. Я наткнулся на стену.

Мне очень любопытно, как программисты на Какао эффективно используют Core Data для полнотекстового поиска, потому что меня обескураживает как скорость вычисления предикатов, так и выразимость NSPredicates. Я наткнулся на стену.

Мне очень любопытно, как программисты на Какао эффективно используют Core Data для полнотекстового поиска, потому что меня обескураживает как скорость вычисления предикатов, так и выразимость NSPredicates. Я наткнулся на стену.

3
ответ дан 1 December 2019 в 23:47
поделиться

После борьбы с этой же проблемой я наткнулся на серию постов, где автор столкнулся с той же проблемой и предложил это решение. Он сообщает об улучшении времени поиска с 6-7 секунд до 0,13-0,05 секунды.

Его набор данных для FTS состоял из 79 документов (размер файла 175k, 3600 дискретных лексем, 10000 ссылок). Я еще не пробовал его решение, но подумал, что должен опубликовать его как можно скорее. См. также Часть 2 его сообщений для документации проблемы и Часть 1 для документации набора данных.

2
ответ дан 1 December 2019 в 23:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: