Google Firestore: requête sur la sous-chaîne d'une valeur de propriété (recherche de texte)

106

Je cherche à ajouter un champ de recherche simple, j'aimerais utiliser quelque chose comme

collectionRef.where('name', 'contains', 'searchTerm')

J'ai essayé d'utiliser where('name', '==', '%searchTerm%'), mais cela n'a rien retourné.

tehfailsafe
la source
1
Firebase prend désormais en charge cela. Veuillez mettre à jour la réponse: stackoverflow.com/a/52715590/2057171
Albert Renshaw
1
Je pense que le meilleur moyen est de créer un script qui indexe manuellement chaque document. Puis interrogez ces index. consultez ceci: angularfirebase.com/lessons/…
Kiblawi_Rabee

Réponses:

38

Il n'y a pas tel opérateur, les permis sont ==, <, <=, >, >=.

Vous pouvez filtrer par préfixes uniquement, par exemple pour tout ce qui commence entre baret foovous pouvez utiliser

collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')

Vous pouvez utiliser un service externe comme Algolia ou ElasticSearch pour cela.

Kuba
la source
5
Ce n'est pas tout à fait ce que je recherche. J'ai une grande liste de produits avec de longs titres. "Raquette de tennis pour hommes Rebok". Un utilisateur peut rechercher tennis, mais en fonction des opérateurs de requête disponibles, il n'y a aucun moyen d'obtenir ces résultats. Combiner >=et <=ne fonctionne pas. Bien sûr, je peux utiliser Algolia, mais je pourrais aussi l'utiliser avec Firebase pour faire la plupart des requêtes sans avoir besoin de passer à Firestore ...
tehfailsafe
4
@tehfailsafe Eh bien, votre question est «comment demander si un champ contient une chaîne», et la réponse est «vous ne pouvez pas faire cela».
Kuba
20
@ A.Chakroun qu'est-ce qui est exactement impoli dans ma réponse?
Kuba
19
tbh c'est quelque chose de vraiment nécessaire. Je ne peux pas comprendre pourquoi l'équipe de Firebase n'a pas réfléchi à cela
Dani
2
Vraiment surpris que Firebase soit si faible dans les requêtes. Je ne peux pas croire qu'il y ait autant de personnes qui l'utilisent s'il ne peut pas prendre en charge une requête aussi simple.
Bagusflyer le
46

Je suis d'accord avec la réponse de @ Kuba, mais il faut quand même ajouter un petit changement pour fonctionner parfaitement pour la recherche par préfixe. voici ce qui a fonctionné pour moi

Pour rechercher des enregistrements commençant par le nom queryText

collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff').

