Ich möchte ein einfaches Suchfeld hinzufügen, möchte so etwas wie verwenden
collectionRef.where('name', 'contains', 'searchTerm')
Ich habe es versucht where('name', '==', '%searchTerm%')
, aber es hat nichts zurückgegeben.
Ich möchte ein einfaches Suchfeld hinzufügen, möchte so etwas wie verwenden
collectionRef.where('name', 'contains', 'searchTerm')
Ich habe es versucht where('name', '==', '%searchTerm%')
, aber es hat nichts zurückgegeben.
Antworten:
Es gibt keine solche Betreiber, erlaubt diejenigen sind ==
, <
, <=
, >
, >=
.
Sie können nur nach Präfixen filtern, z. B. nach allem, was dazwischen beginnt bar
und was foo
Sie verwenden können
collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')
Sie können dafür externe Dienste wie Algolia oder ElasticSearch verwenden.
tennis
, aber basierend auf den verfügbaren Abfrageoperatoren gibt es keine Möglichkeit, diese Ergebnisse zu erhalten. Kombinieren >=
und <=
funktioniert nicht. Natürlich kann ich Algolia verwenden, aber ich kann es auch einfach mit Firebase verwenden, um die meisten Abfragen zu erledigen, und muss nicht zum Firestore wechseln ...
Während Kubas Antwort in Bezug auf Einschränkungen wahr ist, können Sie dies teilweise mit einer satzartigen Struktur emulieren:
{
'terms': {
'reebok': true,
'mens': true,
'tennis': true,
'racket': true
}
}
Jetzt können Sie mit abfragen
collectionRef.where('terms.tennis', '==', true)
Dies funktioniert, da Firestore automatisch einen Index für jedes Feld erstellt. Leider funktioniert dies nicht direkt für zusammengesetzte Abfragen, da Firestore nicht automatisch zusammengesetzte Indizes erstellt.
Sie können dies immer noch umgehen, indem Sie Wortkombinationen speichern, aber dies wird schnell hässlich.
Mit einem Außenborder sind Sie wahrscheinlich immer noch besser dran Volltextsuche sind .
where
Ich stimme der Antwort von @ Kuba zu. Dennoch muss eine kleine Änderung hinzugefügt werden, damit die Suche nach Präfix perfekt funktioniert. hier was für mich funktioniert hat
Zum Suchen von Datensätzen, die mit dem Namen beginnen queryText
collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff')
.
Das \uf8ff
in der Abfrage verwendete Zeichen ist ein sehr hoher Codepunkt im Unicode-Bereich (es handelt sich um einen PUA-Code (Private Usage Area)). Da die meisten regulären Zeichen in Unicode verwendet werden, stimmt die Abfrage mit allen Werten überein, die mit beginnen queryText
.
Während Firebase die Suche nach einem Begriff innerhalb einer Zeichenfolge nicht explizit unterstützt,
Firebase unterstützt (jetzt) Folgendes, das für Ihren Fall und viele andere gelöst werden kann:
Ab August 2018 unterstützen sie die array-contains
Abfrage. Siehe: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
Sie können jetzt alle Schlüsselbegriffe in einem Array als Feld festlegen und dann alle Dokumente abfragen, deren Array 'X' enthält. Sie können das logische UND verwenden, um weitere Vergleiche für zusätzliche Abfragen durchzuführen. (Dies liegt daran, dass Firebase derzeit keine nativen zusammengesetzten Abfragen für mehrere Array-haltige Abfragen unterstützt, sodass die Sortierabfragen 'UND' auf Client-Seite durchgeführt werden müssen.)
Durch die Verwendung von Arrays in diesem Stil können sie für gleichzeitige Schreibvorgänge optimiert werden, was sehr hilfreich ist! Ich habe nicht getestet, ob es Batch-Anfragen unterstützt (Dokumente sagen es nicht), aber ich würde wetten, dass dies der Fall ist, da es eine offizielle Lösung ist.
collection("collectionPath").
where("searchTermsArray", "array-contains", "term").get()
Search term
wird normalerweise ein ganzer Begriff verstanden, der auf beiden Seiten durch Leerzeichen, Interpunktion usw. getrennt ist. Wenn Sie abcde
jetzt googeln, finden Sie nur Ergebnisse für Dinge wie %20abcde.
oder ,abcde!
aber nicht abcdefghijk..
. Auch wenn sicherlich das gesamte eingegebene Alphabet viel häufiger im Internet zu finden ist, wird nicht nach abcde * gesucht, sondern nach einem isolierten abcde
'contains'
, was genau bedeutet, worauf ich mich in vielen Programmiersprachen beziehe. Gleiches gilt '%searchTerm%'
aus SQL-Sicht.
Gemäß den Firestore-Dokumenten unterstützt Cloud Firestore keine native Indizierung oder Suche nach Textfeldern in Dokumenten. Darüber hinaus ist das Herunterladen einer gesamten Sammlung zur clientseitigen Suche nach Feldern nicht praktikabel.
Suchlösungen von Drittanbietern wie Algolia und Elastic Search werden empfohlen.
1.) \uf8ff
funktioniert genauso wie~
2.) Sie können eine where-Klausel oder start end-Klauseln verwenden:
ref.orderBy('title').startAt(term).endAt(term + '~');
ist genau das gleiche wie
ref.where('title', '>=', term).where('title', '<=', term + '~');
3.) Nein, es funktioniert nicht, wenn Sie umkehren startAt()
und endAt()
in jeder Kombination können Sie jedoch das gleiche Ergebnis erzielen, indem Sie ein zweites Suchfeld erstellen, das umgekehrt ist, und die Ergebnisse kombinieren.
Beispiel: Zuerst müssen Sie eine umgekehrte Version des Feldes speichern, wenn das Feld erstellt wird. Etwas wie das:
// 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);
}
Mit dieser Option können Sie die letzten Buchstaben eines Zeichenfolgenfelds und die ersten , nur nicht zufälligen Mittelbuchstaben oder Buchstabengruppen durchsuchen . Dies ist näher am gewünschten Ergebnis. Dies hilft uns jedoch nicht wirklich, wenn wir zufällige mittlere Buchstaben oder Wörter wollen. Denken Sie auch daran, alles in Kleinbuchstaben oder eine Kopie in Kleinbuchstaben für die Suche zu speichern, damit Groß- und Kleinschreibung kein Problem darstellt.
4.) Wenn Sie nur wenige Wörter haben, wird Ken Tans Methode alles tun, was Sie wollen, oder zumindest nachdem Sie sie leicht geändert haben. Mit nur einem Textabschnitt erstellen Sie jedoch exponentiell mehr als 1 MB Daten, was größer ist als die maximale Dokumentgröße des Firestores (ich weiß, ich habe es getestet).
5.) Wenn Sie Array- Enthaltene (oder irgendeine Form von Arrays) mit dem \uf8ff
Trick kombinieren könnten, könnten Sie eine brauchbare Suche haben, die die Grenzen nicht erreicht. Ich habe jede Kombination ausprobiert, auch mit Karten, und ein No Go. Jeder, der das herausfindet, poste es hier.
6.) Wenn Sie sich von ALGOLIA und ELASTIC SEARCH entfernen müssen und ich Ihnen überhaupt keine Vorwürfe mache, können Sie jederzeit mySQL, postSQL oder neo4Js in Google Cloud verwenden. Sie sind alle 3 einfach einzurichten und haben freie Ebenen. Sie hätten eine Cloud-Funktion zum Speichern der Daten onCreate () und eine andere onCall () -Funktion zum Durchsuchen der Daten. Einfach ... ish. Warum nicht einfach zu mySQL wechseln? Die Echtzeitdaten natürlich! Wenn jemand DGraph mit Websocken für Echtzeitdaten schreibt , zählen Sie mich mit!
Algolia und ElasticSearch wurden als reine Suchdatenbanken entwickelt, daher gibt es nichts so schnelles ... aber Sie zahlen dafür. Google, warum führen Sie uns von Google weg und folgen Sie MongoDB noSQL nicht und erlauben Sie Suchvorgänge?
UPDATE - ICH HABE EINE LÖSUNG ERSTELLT:
Späte Antwort, aber für alle, die noch nach einer Antwort suchen: Nehmen wir an, wir haben eine Sammlung von Benutzern und in jedem Dokument der Sammlung ein Feld "Benutzername". Wenn Sie also ein Dokument suchen möchten, in dem der Benutzername mit "al" beginnt, wir können so etwas tun
FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")
Ich bin mir sicher, dass Firebase bald "string-enthält" herausbringen wird, um jeden Index [i] startAt in der Zeichenfolge zu erfassen ... Aber ich habe die Websites recherchiert und festgestellt, dass diese Lösung von jemand anderem gedacht wurde, der Ihre Daten wie eingerichtet hat Dies
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
})
Abfrage wie folgt
firebase
.firestore()
.collection("clubs")
.where(
"titleAsArray",
"array-contains",
this.state.userQuery.toLowerCase()
)
Wenn Sie keinen Drittanbieter-Service wie Algolia verwenden möchten, sind Firebase Cloud-Funktionen eine großartige Alternative. Sie können eine Funktion erstellen, die einen Eingabeparameter empfangen, serverseitig durch die Datensätze verarbeiten und dann diejenigen zurückgeben kann, die Ihren Kriterien entsprechen.
Ich denke tatsächlich, dass die beste Lösung, um dies in Firestore zu tun, darin besteht, alle Teilzeichenfolgen in ein Array einzufügen und einfach eine Abfrage array_contains durchzuführen. Auf diese Weise können Sie Teilzeichenfolgenabgleiche durchführen. Ein bisschen übertrieben, um alle Teilzeichenfolgen zu speichern, aber wenn Ihre Suchbegriffe kurz sind, ist es sehr, sehr vernünftig.
Ich hatte gerade dieses Problem und fand eine ziemlich einfache Lösung.
String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")
Mit isGreaterThanOrEqualTo können wir den Beginn unserer Suche herausfiltern. Durch Hinzufügen eines "z" am Ende von isLessThanOrEqualTo können wir unsere Suche so begrenzen, dass nicht mit den nächsten Dokumenten fortgefahren wird.
Die ausgewählte Antwort funktioniert nur für exakte Suchvorgänge und ist kein natürliches Suchverhalten der Benutzer (die Suche nach "Apfel" in "Joe hat heute einen Apfel gegessen" würde nicht funktionieren).
Ich denke, Dan Feins Antwort oben sollte höher eingestuft werden. Wenn die von Ihnen durchsuchten Zeichenfolgendaten kurz sind, können Sie alle Teilzeichenfolgen der Zeichenfolge in einem Array in Ihrem Dokument speichern und dann mit der Abfrage array_contains von Firebase das Array durchsuchen. Firebase-Dokumente sind auf 1 MiB (1.048.576 Byte) ( Firebase-Kontingente und -Limits ) begrenzt, was ungefähr 1 Million Zeichen entspricht, die in einem Dokument gespeichert sind (ich denke 1 Zeichen ~ = 1 Byte). Das Speichern der Teilzeichenfolgen ist in Ordnung, solange Ihr Dokument nicht nahe an der 1-Millionen-Marke liegt.
Beispiel für die Suche nach Benutzernamen:
Schritt 1: Fügen Sie Ihrem Projekt die folgende String-Erweiterung hinzu. Auf diese Weise können Sie einen String einfach in Teilzeichenfolgen aufteilen. ( Ich habe das hier gefunden ).
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])
}
Schritt 2: Wenn Sie den Namen eines Benutzers speichern, speichern Sie auch das Ergebnis dieser Funktion als Array im selben Dokument. Dadurch werden alle Variationen des Originaltextes erstellt und in einem Array gespeichert. Beispielsweise würde die Texteingabe "Apple" das folgende Array erstellen: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le" "," app "," ppl "," ple "," appl "," pple "," apple "], die alle Suchkriterien umfassen sollten, die ein Benutzer eingeben könnte. Sie können MaximumStringSize als Null belassen, wenn Sie alle Ergebnisse erzielen möchten. Wenn jedoch langer Text vorhanden ist, würde ich empfehlen, ihn zu begrenzen, bevor die Dokumentgröße zu groß wird - ungefähr 15 funktionieren für mich gut (die meisten Leute suchen sowieso nicht nach langen Phrasen ).
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
}
Schritt 3: Sie können die Funktion array_contains von Firebase verwenden!
[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....
Mit Firestore können Sie eine Volltextsuche implementieren, die jedoch immer noch mehr Lesevorgänge kostet als sonst. Außerdem müssen Sie die Daten auf eine bestimmte Weise eingeben und indizieren. Bei diesem Ansatz können Sie also Firebase-Cloud-Funktionen verwenden Tokenisieren und hashen Sie dann Ihren Eingabetext, während Sie eine lineare Hash-Funktion auswählen h(x)
, die die folgenden Anforderungen erfüllt - if x < y < z then h(x) < h (y) < h(z)
. Für die Tokenisierung können Sie einige leichtgewichtige NLP-Bibliotheken auswählen, um die Kaltstartzeit Ihrer Funktion niedrig zu halten und unnötige Wörter aus Ihrem Satz zu entfernen. Anschließend können Sie im Firestore eine Abfrage mit einem Operator kleiner und größer als ausführen. Während Sie Ihre Daten auch speichern, müssen Sie sicherstellen, dass Sie den Text hashen, bevor Sie ihn speichern, und den Nur-Text so speichern, als ob Sie den Nur-Text ändern, ändert sich auch der Hash-Wert.
Dies hat bei mir perfekt funktioniert, kann aber zu Leistungsproblemen führen.
Tun Sie dies, wenn Sie den Firestore abfragen:
Future<QuerySnapshot> searchResults = collectionRef
.where('property', isGreaterThanOrEqualTo: searchQuery.toUpperCase())
.getDocuments();
Tun Sie dies in Ihrem 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);
})
};
Mit dem Back-Tick können wir den Wert einer Zeichenfolge ausdrucken. Das sollte funktionieren:
where('name', '==', `${searchTerm}`)