Une bibliothèque d'indexation de texte intégrale rapide et minimal, écrite dans Objective-C, construite au-dessus de Objective-LevelDB.
De loin, le moyen le plus simple d'intégrer cette bibliothèque dans votre projet consiste à utiliser des cocoapodes.
Faire installer des cocoapodes, si vous ne l'avez pas déjà
Dans votre podfile, ajoutez la ligne
pod 'MHTextSearch'
Installation pod install
Ajoutez le framework libc++.dylib à votre projet.
MHTextIndex *index = [MHTextIndex textIndexInLibraryWithName: @" my.awesome.index " ]; Vous pouvez dire à une instance MHTextIndex pour indexer vos objets (n'importe quel objet)
[ index indexObject: anyObjectYouWant];
[ index updateIndexForObject: anotherPreviousIndexedObject];
[ index removeIndexForObject: anotherPreviousIndexedObject]; Mais pour que cela fonctionne, vous devez nous dire quel identifiant comme NSData * peut être utilisé pour se référer de manière unique à cet objet.
[ index setIdentifier: ^ NSData *(MyCustomObject *object){
return object. indexID ; // a NSData instance
}];Vous devez également nous donner des détails sur les objets, comme quels sont les morceaux de texte à indexer
[ 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;
}];Enfin, si vous voulez pouvoir obtenir une référence facile à votre objet d'origine lorsque vous obtenez les résultats de recherche, vous pouvez nous dire comment le faire pour vous
[ index setObjectGetter: ^MyCustomObject *( NSData *identifier){
return [MyCustomObject customObjectFromIdentifier: identifier];
}];Et c'est tout! C'est tout ce dont vous avez besoin pour faire fonctionner un index complet. MHTextSearch s'occupe de diviser le texte en mots, de tenir compte de la diacritique et de la capitalisation, le tout en ce qui concerne les paramètres régionaux, comme vous vous en doutez (eh bien, Foundation fait la majeure partie du travail ici).
Vous pouvez alors commencer à rechercher:
[ 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).
*/
}]; Vous pouvez également récupérer l'ensemble des instances MHSearchResultItem en utilisant à la fois
NSArray *resultSet = [ index searchResultForKeyword: @" duck "
options: NSEnumerationReverse];Si donner des blocs pour spécifier le comportement n'est pas votre truc, vous pouvez également remplacer les méthodes suivantes:
-[MHTextIndex getIdentifierForObject:] qui, par défaut, utilise le bloc identifier-[MHTextIndex getIndexInfoForObject:andIdentifier:] qui, par défaut, utilise le bloc indexer-[MHTextIndex compareResultItem:withItem:reversed:] qui est utilisé pour commander le jeu de résultats de recherche Vous pouvez utiliser les méthodes de cycle de vie NSManagedObject pour déclencher des modifications à l'index de texte. L'exemple suivant a été pris à partir de http://www.adevelopingstory.com/blog/2013/04/adding-full-text-search-to-ore-cata.html et adapté à l'utilisation avec ce projet:
- ( 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 utilise un NSOperationQueue sous le capot pour coordonner les opérations d'indexation. Il est exposé en tant que propriété nommée indexingQueue . Vous pouvez donc définir sa propriété maxConcurrentOperationCount pour contrôler la concurrence de l'indexation. Étant donné que la bibliothèque de bases de données sous-jacente effectuant des E / S est en file d'attente, la concurrence n'est pas un problème. Cela signifie également que vous pouvez attendre explicitement que les opérations d'indexation se terminent en utilisant:
[ index .indexingQueue waitUntilAllOperationsAreFinished ]; Les trois méthodes d'indexation -[MHTextIndex indexObject:] , -[MHTextIndex updateIndexForObject:] , -[MHTextIndex removeIndexForObject:] Tous renvoient les instances NSOperation , dont vous pouvez profiter, si vous avez besoin, en utilisant sa propriété completionBlock ou -[NSOperation waitUntilFinished] Méthode.
La recherche est également simultanée, mais elle utilise un dispatch_queue_t (pas encore exposé ou accordable).
Il y a quelques boutons avec lesquels vous pouvez jouer pour faire en sorte que MHTextSeach s'adapte à vos besoins.
Une instance MHTextIndex a une propriété booléenne skipStopWords qui est vraie par défaut et qui évite d'indexer des mots anglais très courants. ( Todo : faites-le fonctionner avec d'autres langues)
Il a également une minimalTokenLength de longueur égale à 2 par défaut. Cela définit un minimum pour le nombre de lettres qu'un jeton doit être indexé. Cela minimise également considérablement la taille de l'index, ainsi que le temps d'indexation et de recherche. Il saute l'indexation des mots à une lettre unique et la dernière lettre de chaque mot, lorsqu'il est défini sur 2 .
Lorsque vous indexez des textes de formulaire long (documents plutôt que, disons, des noms simples), vous pouvez activer la propriété booléenne discardDuplicateTokens sur MHTextIndex . Cela ne fait que l'index ne considère que la première occurrence de chaque jeton indexé pour un morceau de textes donné. Si vous êtes d'accord avec seulement savoir si un jeton apparaît dans un texte, plutôt que où chaque occurrence apparaît, vous pouvez gagner une vitesse de vitesse en temps d'indexation , d'un facteur de 3 à 5.
Les graphiques suivants montrent le temps d'indexation et de recherche (en secondes), en fonction de la taille du texte indexé, allant de 500 Ko à environ 10 Mo. Les repères ont été exécutés sur un iPhone 5.
Si vous souhaitez exécuter les tests, vous aurez besoin de Xcode 5, car la suite de tests utilise le nouveau XCTEST.
Cloner ce référentiel et, une fois,
$ cd MHTextSearch iOS Tests
$ pod install
$ cd .. && open * .xcworkspaceActuellement, tous les tests ont été configurés pour travailler avec la suite de tests iOS.
Distribué sous la licence du MIT