Le caractère \uf8ffutilisé dans la requête est un point de code très élevé dans la plage Unicode (il s'agit d'un code de zone d'utilisation privée [PUA]). Comme elle se trouve après la plupart des caractères normaux en Unicode, la requête correspond à toutes les valeurs commençant par queryText.

Ankit Prajapati
la source
1
Bonne réponse!, Cela fonctionne très bien pour la recherche de texte de préfixe. Pour rechercher des mots dans le texte, vous pouvez essayer l'implémentation "array-contains" comme expliqué dans ce post medium.com/@ken11zer01/…
guillefd
Je pense juste, mais théoriquement, vous pouvez faire correspondre toutes les valeurs qui se terminent par queryTest en créant un autre champ et en inversant les données ...
Jonathan
Oui @Jonathan, c'est aussi possible.
Ankit Prajapati
43

Bien que la réponse de Kuba soit vraie en ce qui concerne les restrictions, vous pouvez partiellement émuler cela avec une structure semblable à un ensemble:

{
  'terms': {
    'reebok': true,
    'mens': true,
    'tennis': true,
    'racket': true
  }
}

Vous pouvez maintenant interroger avec

collectionRef.where('terms.tennis', '==', true)

Cela fonctionne car Firestore créera automatiquement un index pour chaque champ. Malheureusement, cela ne fonctionne pas directement pour les requêtes composées car Firestore ne crée pas automatiquement d'index composites.

Vous pouvez toujours contourner ce problème en stockant des combinaisons de mots, mais cela devient très rapide.

Vous êtes probablement toujours mieux avec une recherche de texte intégral hors-bord .

Gil Gilbert
la source
Est-il possible d'utiliser la fonction cloud avec cloud.google.com/appengine/docs/standard/java/search ?
Henry
1
Si vous posez cette question comme suite à cette réponse, alors: La recherche en texte intégral d'AppEngine est complètement séparée de Firestore et cela ne vous aidera donc pas directement. Vous pouvez répliquer vos données à l'aide d'une fonction cloud, mais c'est essentiellement ce que suggère d'utiliser la recherche de texte intégral externe. Si vous posez autre chose, veuillez commencer une nouvelle question.
Gil Gilbert
1
Dans Firestore, vous devez indexer tous les termes avant d'utiliserwhere
Husam
4
Comme Husam l'a mentionné, tous ces champs devraient être indexés. Je souhaitais activer la recherche de tout terme contenu dans le nom de mon produit. J'ai donc créé une propriété de type `` objet '' sur mon document avec des clés faisant partie du nom du produit, chacune avec la valeur `` true '' qui lui est attribuée, en espérant que la recherche où ('nameSegments.tennis', '==', true) serait travail, mais Firestore suggère de créer un index pour nameSegments.tennis, comme pour tous les autres termes. Puisqu'il peut y avoir un nombre infini de termes, cette réponse n'est utilisable que pour un scénario très limité lorsque tous les termes de recherche sont définis à l'avance.
Slawoj
2
@epeleg La requête fonctionnerait, après avoir créé un index pour elle, mais il n'est pas possible de créer un index pour chaque terme possible que votre nom de produit contient, donc pour la recherche textuelle des termes dans les noms de produits, cette approche n'a pas fonctionné pour mon cas.
Slawoj
30

Alors que Firebase ne prend pas explicitement en charge la recherche d'un terme dans une chaîne,

Firebase prend (maintenant) en charge les éléments suivants qui résoudront votre cas et bien d'autres:

Depuis août 2018, ils prennent en charge la array-containsrequête. Voir: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html

Vous pouvez maintenant définir tous vos termes clés dans un tableau en tant que champ, puis rechercher tous les documents qui ont un tableau contenant «X». Vous pouvez utiliser ET logique pour effectuer d'autres comparaisons pour des requêtes supplémentaires. (Ceci est dû au fait que Firebase ne prend actuellement pas en charge de manière native les requêtes composées pour plusieurs requêtes contenant un tableau, donc les requêtes de tri 'ET' devront être effectuées côté client)

L'utilisation de tableaux dans ce style leur permettra d'être optimisés pour les écritures simultanées, ce qui est bien! Je n'ai pas testé qu'il prend en charge les demandes par lots (les documents ne le disent pas), mais je parierais qu'il le fait car c'est une solution officielle.


Usage:

collection("collectionPath").
    where("searchTermsArray", "array-contains", "term").get()
Albert Renshaw
la source
12
C'est une bonne solution. Cependant, corrigez-moi si je me trompe, mais je pense que cela ne vous permet pas de faire ce que @tehfailsafe a demandé. Par exemple, si vous voulez obtenir tous les noms contenant la chaîne "abc", vous n'obtiendrez aucun succès avec array-contains, car il ne renverra que les documents qui ont le nom exact de "abc", mais "abcD" ou "0abc" serait sorti.
Yulian
1
@Yulian Dans le monde de la programmation, Search termon entend généralement un terme entier séparé par un espace, une ponctuation, etc. des deux côtés. Si vous google en abcdece moment, vous ne trouverez que des résultats pour des choses comme %20abcde.ou ,abcde!mais pas abcdefghijk... même si tout l'alphabet tapé est sûrement beaucoup plus courant sur Internet, la recherche n'est pas pour abcde * c'est pour un abcde isolé
Albert Renshaw
1
Je comprends votre argument et je suis d'accord avec lui, mais j'ai probablement été induit en erreur par le mot 'contains', qui signifie exactement ce à quoi je fais référence dans de nombreux langages de programmation. Il en va de même '%searchTerm%'d'un point de vue SQL.
Yulian
2
@Yulian Ouais je comprends ça. Firebase est cependant NoSQL, donc c'est vraiment bon pour rendre ces types d'opérations rapides et efficaces, même si elles peuvent être limitées pour certains problèmes hors de portée comme la recherche de chaînes génériques.
Albert Renshaw
2
Eh bien, vous pouvez créer un champ séparé pour chacun avec la représentation des mots séparés comme titleArray: ['this', 'is', 'a', 'title'] chaque fois que vous mettez à jour le document. Et puis la recherche sera basée sur ce champ au lieu du titre. Vous créez à froid un onUpdate triiger pour créer ces champs. Beaucoup de travail pour le texte basé sur la recherche, mais j'ai plutôt les améliorations de performances de NoSQL.
sfratini
15

Selon les documents Firestore , Cloud Firestore ne prend pas en charge l'indexation native ni la recherche de champs de texte dans les documents. De plus, télécharger une collection entière pour rechercher des champs côté client n'est pas pratique.

Des solutions de recherche tierces comme Algolia et Elastic Search sont recommandées.

Bholben
la source
46
J'ai lu la documentation, même si ce n'est pas idéal. L'inconvénient est qu'Algolia et Firestore ont des modèles de tarification différents ... Je peux heureusement avoir 600 000 documents dans Firestore (tant que je n'en interroge pas trop par jour). Lorsque je les pousse vers Algolia pour faire des recherches, je dois maintenant payer 310 $ par mois à Algolia juste pour pouvoir faire une recherche de titre sur mes documents Firestore.
tehfailsafe
2
le problème est que ce n'est pas gratuit
Dani
C'est la bonne réponse à la question posée et devrait être acceptée comme la meilleure.
briznad
13

Quelques notes ici:

1.) \uf8ff fonctionne de la même manière que~

2.) Vous pouvez utiliser une clause where ou des clauses start end:

