Una biblioteca de indexación de texto completo incrustada rápida y mínima, escrita en Objective-C, construida sobre Objective-LevelDB.
Con mucho, la forma más fácil de integrar esta biblioteca en su proyecto es mediante el uso de Cocoapods.
Tener a los cocoapods instalados, si aún no
En tu podfile, agregue la línea
pod 'MHTextSearch'
Ejecutar pod install
Agregue el marco libc++.dylib a su proyecto.
MHTextIndex *index = [MHTextIndex textIndexInLibraryWithName: @" my.awesome.index " ]; Puede decirle a una instancia MHTextIndex que indexe sus objetos (cualquier objeto)
[ index indexObject: anyObjectYouWant];
[ index updateIndexForObject: anotherPreviousIndexedObject];
[ index removeIndexForObject: anotherPreviousIndexedObject]; Pero para que esto funcione, debe decirnos qué identificador como NSData * puede usarse para referirse de manera única a este objeto.
[ index setIdentifier: ^ NSData *(MyCustomObject *object){
return object. indexID ; // a NSData instance
}];También debe darnos detalles sobre los objetos, como cuáles son las piezas de texto para indexar
[ index setIndexer: ^MHIndexedObject *(MyCustomObject *object, NSData *identifier){
MHIndexedObject *indx = [MHIndexedObject new ];
indx. strings = @[ object.title, object.description ]; // Indexed strings
indx. weight = object. awesomenessLevel ; // Weight given to this object, when sorting results
indx. context = @{ @" title " : object. title }; // A NSDictionary that will be given alongside search results
return indx;
}];Finalmente, si desea poder obtener una referencia fácil a su objeto original cuando obtiene resultados de búsqueda, puede decirnos cómo hacerlo por usted.
[ index setObjectGetter: ^MyCustomObject *( NSData *identifier){
return [MyCustomObject customObjectFromIdentifier: identifier];
}];¡Y eso es todo! Eso es todo lo que necesita para obtener un índice de texto completo. MHTextSearch se encarga de dividir el texto en palabras, factorizando a los diacríticos y la capitalización, todo con respecto al localidad, como era de esperar (bueno, la base hace la mayor parte del trabajo aquí).
Entonces puedes comenzar a buscar:
[ index enumerateObjectsForKeyword: @" duck " options: 0 withBlock: ^(MHSearchResultItem *item,
NSUInteger rank,
NSUInteger count,
BOOL *stop){
item. weight ; // As provided by you earlier
item. rank ; // The effective rank in the search result
item. object ; // The first time it is used, it will use the block
// you provided earlier to get the object
item. context ; // The dictionary you provided in the "indexer" block
item. identifier ; // The object identifier you provided in the "identifier" block
NSIndexPath *token = item. resultTokens [ 0 ];
/* This is an NSArray of NSIndexPath instances, each containing 3 indices:
* - mh_string : the string in which the token occured
* (here, 0 for the object's title)
* - mh_word : the position in the string where the word containing
* the token occured
* - mh_token : the position in the word where the token occured
*/
NSRange tokenRange = [item rangeOfToken: token];
/* This gives the exact range of the matched token in the string where it was found.
*
* So, according to the example setup I've been giving from the start,
* if token.mh_string == 0, that means the token was found in the object's "title",
* and [item.object.title substringWithRange:tokenRange] would yield "duck" (minus
* capitalization and diacritics).
*/
}]; También puede obtener toda la variedad de instancias de MHSearchResultItem a la vez usando
NSArray *resultSet = [ index searchResultForKeyword: @" duck "
options: NSEnumerationReverse];Si no es lo suyo dar bloques para especificar el comportamiento, también puede anular los siguientes métodos:
-[MHTextIndex getIdentifierForObject:] que, por defecto, usa el bloque identifier-[MHTextIndex getIndexInfoForObject:andIdentifier:] que, por defecto, usa el bloque indexer-[MHTextIndex compareResultItem:withItem:reversed:] que se usa para ordenar el conjunto de resultados de búsqueda Puede usar los métodos de ciclo de vida NSManagedObject para desencadenar cambios en el índice de texto. El siguiente ejemplo fue tomado de http://www.adevelopingstory.com/blog/2013/04/adding-full-text-search-to-core-data.html y adaptado para usar con este proyecto:
- ( void )prepareForDeletion
{
[ super prepareForDeletion ];
if (self. indexID . length ) {
[textindex deleteIndexForObject: self .indexID];
}
}
+ ( NSData *)createIndexID {
NSUUID *uuid = [ NSUUID UUID ];
uuid_t uuidBytes;
[uuid getUUIDBytes: uuidBytes];
return [ NSData dataWithBytes: uuidBytes length: 16 ];
}
- ( void )willSave
{
[ super willSave ];
if (self. indexID . length ) {
[textindex updateIndexForObject: self .indexID];
} else {
self. indexID = [[ self class ] createIndexID ];
[textindex indexObject: self .indexID];
}
} MHTextIndex utiliza una NSOperationQueue debajo del capó para coordinar las operaciones de indexación. Está expuesto como una propiedad llamada indexingQueue . Por lo tanto, puede establecer su propiedad maxConcurrentOperationCount para controlar cuán concurrente puede ser la indexación. Dado que la biblioteca de base de datos subyacente que realiza E/S es segura de hilo, la concurrencia no es un problema. Esto también significa que puede esperar explícitamente a que las operaciones de indexación terminen de usar:
[ index .indexingQueue waitUntilAllOperationsAreFinished ]; Los tres métodos de indexación -[MHTextIndex indexObject:] , -[MHTextIndex updateIndexForObject:] , -[MHTextIndex removeIndexForObject:] todas las instancias NSOperation de retorno, que puede aprovechar, si lo necesita, utilizando su propiedad completionBlock o -[NSOperation waitUntilFinished] método.
La búsqueda también es concurrente, pero usa un dispatch_queue_t (aún no expuesto o sintonizable).
Hay algunas perillas con las que puedes jugar para hacer que MHTextSeach se ajuste mejor a tus necesidades.
Una instancia MHTextIndex tiene una propiedad booleana skipStopWords que es verdadera de forma predeterminada y que evita indexar palabras en inglés muy comunes. ( TODO : Haz que funcione con otros idiomas)
También tiene una minimalTokenLength que es igual a 2 por defecto. Esto establece un mínimo para el número de letras que un token debe ser para que se indexe. Esto también minimiza en gran medida el tamaño del índice, así como el tiempo de indexación y búsqueda. Se omite indexar palabras de una sola letra y la última letra de cada palabra, cuando se establece en 2 .
Al indexar textos de formularios largos (documentos en lugar de, por ejemplo, nombres simples), puede encender la propiedad booleana discardDuplicateTokens en MHTextIndex . Esto hace que el índice solo considere la primera ocurrencia de cada token indexado para una pieza dada de textos. Si está de acuerdo con saber solo si aparece un token en un texto, en lugar de dónde aparece cada ocurrencia, puede obtener un aumento de velocidad en el tiempo de indexación , por un factor de 3 a 5.
Los siguientes gráficos muestran el tiempo de indexación y búsqueda (en segundos), en función del tamaño del texto indexado, que varía de 500 kb a aproximadamente 10 MB. Los puntos de referencia se ejecutaron en un iPhone 5.
Si desea ejecutar las pruebas, necesitará Xcode 5, ya que el conjunto de pruebas usa el nuevo XCTEST.
Clonar este repositorio y, una vez en él,
$ cd MHTextSearch iOS Tests
$ pod install
$ cd .. && open * .xcworkspaceActualmente, todas las pruebas se configuraron para trabajar con la suite de prueba iOS.
Distribuido bajo la licencia MIT