Индексация рекордного уровня
Для не документ базировал программы, можно создать индексы Центра внимания, где каждая запись индексируется индивидуально.
Обзор
Можно создать индексы Центра внимания, где каждая запись индексируется индивидуально. Эта функция только поддерживается в не, документ базировал приложения. Для основанных на документе приложений необходимо использовать метаданные хранилища, как описано в Индексации Уровня метаданных Хранилища.
В этом подходе Базовые Данные управляют каталогом в Библиотеке/Кэшах/Метаданных/Ядре, Data/в корневом каталоге пользователя (структура этого каталога является частной — не зависят от него). Для каждой отдельной программы Базовые Данные создают подкаталог; в каждом подкаталоге Данные Ядра создают дальнейший подкаталог для каждого персистентного хранилища, используемого программой. В каталоге для данного хранилища, для каждого экземпляра каждого объекта, который Вы принимаете решение индексировать, Удаляют сердцевину Данных, создает новый внешний рекордный файл. Внешние рекордные файлы используются Центром внимания для индексации персистентного хранилища.
Существует три процесса, вовлеченные в создание и поддержание индекса Центра внимания:
Ваша программа.
В Вашей программе Базовая платформа Данных поддерживает персистентное хранилище и создает внешние рекордные файлы для инициирования индексации центра внимания
Средство импорта Центра внимания.
Средство импорта делает фактическую индексацию. Это запускается каждый раз, когда изменяется внешняя запись.
Необходимо записать и поддержать средство импорта; для каждой внешней записи это должно добавить подходящие данные к индексу Центра внимания. Это - во многих отношениях самая важная часть разработки средства импорта.
Базовые Данные Внешний Рекордный демон.
Это - вторичный процесс, используемый Базовой платформой Данных. Это используется для улучшения производительности при изменении большого количества записей (больше чем приблизительно сто) одновременно. В этой ситуации Базовые Данные пишут единственный рабочий файл, который внешний демон впоследствии распаковывает для создания отдельных внешних записей.
Основная цель внешнего рекордного файла состоит в том, чтобы просто служить указателем на запись (экземпляр объекта) в персистентном хранилище. В средстве импорта Вы выбираете соответствующий управляемый объект от персистентного хранилища своего приложения и используете его для обновления индекса Центра внимания.
При условии, что по крайней мере одно свойство для объекта указано как индексируемый (см. Конфигурирование Модели), тогда каждый раз, когда изменение в экземпляре того объекта посвящает себя персистентному хранилищу, соответствующий внешний рекордный файл обновляется (минимально измененная дата последнего изменения). Внешний рекордный файл может быть пустым (файл нулевой длины), однако можно также, дополнительно, указать свойства, Данные Ядра значений которых должны сохранить в файле. Средство импорта не использует данных, которые Вы, возможно, указали, должен быть сохранен в самой внешней записи (чтобы понять, как Вы могли бы использовать его, посмотрите Используя Внешние Записи).
Создание проекта
Когда Вы создаете новый проект с XCode, выбираете, не документ базировал приложение, использующее Базовые Данные для хранения и имеющее Индексацию Центра внимания (“Базовое Применение данных со Средством импорта Центра внимания”). Шаблон Xcode добавляет файлы Средства импорта и создает цель сборки для средства импорта. Существует тогда две цели, которыми необходимо управлять — приложение и средство импорта. Они оба совместно используют общую модель управляемого объекта.
Конфигурирование модели
Для включения индексации Центра внимания необходимо указать который, свойства которого объекты должны быть индексированы и который — если кто-либо — тех свойств должен быть добавлен к внешней записи.
В инспекторе Конфигурации, для каждого из свойств, что Вы хотите быть индексированными, выберите:
Индекс в центре внимания
(Необязательно) Хранилище во внешнем рекордном файле
При условии, что по крайней мере одному свойству для объекта установили флаг «Index in Spotlight», тогда каждый раз, когда изменение в экземпляре того объекта посвящает себя персистентному хранилищу, соответствующий внешний рекордный файл обновляется (минимально измененная дата последнего изменения). Это означает, что средство импорта Центра внимания будет работать и таким образом, индекс Центра внимания будет обновлен с новыми данными.
Если Вы только выбираете «Index in Spotlight», Core Data просто создает файл нулевой длины для соответствующей записи. Если Вы также выбираете «Store in External Record», свойство добавляется к содержанию внешней записи. Простые типы, такие как строки, даты, и числа, экспортируются непосредственно; отношения сохранены как UUIDs.
Средство импорта самостоятельно не использует данных, которые Вы, возможно, указали, должен быть сохранен во внешней записи. Можно, однако, хотеть использовать внешние записи сами, например для восстановления после ошибки или помогать отладке — посмотрите Используя Внешние Записи. Если Вы не делаете, тем не менее, для предотвращения издержек дублирования данных и занимания места на диске, Вы не должны принимать решение сохранить значения свойств во внешней записи.
Конфигурирование Вашей Программы
Существует несколько опций, которые можно установить для хранилища для указания, где внешние записи должны быть сохранены, расширение для внешних рекордных имен файлов и формат, в котором должны быть записаны внешние записи:
NSExternalRecordsDirectoryOption
Эта константа указывает каталог URL, где сохранены внешние записи.
Необходимо указать каталог в пользователе
Library/Caches/Metadata/CoreData
илиLibrary/CoreData
каталог. Необходимо использовать это в сочетании сNSExternalRecordsExtensionOption
опция.NSExternalRecordExtensionOption
Эта константа указывает расширение, используемое для внешних рекордных файлов.
Необходимо использовать это в сочетании с
NSExternalRecordsDirectoryOption
опция.NSExternalRecordsFileFormatOption
Эта константа указывает формат файла для использования при записи внешних записей.
Значение по умолчанию должно записать записи как XML (
NSXMLExternalRecordType
) Если Вы хотите человекочитаемый формат или хотите быть в состоянии совместно использовать содержание с другими приложениями — это - хороший выбор. Иначе необходимо обычно использовать двоичный формат (NSBinaryExternalRecordType
).
Вы устанавливаете эти опции при конфигурировании персистентного координатора хранилища, как показано в следующем примере:
NSString *externalRecordsSupportFolder = <#Path for records folder#>; |
NSURL *storeURL = <#URL for the store#>; |
NSMutableDictionary *storeOptions = @{ |
NSExternalRecordExtensionOption:@"MyExtension", |
NSExternalRecordsDirectoryOption:externalRecordsSupportFolder, |
NSExternalRecordsFileFormatOption:NSBinaryExternalRecordType}; |
NSPersistentStoreCoordinator *aPSC = <#Get the coordinator#>;. |
if (![aPSC addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL |
options:storeOptions error:&error]) { |
[[NSApplication sharedApplication] presentError:error]; |
} |
Реализация средства импорта
В этом разделе описываются прежде всего функции средства импорта, которые уникальны для Базовых Данных. Для понимания основных принципов того, как реализовать средство импорта Центра внимания необходимо считать Руководство по программированию Средства импорта Центра внимания. Отметьте также, что большая часть конфигурации и подробных данных реализации обсуждены в Средстве импорта файл ReadMe и предоставлены как комментарии в шаблонах.
Вообще говоря существует два аспекта к реализации средства импорта: конфигурируя настройки проекта, например, расширение файла для Вашего внешнего типа записи; и настройка метода, добавляющего данные от записи до индекса Центра внимания. Последним аспектом является, вероятно, самый важный аспект задачи.
Конфигурация проекта
Во-первых при использовании пользовательского типа хранилища необходимо соединить пользовательский класс хранилища в проект. Тогда существует три дополнительных настройки, которые необходимо сконфигурировать:
Расширение для внешнего рекордного файла.
В шаблоне это дано как
YOUR_EXTERNAL_RECORD_EXTENSION
; необходимо обеспечить пользовательское расширение для типа файла (например,myFileType
). Это - a#define
, таким образом, можно просто использовать:#define YOUR_EXTERNAL_RECORD_EXTENSION @"myFileType"
и отпуск
define
d постоянный неповрежденный всюду по проекту.UTI для Ваших рекордных файлов.
В шаблоне это дано как
YOUR_EXTERNAL_RECORD_EXTENSION
; необходимо обеспечить пользовательский UTI для типа (например,mycompany.mytype1
). Это - строковый литерал в нескольких файлах (info.plist), таким образом, необходимо изменить каждое возникновение.Тип хранилища.
В
MySpotlightImporter.m
, необходимо определить тип персистентного хранилища, которое создает программа. По умолчанию это, указывает как тип хранилища XML:#define YOUR_STORE_TYPE NSXMLStoreType
Необходимо указать расширение и UTI для записей в plist файлах проекта. При определении собственного UTI Вы указываете его в файле Средства-импорта-Info.plist. Для получения дополнительной информации см. Руководство по программированию Средства импорта Центра внимания.
Добавление свойств к индексу центра внимания
Цель средства импорта состоит в том, чтобы добавить к выбранным свойствам индекса Центра внимания от записи, идентифицированной внешним рекордным файлом. Большая часть кода предоставлена шаблоном проекта, и — кроме конфигурации, обрисованной в общих чертах в Конфигурации Проекта — должно быть мало потребности изменить реализацию любого из методов кроме:
- (BOOL)importFileAtPath:(NSString *)filePath attributes:(NSMutableDictionary *)spotlightData error:(NSError **)error |
Это - метод, фактически выбирающий управляемый объект для записи и добавляющий надлежащие пары ключ/значение к spotlightData
словарь, который будет добавлен к индексу Центра внимания. Шаблон кода получает управляемый объект; необходимо записать код для добавления пар ключ/значение к spotlightData
словарь.
Получение управляемого объекта
path
параметр importFileAtPath:attributes:error:
путь к внешнему рекордному файлу. Используя этот путь, NSPersistentStoreCoordinator
класс может получить информацию о записи:
NSDictionary *pathInfo = [NSPersistentStoreCoordinator elementsDerivedFromExternalRecordURL:[NSURL fileURLWithPath:filePath]]; |
Существует три данных потребности средства импорта от записи:
URL для модели управляемого объекта
URL для персистентного хранилища
URI для самой записи
self.modelURL = [NSURL fileURLWithPath:[pathInfo valueForKey:NSModelPathKey]]; |
self.storeURL = [NSURL fileURLWithPath:[pathInfo valueForKey:NSStorePathKey]]; |
NSURL *objectURI = [pathInfo valueForKey:NSObjectURIKey]; |
Учитывая их, средство импорта может создать Базовый Стек данных и идентификатор объекта для надлежащего управляемого объекта, и тем самым получить управляемый объект:
NSManagedObject *instance = [[self managedObjectContext] objectWithID:oid]; |
Учитывая управляемый объект, можно обновить индексные данные Центра внимания.
Обновление словаря данных Центра внимания
В целом, тем не менее, Вы принимаете следующий образец: Для каждого объекта, Вас:
Установите строку дисплея для записи
Выполните итерации через атрибуты и отношения управляемого объекта, добавив подходящие записи в словарь данных Центра внимания.
Во-первых, Вы строка дисплея аппарата (строка, которую Центр внимания выводит на экран для записи в меню результатов поиска):
NSString *displayString = <#Create a display string from a managed object#>; |
[spotlightData setObject:displayString forKey:(__bridge id<NSCopying>)kMDItemDisplayName]; |
Вы тогда выполняете итерации через атрибуты и отношения для управляемого объекта. Для каждого свойства, которое Вы отметили, чтобы быть индексированными (Конфигурирование Модели), Вы добавляете подходящую запись в spotlightData
словарь. Для каждой записи Вы выбираете подходящий ключ атрибута метаданных (например, kMDItemTextContent
— посмотрите MDItemRef
для списка ключей атрибута, которые характерны для многих типов файлов), и надлежащее представление значения:
Вам решать определить как преобразование иерархически организованные данные в Вашем приложении в представление, подходящее для индекса Центра внимания. Необходимо рассмотреть отношения между объектами. Например, учитывая приложение, имеющее объекты Лица и Адреса, и к - многие отношение от Лица для Обращения, Вы могли бы:
В объекте Лица сгладьте отношение, чтобы Адресовать и просто индексировать объект Лица.
В этом случае пользователи будут в состоянии найти лицо, использующее любой из адресов, к которым у них есть отношение.
В объекте Адреса сгладьте отношение Лицу и просто индексируйте объект Адреса.
В этом случае пользователи только будут в состоянии найти лицо, если у них также будет связанный адрес.
В объекте Адреса сгладьте отношение Лицу и индексируйте и объекты Адреса и Лица.
При выравнивании отношения необходимо рассмотреть, как исходный объект будет обновлен, если изменится место назначения. Например, при изменении просто почтового индекса адреса лица как это должно измениться быть замеченным в приложении и распространенным к индексу Центра внимания (если вообще)?
Предотвращение, «если тогда еще»
Самый важный аспект разработки Вашего средства импорта решает, как импортировать каждый объект. Можно использовать следующий подход для предотвращения потребности использовать многократные условные операторы в importFileAtPath:attributes:error:
метод и вместо этого фокусируется на каждом объекте индивидуально.
В importFileAtPath:attributes:error:
, можно создать NSInvocation
объект, отправляющий сообщение в self
, где селектор следует за форматом import<EntityName>:attributes:
и метод возвращает a BOOL
значение, указывающее, был ли импорт успешен. Для каждого импортированного объекта Вы тогда реализуете метод формы:
- (BOOL)import<EntityName>:(EntityClass *)instance |
attributes:(NSMutableDictionary *)spotlightData |
importFileAtPath:attributes:error:
метод определяет имя объекта текущей записи и создает подходящее NSInvocation
объект:
- (BOOL)importFileAtPath:(NSString *)filePath attributes:(NSMutableDictionary *)spotlightData error:(NSError **)error { |
NSDictionary *pathInfo = |
[NSPersistentStoreCoordinator elementsDerivedFromExternalRecordURL: |
[NSURL fileURLWithPath:filePath]]; |
NSString *entityName = [pathInfo objectForKey:NSEntityNameInPathKey]; |
self.modelURL = [NSURL fileURLWithPath:[pathInfo valueForKey:NSModelPathKey]]; |
self.storeURL = [NSURL fileURLWithPath:[pathInfo valueForKey:NSStorePathKey]]; |
NSURL *objectURI = [pathInfo valueForKey:NSObjectURIKey]; |
NSManagedObjectID *oid = |
[[self persistentStoreCoordinator] managedObjectIDForURIRepresentation:objectURI]; |
if (!oid) { |
NSLog(@"%@:%s to find object id from path %@", [self class], _cmd, filePath); |
return NO; |
} |
/* |
Create and invoke an NSInvocation object that sends a message to self where: |
The selector follows the format import<EntityName>:attributes: |
The return value is a BOOL. |
*/ |
NSString *importSelectorString = |
[NSString stringWithFormat:@"import%@:attributes:", entityName]; |
SEL importSelector = NSSelectorFromString(importSelectorString); |
if (![self respondsToSelector:importSelector]) { |
NSLog(@"%@:%s Couldn't import an instance of entity %@", |
[self class], _cmd, entityName); |
return NO; |
} |
NSManagedObject *instance = [[self managedObjectContext] objectWithID:oid]; |
NSMethodSignature *methodSignature = [self methodSignatureForSelector:importSelector]; |
NSInvocation *invocation = |
[NSInvocation invocationWithMethodSignature:methodSignature]; |
[invocation setTarget:self]; |
[invocation setSelector:importSelector]; |
[invocation setArgument:&instance atIndex:2]; |
[invocation setArgument:&spotlightData atIndex:3]; |
BOOL returnValue; |
[invocation invoke]; |
[invocation getReturnValue:&returnValue]; |
return returnValue; |
} |
Учитывая предыдущую реализацию importFileAtPath:attributes:error:
, предположите, что у Вас есть модель, содержащая два объекта, Лицо и Адрес, и что существует к - многие отношение от Лица для Обращения. Вы могли бы реализовать метод для импорта экземпляров объекта Лица следующим образом:
- (BOOL)importPerson:(Person *)person attributes:(NSMutableDictionary *)spotlightData { |
// Set the display name for Spotlight search results. |
[spotlightData setObject:[person fullName] forKey:(__bridge id<NSCopying>)kMDItemDisplayName]; |
NSDate *dob = person.dateOfBirth; |
if (dob != nil) { |
[spotlightData setObject:dob forKey:(__bridge id<NSCopying>)kMDItemTimestamp]; |
} |
NSMutableString *text = [NSMutableString stringWithFormat:@"%@", [person fullName]]; |
for (NSManagedObject *address in person.addresses) { |
NSString *component; |
component = [address street1]; |
if (component) { |
[text appendFormat:@" %@", component]; |
} |
component = [address city]; |
if (component) { |
[text appendFormat:@" %@", component]; |
} |
component = [address state]; |
if (component) { |
[text appendFormat:@" %@", component]; |
} |
component = [address zip]; |
if (component) { |
[text appendFormat:@" %@", component]; |
} |
} |
[spotlightData setObject:text forKey:(__bridge id<NSCopying>)kMDItemTextContent]; |
return YES; |
} |
Открытие внешней записи
Когда пользователь делает выбор после поиска Центра внимания, заключительный аспект поддержки интеграции Центра внимания должен вывести на экран надлежащую запись. Необходимо реализовать метод делегата приложения application:openFiles:
реагировать на открытый запрос файла на внешнюю запись:
- (void)application:(NSApplication *)theApplication openFiles:(NSArray *)files { |
NSString *aPath = [files lastObject]; // Just an example to get at one of the paths. |
if (aPath && [aPath hasSuffix:YOUR_EXTERNAL_RECORD_EXTENSION]) { |
// Decode URI from path. |
NSURL *objectURI = [[NSPersistentStoreCoordinator elementsDerivedFromExternalRecordURL:[NSURL fileURLWithPath:aPath]] objectForKey:NSObjectURIKey]; |
if (objectURI) { |
NSManagedObjectID *moid = [[self persistentStoreCoordinator] managedObjectIDForURIRepresentation:objectURI]; |
if (moid) { |
NSManagedObject *mo = [[self managedObjectContext] objectWithID:moid]; |
// Your code to select the object in your application's UI. |
} |
} |
} |
} |
Шаблон показывает, как идентифицировать управляемый объект, который выбрал пользователь. То, как Вы тогда гарантируете, что этот объект выведен на экран в пользовательском интерфейсе, зависит полностью на Вашу архитектуру приложения и схему в Вашей модели управляемого объекта.
Используя внешние записи
Средство импорта не использует данных, которые Вы, возможно, указали, должен быть сохранен в самой внешней записи (см. Обзор). В других программах, однако, можно использовать следующий NSPersistentStoreCoordinator
методы для взаимодействия с внешними записями:
elementsDerivedFromExternalRecordURL:
метод класса, берущий URL к внешнему рекордному файлу и возвращающий словарь с полученными элементами, такими как имя объекта и URI для экземпляра управляемого объекта.importStoreWithIdentifier:fromExternalRecordsDirectory:toURL:options:withType:error:
создает и заполняет новое хранилище с помощью внешних записей, найденных в указанном расположении.
При некоторых обстоятельствах, поэтому, Вы могли использовать внешнюю запись, например, для восстановления после ошибки или воссоздать хранилище, так или иначе ставшее поврежденным. Вы могли бы также использовать представление XML для разрешения обмена данными с другими программами. Вам решать для решения, какая информация могла бы быть полезной и в том, какие ситуации Вы могли бы использовать эти данные, и поэтому который, свойства которого объекты Вы хотите включать во внешнюю запись.
Структура внешнего каталога записей, как гарантируют, не останется постоянной между выпусками операционной системы. Вы можете, тем не менее, независимо от их расположения, просто выполнять итерации через внешние каталоги записей (например, с помощью NSFileManager
enumeratorAtPath:
) получать все файлы, расширение пути которых - это, которое Вы указали для своих внешних записей.
Можно также использовать внешние записи в качестве диагностики при разработке программы и средства импорта. Можно ли гарантировать, что корректная информация добавляется к записям, и что они обновляются, когда Вы ожидаете, что они будут — несмотря на то, что отмечают протест в том, Почему мои записи в синхронизации не?.
Поиск и устранение неисправностей
Много подсказок для поиска и устранения неисправностей даны в Руководстве по программированию Средства импорта Центра внимания. В частности можно протестировать использование средства импорта /usr/bin/mdimport
, как проиллюстрировано этим примером:
/usr/bin/mdimport -d2 ~/Library/Caches/Metadata/CoreData/$ApplicationName/$StoreID/$Entity/$fileName |
Используя корректное средство импорта
Для тестирования средства импорта создайте ссылку из каталога плагинов Центра внимания (/Library/Spotlight
) к недавно созданному средству импорта или копии средство импорта к /Library/Spotlight
каталог.
Проект по умолчанию включает средство импорта Центра внимания как ресурс приложений; если Вы хотите протестировать средство импорта из/Library/Spotlight каталога, удостоверьтесь, что Вы удаляете средство импорта из приложения.
Когда Вы используете /usr/bin/mdimport
в режиме отладки это сообщает, какое средство импорта это использует. Проверьте путь тщательно.
Пользовательские классы и методы
Если средство импорта не может найти пользовательский класс, удостоверьтесь, что Вы добавили, что оно к средству импорта предназначается в XCode.
Если средство импорта не может найти пользовательские методы, вспомните, что шаблон по умолчанию устанавливает класс управляемых объектов для объектов к NSManagedObject
; можно пропустить объекты, для которых Вы действительно хотите использовать свой пользовательский класс:
// Clear out all custom classes used by the model to avoid having to link them |
// with the importer. Remove this code if you need to access your custom logic. |
NSString *managedObjectClassName = [NSManagedObject className]; |
for (NSEntityDescription *entity in managedObjectModel) { |
if (![[entity name] isEqualToString:@"Person"]) { |
[entity setManagedObjectClassName:managedObjectClassName]; |
} |
} |
UTI
Если Центр внимания не использует Ваше средство импорта, удостоверьтесь, что Вы последовательно устанавливаете UTI всюду по проекту.
Вспомните также, что буквы в UTI должны все быть нижним регистром.
FAQ
Насколько эффективный эта технология?
Процесс обычно очень эффективен. Большая часть работы разгружена к фоновым процессам, таким образом, непосредственно не влияют на Вашу программу. При сохранении большого количества записей (больше чем приблизительно сто) Базовые Данные пишут единственный рабочий файл, который они впоследствии распаковывают для создания отдельных файлов. Иногда, однако, внешние записи могут временно стать из синхронизации с персистентным хранилищем — видят, Почему мои записи в синхронизации не?.
Почему мои записи в синхронизации не?
Как имеет место с любым Связанным с центром внимания процессом, индексу не гарантируют всегда, сразу отражают то, что находится в источнике (персистентное хранилище). Как описано в Обзоре, существует три процесса, управляющие данными — Вашей программой, средством импорта, и Базовым демоном Данных — весь из который выполненный одновременно. При создании нескольких сотен новых записей средство импорта должно быть в состоянии поддержать на высоком уровне. Если Вы обновляете и удаляете большее число записей, однако, возможно, что вещи доберутся временно из синхронизации.
Существует несколько других проблем, которые необходимо принять во внимание — особенно релевантный во время разработки:
При изменении модели или формата файла внешние записи не будут обновлены, пока исходная запись не изменяется.
При удалении объекта из модели внешний рекордный каталог для того объекта не будет удален.
Если необходимо, можно заставить все хранилище быть повторно индексированным (и так все внешние записи, которые будут воссозданы) просто путем удаления соответствующего внешнего рекордного каталога.