ref.orderBy('title').startAt(term).endAt(term + '~');

est exactement le même que

ref.where('title', '>=', term).where('title', '<=', term + '~');

3.) Non, cela ne fonctionne pas si vous inversez startAt()et endAt()dans chaque combinaison, cependant, vous pouvez obtenir le même résultat en créant un deuxième champ de recherche inversé et en combinant les résultats.

Exemple: vous devez d'abord enregistrer une version inversée du champ lors de la création du champ. Quelque chose comme ça:

// collection
const postRef = db.collection('posts')

async function searchTitle(term) {

  // reverse term
  const termR = term.split("").reverse().join("");

  // define queries
  const titles = postRef.orderBy('title').startAt(term).endAt(term + '~').get();
  const titlesR = postRef.orderBy('titleRev').startAt(termR).endAt(termR + '~').get();

  // get queries
  const [titleSnap, titlesRSnap] = await Promise.all([
    titles,
    titlesR
  ]);
  return (titleSnap.docs).concat(titlesRSnap.docs);
}

Avec cela, vous pouvez rechercher les dernières lettres d'un champ de chaîne et les premières lettres du milieu ou groupes de lettres, mais pas aléatoires. C'est plus proche du résultat souhaité. Cependant, cela ne nous aidera pas vraiment lorsque nous voulons des lettres intermédiaires ou des mots aléatoires. N'oubliez pas non plus de sauvegarder tout en minuscules ou une copie en minuscules pour la recherche, afin que la casse ne soit pas un problème.

4.) Si vous n'avez que quelques mots, la méthode de Ken Tan fera tout ce que vous voulez, ou du moins après l'avoir légèrement modifiée. Cependant, avec seulement un paragraphe de texte, vous créerez de manière exponentielle plus de 1 Mo de données, ce qui est plus grand que la taille limite du document de Firestore (je sais, je l'ai testé).

5.) Si vous pouviez combiner array-contains (ou une forme quelconque de tableaux) avec l' \uf8ffastuce, vous pourriez avoir une recherche viable qui n'atteindrait pas les limites. J'ai essayé toutes les combinaisons, même avec des cartes, et je n'ai pas réussi. N'importe qui le comprend, postez-le ici.

