Comment optimiser la requête de base de données pour la recherche en texte intégral
Puis-je optimiser une Base de Données de requête lors de la recherche pour la mise en correspondance des mots dans un texte? (Cette question concerne également la sagesse de SQL personnalisée rapport de Données de Base sur un iPhone.)
Je suis en train de travailler sur un nouveau (iPhone) de l'application qui est un ordinateur de poche outil de référence pour une base de données scientifiques. L'interface principale est une norme consultable vue de la table et je veux que-vous-type de réponse que l'utilisateur tape des mots nouveaux. Mots matchs doit être préfixes des mots dans le texte. Le texte est composé de 100 000 s de mots.
Dans mon prototype j'ai codé SQL directement. J'ai créé un des "mots" de la table contenant tous les mots dans le texte des champs de l'entité principale. J'mots et effectué des recherches le long de la lignes de
SELECT id, * FROM textTable
JOIN (SELECT DISTINCT textTableId FROM words
WHERE word BETWEEN 'foo' AND 'fooz' )
ON id=textTableId
LIMIT 50
Cela va très vite. À l'aide d'un EN serait probablement tout aussi bien, c'est à dire
SELECT * FROM textTable
WHERE id IN (SELECT textTableId FROM words
WHERE word BETWEEN 'foo' AND 'fooz' )
LIMIT 50
La LIMITE est crucial et me permet d'afficher des résultats rapidement. Je l'ai signaler à l'utilisateur qu'il y a de trop nombreux à afficher si la limite est atteinte. C'est encombrants.
J'ai passé les derniers jours à méditer sur les avantages d'un déplacement de Base de Données, mais je m'inquiète de l'absence de contrôle dans le schéma, l'indexation et l'interrogation d'une importante requête.
Théoriquement, un NSPredicate de textField MATCHES '.*\bfoo.*'
serait juste de travailler, mais je suis sûr que ce sera lent. Cette sorte de recherche de texte semble tellement commun que je me demande qu'est-ce que l'habitude d'attaque? Voulez-vous créer un mots entité comme je l'ai fait ci-dessus et utiliser un prédicat de "mot BEGINSWITH 'foo'"? Est-ce aussi vite que mon prototype? Sera Base de Données de créer automatiquement le droit d'index? Je ne trouve pas de moyens explicites de conseiller le magasin persistant sur les index.
Je voir quelques belles avantages de Base de Données dans mon iPhone. Les failles et d'autres considérations relatives à la mémoire de permettre la base de données efficace pour les récupérations pour tableview requêtes, sans fixer de limites arbitraires. L'objet graphique de gestion me permet de parcourir aisément les entités sans écrire beaucoup de code SQL. Caractéristiques de Migration va être sympa dans l'avenir. D'autre part, dans une ressource limitée de l'environnement (iPhone) j'ai peur qu'généré automatiquement la base de données va être gonflé à l'aide de métadonnées, inutile inverse des relations, de l'inefficacité de l'attribut type de données, etc.
Doit je faire de la plongée ou de procéder avec prudence?
source d'informationauteur dk.
Vous devez vous connecter pour publier un commentaire.
J'ai fait une solution de rechange. Je pense que c'est similaire à ce post. J'ai ajouté la fusion de code source de ma Base de Données de projet, puis il a créé une recherche de texte intégral de la classe qui n'était pas un objet géré sous-classe. Dans le FTS de classe I
#import "sqlite3.h"
(le fichier source) au lieu de l'sqlite cadre. Le FTS sauve de classe à une autre .sqlite fichier de la Base de Données de persistance.Lorsque j'importe mes données, la Base de Données objet stocke les rowid de la FTS objet comme un attribut entier. J'ai un statique dataset, donc je ne vous inquiétez pas à propos de l'intégrité référentielle, mais le code afin de maintenir l'intégrité doit être trivial.
Pour effectuer FTS, je
MATCH
requête de la FTS classe, en retournant un ensemble de rowids. Dans ma classe d'objets gérés, j'ai une requête pour les objets correspondants avec[NSPredicate predicateWithFormat:@"rowid IN %@", rowids]
. - Je éviter de traverser toute plusieurs-à-plusieurs liens de cette façon.L'amélioration de la performance est spectaculaire. Mon dataset est 142287 lignes, composé de 194 MO (Base de Données) et 92 MO (FTS avec les mots vides supprimé). Selon le terme de recherche de fréquence, mes recherches sont allés de plusieurs secondes à 0,1 secondes pour peu fréquents termes (<100 hits) et de 0,2 secondes pour fréquentes termes (>2000 visiteurs).
Je suis sûr qu'il ya une myriade de problèmes avec mon approche (code de ballonnements, d'éventuelles collisions d'espace de noms, la perte de certaines Données de Base des fonctionnalités), mais il semble fonctionner.
Pour le suivi de cette question, j'ai trouvé que l'interrogation est chien lente à l'aide de Données de Base. J'ai gratté ma tête sur celui-ci pendant de nombreuses heures.
Comme dans l'exemple SQL dans ma question, il y a deux entités: textTable et des mots où les mots contient chaque mot, il est indexé, et il y a un plusieurs-à-plusieurs relation entre textTable et des mots. J'ai remplie la base de données avec un simple 4000 mots et 360 textTable objets. Supposons que le textTable relation les paroles de l'objet est appelé searchWords, alors je peux utiliser un prédicat sur les textTable entité qui ressemble à
(Je peux ajouter des conjonctions de ce prédicat pour de multiples termes de la requête.)
Sur l'iPhone, cette requête prend plusieurs secondes. La réponse pour mon codée à la main SQL à l'aide d'un plus grand ensemble de test a été instantanée.
Mais ce n'est même pas la fin de celui-ci. Il y a des limites à NSPredicate qui font plutôt de simples requêtes lentes et complexes. Imaginez, par exemple, dans l'exemple ci-dessus que vous souhaitez filtrer à l'aide d'un bouton portée. Supposons que les mots entité contient tous les mots de tous les champs de texte, mais la portée serait de les limiter à des mots à partir des domaines spécifiques. Ainsi, les mots peuvent avoir un attribut "source" (par exemple, en-tête et le corps de message e-mail).
Naturellement, d'un texte complet serait ignorer la source de l'attribut, comme dans l'exemple ci-dessus, mais une requête filtrée limiter la recherche à une source de valeur. Cette apparemment simple changement nécessite une sous-REQUÊTE. Par exemple, cela ne fonctionne pas:
parce que les entités qui sont vraies pour les deux expressions peuvent être différentes. Au lieu de cela, vous avez à faire quelque chose comme:
J'ai trouvé que ces sous-requêtes sont, sans surprise, plus lent que les prédicats à l'aide de "TOUT".
À ce point, je suis très curieux de voir comment le Cacao programmeurs utilisent efficacement les Données de Base pour la recherche plein texte parce que je suis découragé par deux la vitesse de l'évaluation du prédicat et expressibility de NSPredicates. J'ai couru contre un mur.
De plongée.
Voici une façon d'aller à ce sujet:
NSFetchedResultsController
pour gérer un ensemble de résultats sur votreWord
entités (Base de Données équivalent avec SQL "les mots" table)UISearchDisplayController
pour appliquer uneNSPredicate
sur l'ensemble des résultats en temps réelUne fois que vous avez un ensemble de résultats via
NSFetchedResultsController
il est assez facile d'appliquer un prédicat. Dans mon expérience, il sera sensible, trop. Par exemple:permettra de filtrer le résultat
[self.fetchedResultsController fetchedObjects]
à la volée, en faisant de la casse de la recherche surword
.Après avoir lutté avec cette même question, j'ai couru à travers une série d'articles où l'auteur a eu le même problème et est venu avec cette solution. Il signale une amélioration à partir de 6-7 deuxième temps de recherche entre 0,13 et de 0,05 secondes.
Son dataset pour FTS a été de 79 documents (taille du fichier 175k, 3600 discret jetons, 10000 références). Je n'ai pas encore essayé cette solution, mais pensé que je poste dès que possible. Voir aussi Partie 2 de ses messages pour la documentation du problème et Partie 1 pour la documentation de la base de données.