Saya ingin menambahkan bidang pencarian sederhana, ingin menggunakan sesuatu seperti
collectionRef.where('name', 'contains', 'searchTerm')
Saya mencoba menggunakan where('name', '==', '%searchTerm%')
, tetapi tidak mengembalikan apa pun.
Saya ingin menambahkan bidang pencarian sederhana, ingin menggunakan sesuatu seperti
collectionRef.where('name', 'contains', 'searchTerm')
Saya mencoba menggunakan where('name', '==', '%searchTerm%')
, tetapi tidak mengembalikan apa pun.
Jawaban:
Tidak ada operator yang seperti itu, yang diperbolehkan adalah ==
, <
, <=
, >
, >=
.
Anda hanya dapat memfilter berdasarkan prefiks, misalnya untuk semua yang dimulai di antara bar
dan foo
Anda dapat menggunakan
collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')
Anda dapat menggunakan layanan eksternal seperti Algolia atau ElasticSearch untuk itu.
tennis
, tetapi berdasarkan operator kueri yang tersedia, tidak ada cara untuk mendapatkan hasil tersebut. Menggabungkan >=
dan <=
tidak berfungsi. Tentu saja saya dapat menggunakan Algolia, tetapi saya juga dapat menggunakannya dengan Firebase untuk melakukan sebagian besar kueri dan tidak perlu beralih ke Firestore ...
Meskipun jawaban Kuba benar sejauh batasannya, Anda dapat meniru sebagian ini dengan struktur seperti set:
{
'terms': {
'reebok': true,
'mens': true,
'tennis': true,
'racket': true
}
}
Sekarang Anda dapat melakukan kueri dengan
collectionRef.where('terms.tennis', '==', true)
Ini berfungsi karena Firestore secara otomatis akan membuat indeks untuk setiap bidang. Sayangnya ini tidak berfungsi secara langsung untuk kueri gabungan karena Firestore tidak secara otomatis membuat indeks komposit.
Anda masih dapat menyiasatinya dengan menyimpan kombinasi kata tetapi ini menjadi sangat buruk.
Anda mungkin masih lebih baik dengan pencarian teks lengkap tempel .
where
Saya setuju dengan jawaban @ Kuba, Tapi tetap saja, perlu menambahkan sedikit perubahan agar berfungsi sempurna untuk pencarian dengan awalan. di sini apa yang berhasil untuk saya
Untuk mencari record yang dimulai dengan nama queryText
collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff')
.
Karakter yang \uf8ff
digunakan dalam kueri adalah titik kode yang sangat tinggi dalam rentang Unicode (ini adalah kode Area Penggunaan Pribadi [PUA]). Karena setelah sebagian besar karakter reguler di Unicode, kueri cocok dengan semua nilai yang dimulai dengan queryText
.
Meskipun Firebase tidak secara eksplisit mendukung pencarian istilah dalam string,
Firebase (sekarang) mendukung hal berikut yang akan menyelesaikan kasus Anda dan banyak lainnya:
Mulai Agustus 2018, mereka mendukung array-contains
kueri. Lihat: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
Anda sekarang dapat mengatur semua istilah kunci Anda ke dalam larik sebagai bidang lalu kueri untuk semua dokumen yang memiliki larik yang berisi 'X'. Anda dapat menggunakan logika AND untuk membuat perbandingan lebih lanjut untuk kueri tambahan. (Ini karena firebase saat ini secara native tidak mendukung kueri gabungan untuk beberapa kueri berisi larik sehingga kueri pengurutan 'DAN' harus dilakukan di sisi klien)
Menggunakan array dalam gaya ini akan memungkinkan mereka untuk dioptimalkan untuk penulisan bersamaan yang bagus! Belum menguji bahwa itu mendukung permintaan batch (dokumen tidak mengatakan) tetapi saya berani bertaruh itu karena ini adalah solusi resmi.
collection("collectionPath").
where("searchTermsArray", "array-contains", "term").get()
Search term
biasanya diartikan sebagai keseluruhan istilah yang dipisahkan oleh spasi, tanda baca, dll pada kedua sisinya. Jika Anda google abcde
sekarang, Anda hanya akan menemukan hasil untuk hal-hal seperti %20abcde.
atau ,abcde!
tetapi tidak abcdefghijk..
. meskipun tentunya seluruh alfabet yang diketik jauh lebih umum ditemukan di internet, penelusurannya bukan untuk abcde * melainkan untuk abcde yang terisolasi
'contains'
, yang artinya persis seperti yang saya maksud dalam banyak bahasa pemrograman. Hal yang sama berlaku untuk '%searchTerm%'
sudut pandang SQL.
Sesuai dengan dokumen Firestore , Cloud Firestore tidak mendukung pengindeksan asli atau penelusuran kolom teks dalam dokumen. Selain itu, mengunduh seluruh koleksi untuk mencari bidang di sisi klien tidaklah praktis.
Solusi pencarian pihak ketiga seperti Algolia dan Pencarian Elastis direkomendasikan.
1.) \uf8ff
bekerja dengan cara yang sama seperti~
2.) Anda dapat menggunakan klausa where atau klausa awal akhir:
ref.orderBy('title').startAt(term).endAt(term + '~');
persis sama dengan
ref.where('title', '>=', term).where('title', '<=', term + '~');
3.) Tidak, ini tidak berfungsi jika Anda membalikkan startAt()
dan endAt()
dalam setiap kombinasi, namun, Anda dapat mencapai hasil yang sama dengan membuat bidang pencarian kedua yang dibalik, dan menggabungkan hasilnya.
Contoh: Pertama, Anda harus menyimpan versi bidang yang dibalik saat bidang dibuat. Sesuatu seperti ini:
// 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);
}
Dengan ini, Anda dapat mencari huruf terakhir dari bidang string dan yang pertama , tidak hanya huruf tengah atau kelompok huruf acak. Ini lebih mendekati hasil yang diinginkan. Namun, ini tidak akan benar-benar membantu kita ketika kita menginginkan huruf atau kata tengah secara acak. Juga, ingatlah untuk menyimpan semuanya dengan huruf kecil, atau salinan huruf kecil untuk pencarian, jadi case tidak akan menjadi masalah.
4.) Jika Anda hanya memiliki beberapa kata, Metode Ken Tan akan melakukan semua yang Anda inginkan, atau setidaknya setelah Anda mengubahnya sedikit. Namun, dengan hanya satu paragraf teks, Anda akan secara eksponensial membuat lebih dari 1MB data, yang lebih besar dari batas ukuran dokumen firestore (saya tahu, saya mengujinya).
5.) Jika Anda bisa menggabungkan array-contains (atau beberapa bentuk array) dengan \uf8ff
trik, Anda mungkin bisa memiliki pencarian yang layak yang tidak mencapai batas. Saya mencoba setiap kombinasi, bahkan dengan peta, dan tidak boleh. Ada yang tahu, posting di sini.
6.) Jika Anda harus menjauh dari ALGOLIA dan ELASTIC SEARCH, dan saya tidak menyalahkan Anda sama sekali, Anda selalu dapat menggunakan mySQL, postSQL, atau neo4J di Google Cloud. Semuanya mudah diatur, dan memiliki tingkatan gratis. Anda akan memiliki satu fungsi cloud untuk menyimpan data onCreate () dan fungsi onCall () lainnya untuk mencari data. Sederhana ... ish. Mengapa tidak beralih ke mySQL saja? Data waktu nyata tentu saja! Saat seseorang menulis DGraph dengan websocks untuk data real-time, hitung saya!
Algolia dan ElasticSearch dibuat untuk menjadi dbs khusus penelusuran, jadi tidak ada yang secepat ... tetapi Anda membayarnya. Google, mengapa Anda mengarahkan kami menjauh dari Google, dan Anda tidak mengikuti MongoDB noSQL dan mengizinkan pencarian?
UPDATE - SAYA BUAT SOLUSI:
Jawaban terlambat tetapi bagi siapa saja yang masih mencari jawaban, Katakanlah kita memiliki kumpulan pengguna dan di setiap dokumen koleksi kami memiliki bidang "nama pengguna", jadi jika ingin mencari dokumen di mana nama pengguna dimulai dengan "al" kita bisa melakukan sesuatu seperti
FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")
Saya yakin Firebase akan segera keluar dengan "string-contains" untuk menangkap indeks apa pun [i] startAt dalam string ... Tapi saya telah meneliti web dan menemukan solusi ini dipikirkan oleh orang lain yang menyiapkan data Anda seperti ini
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
})
kueri seperti ini
firebase
.firestore()
.collection("clubs")
.where(
"titleAsArray",
"array-contains",
this.state.userQuery.toLowerCase()
)
Jika Anda tidak ingin menggunakan layanan pihak ketiga seperti Algolia, Firebase Cloud Functions adalah alternatif yang bagus. Anda dapat membuat fungsi yang dapat menerima parameter input, memproses melalui sisi server catatan dan kemudian mengembalikan yang sesuai dengan kriteria Anda.
Saya benar-benar berpikir solusi terbaik untuk melakukan ini dalam Firestore adalah meletakkan semua substring dalam sebuah array, dan hanya melakukan kueri array_contains. Ini memungkinkan Anda melakukan pencocokan substring. Sedikit berlebihan untuk menyimpan semua substring tetapi jika istilah pencarian Anda pendek, itu sangat masuk akal.
Saya baru saja mengalami masalah ini dan menemukan solusi yang cukup sederhana.
String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")
IsGreaterThanOrEqualTo memungkinkan kita menyaring awal pencarian kita dan dengan menambahkan "z" di akhir isLessThanOrEqualTo kita membatasi pencarian kita agar tidak bergulir ke dokumen berikutnya.
Jawaban yang dipilih hanya berfungsi untuk pencarian yang tepat dan bukan perilaku pencarian pengguna yang wajar (mencari "apel" dalam "Joe makan apel hari ini" tidak akan berfungsi).
Saya pikir jawaban Dan Fein di atas harus berperingkat lebih tinggi. Jika data String yang Anda telusuri pendek, Anda dapat menyimpan semua substring dari string dalam larik di Dokumen Anda dan kemudian menelusuri larik dengan kueri Firebase's array_contains. Dokumen Firebase dibatasi hingga 1 MiB (1.048.576 byte) ( Kuota dan Batas Firebase ), yaitu sekitar 1 juta karakter disimpan dalam sebuah dokumen (menurut saya 1 karakter ~ = 1 byte). Menyimpan substring baik-baik saja selama dokumen Anda tidak mendekati 1 juta tanda.
Contoh untuk mencari nama pengguna:
Langkah 1: Tambahkan ekstensi String berikut ke proyek Anda. Ini memungkinkan Anda dengan mudah memecah string menjadi beberapa substring. ( Saya menemukan ini di sini ).
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])
}
Langkah 2: Saat Anda menyimpan nama pengguna, simpan juga hasil dari fungsi ini sebagai larik dalam Dokumen yang sama. Ini membuat semua variasi teks asli dan menyimpannya dalam larik. Misalnya, input teks "Apple" akan membuat array berikut: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le "," app "," ppl "," ple "," appl "," pple "," apple "], yang harus mencakup semua kriteria penelusuran yang mungkin dimasukkan pengguna. Anda dapat meninggalkan maximumStringSize sebagai nihil jika Anda menginginkan semua hasil, namun, jika ada teks yang panjang, saya akan merekomendasikan untuk membatasi sebelum ukuran dokumen menjadi terlalu besar - sekitar 15 bekerja dengan baik untuk saya (kebanyakan orang tidak mencari frase yang panjang pula ).
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
}
Langkah 3: Anda dapat menggunakan fungsi array_contains Firebase!
[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....
Dengan Firestore Anda dapat mengimplementasikan pencarian teks lengkap tetapi biayanya masih lebih mahal daripada yang seharusnya, dan Anda juga harus memasukkan dan mengindeks data dengan cara tertentu, Jadi dalam pendekatan ini Anda dapat menggunakan fungsi cloud firebase untuk tokenise dan kemudian hash teks masukan Anda saat memilih fungsi hash linier h(x)
yang memenuhi berikut ini - jika x < y < z then h(x) < h (y) < h(z)
. Untuk tokenisasi, Anda dapat memilih beberapa Pustaka NLP ringan untuk menjaga waktu mulai dingin fungsi Anda tetap rendah yang dapat menghapus kata-kata yang tidak perlu dari kalimat Anda. Kemudian Anda dapat menjalankan kueri dengan operator kurang dari dan lebih besar dari di Firestore. Saat menyimpan data Anda juga, Anda harus memastikan bahwa Anda mencirikan teks sebelum menyimpannya, dan menyimpan teks biasa juga seolah-olah Anda mengubah teks biasa, nilai hash juga akan berubah.
Ini bekerja untuk saya dengan sempurna tetapi mungkin menyebabkan masalah kinerja.
Lakukan ini saat menanyakan firestore:
Future<QuerySnapshot> searchResults = collectionRef
.where('property', isGreaterThanOrEqualTo: searchQuery.toUpperCase())
.getDocuments();
Lakukan ini di FutureBuilder Anda:
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);
})
};
Saat ini, pada dasarnya ada 3 solusi berbeda, yang disarankan oleh para ahli, sebagai jawaban atas pertanyaan tersebut.
Saya sudah mencoba semuanya. Saya pikir mungkin berguna untuk mendokumentasikan pengalaman saya dengan masing-masing dari mereka.
Metode-A: Menggunakan: (dbField "> =" searchString) & (dbField "<=" searchString + "\ uf8ff")
Direkomendasikan oleh @Kuba & @kit Prajapati
.where("dbField1", ">=", searchString)
.where("dbField1", "<=", searchString + "\uf8ff");
A.1 Kueri Firestore hanya dapat melakukan filter rentang (>, <,> =, <=) pada satu bidang. Kueri dengan filter rentang di beberapa bidang tidak didukung. Dengan menggunakan metode ini, Anda tidak bisa memiliki operator jangkauan di bidang lain di db, misalnya bidang tanggal.
A.2. Metode ini TIDAK berfungsi untuk mencari di beberapa bidang secara bersamaan. Misalnya, Anda tidak dapat memeriksa apakah string pencarian ada di salah satu file (nama, catatan & alamat).
Metode-B: Menggunakan MAP string penelusuran dengan "true" untuk setiap entri di peta, & menggunakan operator "==" di kueri
Direkomendasikan oleh @Gil Gilbert
document1 = {
'searchKeywordsMap': {
'Jam': true,
'Butter': true,
'Muhamed': true,
'Green District': true,
'Muhamed, Green District': true,
}
}
.where(`searchKeywordsMap.${searchString}`, "==", true);
B.1 Tentunya, metode ini membutuhkan pemrosesan ekstra setiap kali data disimpan ke db, dan yang lebih penting, membutuhkan ruang ekstra untuk menyimpan peta string pencarian.
B.2 Jika kueri Firestore memiliki satu kondisi seperti di atas, tidak ada indeks yang perlu dibuat sebelumnya. Solusi ini akan berfungsi dengan baik dalam kasus ini.
B.3 Namun, jika kueri memiliki kondisi lain, misalnya (status === "aktif",) tampaknya indeks diperlukan untuk setiap "string pencarian" yang dimasukkan pengguna. Dengan kata lain, jika pengguna menelusuri "Selai" dan pengguna lain menelusuri "Mentega", indeks harus dibuat sebelumnya untuk string "Selai", dan satu lagi untuk "Mentega", dll. Kecuali Anda dapat memprediksi semua kemungkinan string pencarian pengguna, ini TIDAK berfungsi - jika kueri memiliki kondisi lain!
.where(searchKeywordsMap["Jam"], "==", true); // requires an index on searchKeywordsMap["Jam"]
.where("status", "==", "active");
** Metode-C: Menggunakan ARRAY string pencarian, & operator "array-contains"
Direkomendasikan oleh @Albert Renshaw & didemonstrasikan oleh @Nick Carducci
document1 = {
'searchKeywordsArray': [
'Jam',
'Butter',
'Muhamed',
'Green District',
'Muhamed, Green District',
]
}
.where("searchKeywordsArray", "array-contains", searchString);
C.1 Mirip dengan Metode-B, metode ini memerlukan pemrosesan ekstra setiap kali data disimpan ke db, dan yang lebih penting, membutuhkan ruang ekstra untuk menyimpan larik string pencarian.
C.2 Kueri Firestore dapat menyertakan paling banyak satu klausa "array-contains" atau "array-contains-any" dalam kueri gabungan.
Batasan Umum:
Tidak ada satu solusi yang cocok untuk semua. Setiap solusi memiliki batasannya. Saya harap informasi di atas dapat membantu Anda selama proses pemilihan di antara solusi ini.
Untuk daftar ketentuan kueri Firestore, lihat dokumentasi https://firebase.google.com/docs/firestore/query-data/queries .
Saya belum mencoba https://fireblog.io/blog/post/firestore-full-text-search , yang disarankan oleh @Jonathan.
Kita dapat menggunakan tanda centang balik untuk mencetak nilai string. Ini harus bekerja:
where('name', '==', `${searchTerm}`)