6.) Si vous devez vous éloigner de ALGOLIA et ELASTIC SEARCH et que je ne vous en veux pas du tout, vous pouvez toujours utiliser mySQL, postSQL ou neo4Js sur Google Cloud. Ils sont tous 3 faciles à configurer et ont des niveaux gratuits. Vous auriez une fonction cloud pour enregistrer les données onCreate () et une autre fonction onCall () pour rechercher les données. Simple ... ish. Pourquoi ne pas simplement passer à mySQL alors? Les données en temps réel bien sûr! Quand quelqu'un écrit DGraph avec des websocks pour des données en temps réel, comptez-moi!

Algolia et ElasticSearch ont été conçus pour être des bases de données de recherche uniquement, il n'y a donc rien d'aussi rapide ... mais vous payez pour cela. Google, pourquoi nous éloignez-vous de Google et ne suivez-vous pas MongoDB noSQL et n'autorisez pas les recherches?

MISE À JOUR - J'AI CRÉÉ UNE SOLUTION:

https://fireblog.io/blog/post/firestore-full-text-search

Jonathan
la source
Un bon aperçu et très utile.
RedFilter
Impressionnant! upvote pour une réponse bien structurée et informative.
King Of The Jungle
10

Réponse tardive mais pour tous ceux qui recherchent encore une réponse, disons que nous avons une collection d'utilisateurs et que dans chaque document de la collection, nous avons un champ "username", donc si vous voulez trouver un document où le nom d'utilisateur commence par "al" on peut faire quelque chose comme

 FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")
MoTahir
la source
c'est une excellente solution facile merci. Mais que faire si vous voulez vérifier plus d'un champ. Comme "nom" et "description" reliés par un OU?
Essayez-le le
Je ne pense pas que vous puissiez interroger sur la base de deux champs, malheureusement, Firebase est mauvais en ce qui concerne les requêtes, vous pouvez vérifier cela en espérant que cela aidera stackoverflow.com/questions/26700924/…
MoTahir
1
Confirmé, @MoTahir. Il n'y a pas de "OU" dans Firestore.
Rap
cette solution ne correspond pas aux noms d'utilisateur commençant par "al" ... par exemple, "hello" correspondrait ("hello"> "al")
antoine129
L'interrogation par OR consiste simplement à combiner deux résultats de recherche. Trier ces résultats est un problème différent ...
Jonathan le
7

Je suis sûr que Firebase sortira bientôt avec "string-contains" pour capturer n'importe quel index [i] startAt dans la chaîne ... Mais j'ai recherché les sites Web et trouvé cette solution pensée par quelqu'un d'autre a configuré vos données comme ce

state = {title:"Knitting"}
...
const c = this.state.title.toLowerCase()

var array = [];
for (let i = 1; i < c.length + 1; i++) {
 array.push(c.substring(0, i));
}

firebase
.firestore()
.collection("clubs")
.doc(documentId)
.update({
 title: this.state.title,
 titleAsArray: array
})

entrez la description de l'image ici

requête comme celle-ci

firebase
.firestore()
.collection("clubs")
.where(
 "titleAsArray",
 "array-contains",
 this.state.userQuery.toLowerCase()
)
Nick Carducci
la source
Pas du tout recommandé. Comme les documents ont une limite de 20k lignes, vous ne pouvez donc pas l'utiliser de cette façon, jusqu'à ce que vous soyez sûr que votre document n'atteindra jamais de telles limites
Sandeep
C'est la meilleure option pour le moment, qu'est-ce qui est recommandé d'autre?
Nick Carducci
1
@Sandeep Je suis presque sûr que la taille est limitée à 1 Mo de taille et 20 niveaux de profondeur par document. Qu'entendez-vous par 20 000 lignes? C'est actuellement la meilleure solution de contournement si l'utilisation d'Algolia ou d'ElasticSearch est hors de
propos
6

Si vous ne souhaitez pas utiliser un service tiers comme Algolia, Firebase Cloud Functions est une excellente alternative. Vous pouvez créer une fonction qui peut recevoir un paramètre d'entrée, traiter les enregistrements côté serveur, puis renvoyer ceux qui correspondent à vos critères.

Rap
la source
1
Et Android?
Pratik Butani
Proposez-vous que les gens parcourent chaque enregistrement d'une collection?
DarkNeuron le
Pas vraiment. J'utiliserais Array.prototype. * - Comme .every (), .some (), .map (), .filter (), etc. Ceci est fait dans Node sur le serveur dans une fonction Firebase avant de retourner les valeurs au client.
Rap
3
Vous auriez encore à lire TOUS les documents pour les rechercher, ce qui entraîne des dépenses et coûte cher en temps.
Jonathan le
4

La réponse sélectionnée ne fonctionne que pour les recherches exactes et ne correspond pas au comportement de recherche naturel des utilisateurs (la recherche de «pomme» dans «Joe a mangé une pomme aujourd'hui» ne fonctionnerait pas).

Je pense que la réponse de Dan Fein ci-dessus devrait être mieux classée. Si les données String que vous recherchez sont courtes, vous pouvez enregistrer toutes les sous-chaînes de la chaîne dans un tableau de votre document, puis rechercher dans le tableau avec la requête array_contains de Firebase. Les documents Firebase sont limités à 1 Mio (1 048 576 octets) ( Quotas et limites Firebase ), soit environ 1 million de caractères enregistrés dans un document (je pense que 1 caractère ~ = 1 octet). Le stockage des sous-chaînes est correct tant que votre document n'est pas proche de 1 million de marques.

Exemple de recherche de noms d'utilisateurs:

Étape 1: ajoutez l'extension String suivante à votre projet. Cela vous permet de diviser facilement une chaîne en sous-chaînes. ( J'ai trouvé ça ici ).

extension String {

var length: Int {
    return count
}

subscript (i: Int) -> String {
    return self[i ..< i + 1]
}

func substring(fromIndex: Int) -> String {
    return self[min(fromIndex, length) ..< length]
}

func substring(toIndex: Int) -> String {
    return self[0 ..< max(0, toIndex)]
}

subscript (r: Range<Int>) -> String {
    let range = Range(uncheckedBounds: (lower: max(0, min(length, r.lowerBound)),
                                        upper: min(length, max(0, r.upperBound))))
    let start = index(startIndex, offsetBy: range.lowerBound)
    let end = index(start, offsetBy: range.upperBound - range.lowerBound)
    return String(self[start ..< end])
}

Étape 2: lorsque vous stockez le nom d'un utilisateur, stockez également le résultat de cette fonction sous forme de tableau dans le même document. Cela crée toutes les variations du texte d'origine et les stocke dans un tableau. Par exemple, l'entrée de texte "Apple" crée le tableau suivant: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le "," app "," ppl "," ple "," appl "," pple "," apple "], qui devrait englober tous les critères de recherche qu'un utilisateur peut entrer. Vous pouvez laisser maximumStringSize comme nul si vous voulez tous les résultats, cependant, s'il y a du texte long, je recommanderais de le plafonner avant que la taille du document ne devienne trop grande - quelque part environ 15 fonctionne bien pour moi (la plupart des gens ne recherchent pas de longues phrases de toute façon ).

func createSubstringArray(forText text: String, maximumStringSize: Int?) -> [String] {

    var substringArray = [String]()
    var characterCounter = 1
    let textLowercased = text.lowercased()

    let characterCount = text.count
    for _ in 0...characterCount {
        for x in 0...characterCount {
            let lastCharacter = x + characterCounter
            if lastCharacter <= characterCount {
                let substring = textLowercased[x..<lastCharacter]
                substringArray.append(substring)
            }
        }
        characterCounter += 1

        if let max = maximumStringSize, characterCounter > max {
            break
        }
    }

    print(substringArray)
    return substringArray
}

Étape 3: Vous pouvez utiliser la fonction array_contains de Firebase!

[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....
pur pain
la source
3

Je pense en fait que la meilleure solution pour faire cela dans Firestore est de mettre toutes les sous-chaînes dans un tableau et de simplement faire une requête array_contains. Cela vous permet de faire une correspondance de sous-chaînes. Un peu exagéré de stocker toutes les sous-chaînes, mais si vos termes de recherche sont courts, c'est très très raisonnable.

Dan Fein
la source
2

Je viens d'avoir ce problème et j'ai trouvé une solution assez simple.

String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")

Le isGreaterThanOrEqualTo nous permet de filtrer le début de notre recherche et en ajoutant un "z" à la fin de isLessThanOrEqualTo, nous limitons notre recherche pour ne pas passer aux documents suivants.

Jacob Bonk
la source
3
J'ai essayé cette solution mais pour moi cela ne fonctionne que lorsque la chaîne complète est entrée. Par exemple, si je veux obtenir le terme «gratuit», si je commence à taper «fr», rien ne reviendra. Une fois que je tape "gratuit", le terme me donne son instantané.
Chris
Utilisez-vous le même format de code? Et le terme est-il une chaîne dans Firestore? Je sais que vous ne pouvez pas filtrer par documentId.
Jacob Bonk
0

Avec Firestore, vous pouvez implémenter une recherche en texte intégral, mais cela coûtera toujours plus de lectures qu'il n'en coûterait autrement, et vous devrez également entrer et indexer les données d'une manière particulière, donc dans cette approche, vous pouvez utiliser les fonctions cloud de firebase pour tokenise, puis hachez votre texte d'entrée tout en choisissant une fonction de hachage linéaire h(x)qui satisfait à ce qui suit - if x < y < z then h(x) < h (y) < h(z). Pour la tokenisation, vous pouvez choisir des bibliothèques PNL légères afin de maintenir le temps de démarrage à froid de votre fonction bas, ce qui peut supprimer les mots inutiles de votre phrase. Ensuite, vous pouvez exécuter une requête avec l'opérateur inférieur et supérieur à dans Firestore. Tout en stockant vos données également, vous devrez vous assurer que vous hachez le texte avant de le stocker, et stockez le texte brut également comme si vous modifiez le texte brut, la valeur hachée changera également.

Adarsh ​​Srivastava
la source
0

Cela a parfaitement fonctionné pour moi mais pourrait causer des problèmes de performances.

Faites ceci lors de l'interrogation de Firestore:

   Future<QuerySnapshot> searchResults = collectionRef
        .where('property', isGreaterThanOrEqualTo: searchQuery.toUpperCase())
        .getDocuments();

Faites ceci dans votre FutureBuilder:

    return FutureBuilder(
          future: searchResults,
          builder: (context, snapshot) {           
            List<Model> searchResults = [];
            snapshot.data.documents.forEach((doc) {
              Model model = Model.fromDocumet(doc);
              if (searchQuery.isNotEmpty &&
                  !model.property.toLowerCase().contains(searchQuery.toLowerCase())) {
                return;
              }

              searchResults.add(model);
            })
   };
Arun Yogeshwaran
la source
0

À ce jour, il existe essentiellement 3 solutions de contournement différentes, suggérées par les experts, comme réponses à la question.

Je les ai tous essayés. J'ai pensé qu'il pourrait être utile de documenter mon expérience avec chacun d'eux.

Méthode A: Utilisation de: (dbField "> =" searchString) & (dbField "<=" searchString + "\ uf8ff")

Suggéré par @Kuba & @Ankit Prajapati

.where("dbField1", ">=", searchString)
.where("dbField1", "<=", searchString + "\uf8ff");

A.1 Les requêtes Firestore peuvent uniquement effectuer des filtres de plage (>, <,> =, <=) sur un seul champ. Les requêtes avec des filtres de plage sur plusieurs champs ne sont pas prises en charge. En utilisant cette méthode, vous ne pouvez pas avoir d'opérateur de plage dans un autre champ de la base de données, par exemple un champ de date.

A.2. Cette méthode ne fonctionne PAS pour rechercher dans plusieurs champs en même temps. Par exemple, vous ne pouvez pas vérifier si une chaîne de recherche se trouve dans l'un des fichiers (nom, notes et adresse).

Méthode-B: Utilisation d'une MAP de chaînes de recherche avec "vrai" pour chaque entrée de la carte, & utilisation de l'opérateur "==" dans les requêtes

Suggéré par @Gil Gilbert

document1 = {
  'searchKeywordsMap': {
    'Jam': true,
    'Butter': true,
    'Muhamed': true,
    'Green District': true,
    'Muhamed, Green District': true,
  }
}

.where(`searchKeywordsMap.${searchString}`, "==", true);

B.1 De toute évidence, cette méthode nécessite un traitement supplémentaire à chaque fois que des données sont enregistrées dans la base de données et, plus important encore, nécessite un espace supplémentaire pour stocker la carte des chaînes de recherche.

B.2 Si une requête Firestore a une seule condition comme celle ci-dessus, aucun index ne doit être créé au préalable. Cette solution fonctionnerait très bien dans ce cas.

B.3 Cependant, si la requête a une autre condition, par exemple (status === "active",) il semble qu'un index soit requis pour chaque "chaîne de recherche" saisie par l'utilisateur. En d'autres termes, si un utilisateur recherche "Jam" et qu'un autre utilisateur recherche "Butter", un index doit être créé au préalable pour la chaîne "Jam", et un autre pour "Butter", etc. Sauf si vous pouvez prédire tout ce qui est possible les chaînes de recherche des utilisateurs, cela ne fonctionne PAS - dans le cas où la requête a d'autres conditions!

.where(searchKeywordsMap["Jam"], "==", true); // requires an index on searchKeywordsMap["Jam"]
.where("status", "==", "active");

** Méthode-C: Utilisation d'un ARRAY de chaînes de recherche et de l'opérateur "array-contains"

Suggéré par @Albert Renshaw et démontré par @Nick Carducci

document1 = {
  'searchKeywordsArray': [
    'Jam',
    'Butter',
    'Muhamed',
    'Green District',
    'Muhamed, Green District',
  ]
}

.where("searchKeywordsArray", "array-contains", searchString); 

C.1 Semblable à la méthode B, cette méthode nécessite un traitement supplémentaire à chaque fois que des données sont enregistrées dans la base de données et, plus important encore, nécessite un espace supplémentaire pour stocker le tableau de chaînes de recherche.

C.2 Les requêtes Firestore peuvent inclure au plus une clause "array-contains" ou "array-contains-any" dans une requête composée.

Limitations générales:

  1. Aucune de ces solutions ne semble prendre en charge la recherche de chaînes partielles. Par exemple, si un champ de base de données contient "1 Peter St, Green District", vous ne pouvez pas rechercher la chaîne "strict".
  2. Il est presque impossible de couvrir toutes les combinaisons possibles de chaînes de recherche attendues. Par exemple, si un champ de base de données contient "1 Mohamed St, Green District", vous ne pourrez peut-être PAS rechercher la chaîne "Green Mohamed", qui est une chaîne dont les mots sont dans un ordre différent de celui utilisé dans la base de données champ.

Il n’existe pas de solution unique. Chaque solution de contournement a ses limites. J'espère que les informations ci-dessus peuvent vous aider pendant le processus de sélection entre ces solutions de contournement.

Pour obtenir la liste des conditions de requête Firestore, veuillez consulter la documentation https://firebase.google.com/docs/firestore/query-data/queries .

Je n'ai pas essayé https://fireblog.io/blog/post/firestore-full-text-search , qui est suggéré par @Jonathan.

Bilal Abdeen
la source
-10

Nous pouvons utiliser le back-tick pour afficher la valeur d'une chaîne. Cela devrait fonctionner:

where('name', '==', `${searchTerm}`)
Zach J
la source
Merci, mais cette question concerne l'obtention de valeurs non exactes. Par exemple, l'exemple en question fonctionne pour trouver si le nom est exact. Si j'ai doc avec le nom: "Test" et ensuite je recherche "Test", cela fonctionne. Mais je m'attendrais à pouvoir rechercher "tes" ou "est" et toujours obtenir le résultat "Test". Imaginez un cas d'utilisation avec des titres de livres. Les gens recherchent souvent des titres partiels de livres plutôt que le titre précis entièrement.
tehfailsafe
13
@suulisin vous avez raison, je ne l'ai pas lu attentivement car j'avais hâte de partager ce que j'avais trouvé. Merci pour vos efforts pour le souligner, et je serai plus prudent
Zach J