Google Firestore: Kueri tentang substring dari nilai properti (penelusuran teks)

104

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.

tehfailsafe
sumber
2
Plesase sudahkah Anda menemukan solusi apapun. Saya telah mencari selama berhari-hari untuk sesuatu yang serupa tanpa kekurangan
suulisin
1
Firebase sekarang mendukung ini. Harap perbarui jawaban: stackoverflow.com/a/52715590/2057171
Albert Renshaw
1
Menurut saya cara terbaik adalah membuat skrip yang mengindeks setiap dokumen secara manual. Kemudian buat kueri indeks tersebut. lihat ini: angularfirebase.com/lessons/…
Kiblawi_Rabee

Jawaban:

35

Tidak ada operator yang seperti itu, yang diperbolehkan adalah ==, <, <=, >, >=.

Anda hanya dapat memfilter berdasarkan prefiks, misalnya untuk semua yang dimulai di antara bardan fooAnda dapat menggunakan

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

Anda dapat menggunakan layanan eksternal seperti Algolia atau ElasticSearch untuk itu.

Kuba
sumber
5
Bukan itu yang saya cari. Saya memiliki banyak daftar produk dengan judul yang panjang. "Raket Tenis Pria Rebok". Pengguna mungkin menelusuri 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 ...
tehfailsafe
4
@tehfailsafe Pertanyaan Anda adalah 'bagaimana menanyakan jika sebuah field berisi string', dan jawabannya adalah 'Anda tidak dapat melakukan itu'.
Kuba
20
@ A. Chakroun apa sebenarnya yang tidak sopan dalam jawaban saya?
Kuba
18
tbh ini adalah sesuatu yang sangat diperlukan. Saya tidak mengerti mengapa tim Firebase tidak memikirkan hal ini
Dani
2
Benar-benar mengejutkan bahwa Firebase sangat lemah dalam membuat kueri. Tidak percaya ada begitu banyak orang yang menggunakannya jika tidak dapat mendukung kueri sesederhana itu.
Bagusflyer
43

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 .

Gil Gilbert
sumber
Apakah mungkin menggunakan fungsi awan dengan cloud.google.com/appengine/docs/standard/java/search ?
Henry
1
Jika Anda menanyakan ini sebagai tindak lanjut dari jawaban ini maka: Pencarian teks lengkap AppEngine benar-benar terpisah dari Firestore dan ini tidak akan membantu Anda secara langsung. Anda dapat mereplikasi data Anda menggunakan fungsi cloud, tetapi pada dasarnya itulah saran untuk menggunakan pencarian teks lengkap tempel. Jika Anda menanyakan hal lain, silakan mulai pertanyaan baru.
Gil Gilbert
1
Di Firestore, Anda harus mengindeks semua istilah sebelum menggunakanwhere
Husam
4
Seperti yang dikatakan Husam, semua bidang ini perlu diindeks. Saya ingin mengaktifkan penelusuran istilah apa pun yang terkandung dalam nama produk saya. Jadi saya membuat properti tipe 'objek' pada dokumen saya dengan kunci menjadi bagian dari nama produk, masing-masing dengan nilai 'true' yang ditetapkan padanya, berharap pencarian di mana ('nameSegments.tennis', '==', true) akan berfungsi, tetapi firestore menyarankan untuk membuat indeks untuk nameSegments.tennis, sama untuk setiap istilah lainnya. Karena jumlah istilah yang ada tidak terbatas, jawaban ini hanya dapat digunakan untuk scennario yang sangat terbatas ketika semua istilah penelusuran ditentukan sebelumnya.
Slawoj
2
@epeleg Kueri akan berfungsi, setelah Anda membuat indeks untuk itu, namun tidak layak untuk membuat indeks untuk setiap istilah yang mungkin dikandung nama produk Anda, jadi untuk pencarian teks istilah dalam nama produk pendekatan ini tidak berfungsi untuk kasus saya.
Slawoj
43

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 \uf8ffdigunakan 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.

Ankit Prajapati
sumber
1
Jawaban bagus !, ini berfungsi dengan baik untuk pencarian teks awalan. Untuk mencari kata-kata dalam teks, dapat mencoba implementasi "array-contains" seperti yang dijelaskan dalam posting ini medium.com/@ken11zer01/…
guillefd
Hanya berpikir, tetapi secara teoritis Anda bisa mencocokkan semua nilai yang diakhiri dengan queryTest dengan membuat bidang lain dan membalikkan datanya ...
Jonathan
Ya @Jonathan, itu juga mungkin.
Ankit Prajapati
30

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-containskueri. 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.


Pemakaian:

collection("collectionPath").
    where("searchTermsArray", "array-contains", "term").get()
Albert Renshaw
sumber
12
Ini adalah solusi yang bagus. Namun, perbaiki saya jika saya salah, tetapi menurut saya itu tidak memungkinkan Anda melakukan apa yang diminta @tehfailsafe. Misalnya, jika Anda ingin mendapatkan semua nama yang berisi string "abc", Anda tidak akan berhasil dengan array-contains, karena hanya akan mengembalikan dokumen yang memiliki nama persis "abc", tetapi "abcD" atau "0abc" akan keluar.
Yulian
1
@Yulian Dalam dunia pemrograman, Search termbiasanya diartikan sebagai keseluruhan istilah yang dipisahkan oleh spasi, tanda baca, dll pada kedua sisinya. Jika Anda google abcdesekarang, 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
Albert Renshaw
1
Saya mengerti maksud Anda dan saya setuju dengan itu, tapi saya mungkin disesatkan oleh kata itu 'contains', yang artinya persis seperti yang saya maksud dalam banyak bahasa pemrograman. Hal yang sama berlaku untuk '%searchTerm%'sudut pandang SQL.
Yulian
2
@Yulian Ya saya mengerti. Firebase adalah NoSQL jadi sangat bagus dalam membuat jenis operasi ini cepat dan efisien, meskipun mungkin dibatasi untuk beberapa masalah di luar cakupan seperti pencarian string kartu liar.
Albert Renshaw
2
Nah, Anda dapat membuat bidang terpisah untuk masing-masing dengan representasi kata-kata yang dipisahkan seperti titleArray: ['this', 'is', 'a', 'title'] setiap kali Anda memperbarui dokumen. Dan kemudian pencarian akan didasarkan pada bidang itu, bukan judul. Anda dingin membuat triiger onUpdate untuk membuat bidang ini. Banyak pekerjaan untuk teks berbasis pencarian tetapi saya lebih suka memiliki peningkatan kinerja NoSQL.
sfratini
14

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.

bholben.dll
sumber
46
Saya telah membaca dokumennya, meskipun itu tidak ideal. Kekurangannya adalah Algolia dan Firestore memiliki model penetapan harga yang berbeda ... Saya dengan senang hati memiliki 600.000 dokumen di Firestore (selama saya tidak meminta terlalu banyak per hari). Ketika saya mendorong mereka ke Algolia untuk melakukan pencarian, saya sekarang harus membayar Algolia $ 310 per bulan hanya untuk dapat melakukan pencarian judul pada dokumen Firestore saya.
tehfailsafe
2
masalahnya adalah bahwa ini tidak gratis
Dani
Ini adalah jawaban yang benar untuk pertanyaan yang diajukan dan harus diterima sebagai yang terbaik.
briznad
11

Beberapa catatan di sini:

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 \uf8fftrik, 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:

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

Jonathan
sumber
Gambaran yang bagus dan sangat membantu.
RedFilter
Hebat! suara positif untuk respons yang terstruktur & informatif.
King Of The Jungle
10

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")
MoTahir
sumber
ini adalah solusi yang bagus dan mudah terima kasih. Tetapi bagaimana jika Anda ingin memeriksa lebih dari satu bidang. Suka "nama" dan "deskripsi" yang dihubungkan dengan ATAU?
Cobalah
Saya tidak berpikir Anda dapat melakukan kueri berdasarkan dua bidang, sayangnya firebase buruk dalam hal kueri, Anda dapat memeriksa harapan ini membantu stackoverflow.com/questions/26700924/…
MoTahir
1
Dikonfirmasi, @MoTahir. Tidak ada "OR" di Firestore.
Rap
solusi itu tidak cocok dengan nama pengguna yang dimulai dengan "al" ... misalnya "hello" akan dicocokkan ("hello"> "al")
antoine129
Membuat kueri dengan ATAU hanyalah masalah menggabungkan dua hasil penelusuran. Menyortir hasil tersebut adalah masalah yang berbeda ...
Jonathan
7

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
})

masukkan deskripsi gambar di sini

kueri seperti ini

firebase
.firestore()
.collection("clubs")
.where(
 "titleAsArray",
 "array-contains",
 this.state.userQuery.toLowerCase()
)
Nick Carducci
sumber
Tidak direkomendasikan sama sekali. Karena dokumen memiliki batas 20k baris, jadi Anda tidak dapat menggunakannya dengan cara ini, sampai Anda yakin bahwa dokumen Anda tidak akan pernah mencapai batas tersebut
Sandeep
Itu adalah opsi terbaik saat ini, apa lagi yang direkomendasikan?
Nick Carducci
1
@Sandeep Saya cukup yakin bahwa ukurannya dibatasi pada ukuran 1MB dan kedalaman 20 level per dokumen. Apa yang Anda maksud dengan 20k baris? Ini adalah solusi terbaik saat ini jika menggunakan Algolia atau ElasticSearch tidak tersedia
ppicom
5

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.

Rap
sumber
1
Bagaimana dengan android?
Pratik Butani
Apakah Anda mengusulkan agar orang-orang mengulang setiap rekaman dalam sebuah koleksi?
DarkNeuron
Tidak juga. Saya akan menggunakan Array.prototype. * - Like .every (), .some (), .map (), .filter (), dll. Ini dilakukan di Node di server dalam Fungsi Firebase sebelum mengembalikan nilai ke klien.
Rap
3
Anda masih harus membaca SEMUA dokumen untuk mencarinya, yang menimbulkan biaya dan mahal untuk Waktu.
Jonathan
3

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.

Dan Fein
sumber
2

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.

Jacob Bonk
sumber
3
Saya sudah mencoba solusi ini tetapi bagi saya itu hanya berfungsi ketika string lengkap dimasukkan. Misalnya, jika saya ingin mendapatkan istilah "gratis", jika saya mulai mengetik "fr" tidak akan ada yang kembali. Setelah saya mengetik "gratis" maka istilah tersebut memberi saya gambarannya.
Chris
Apakah Anda menggunakan format kode yang sama? Dan apakah istilah string di firestore? Saya tahu bahwa Anda tidak dapat memfilter berdasarkan documentId.
Jacob Bonk
2

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....
roti purebreadd
sumber
0

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.

Adarsh ​​Srivastava
sumber
0

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);
            })
   };
Arun Yogeshwaran
sumber
0

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:

  1. Tak satu pun dari solusi ini tampaknya mendukung pencarian string parsial. Misalnya, jika kolom db berisi "1 Peter St, Green District", Anda tidak dapat mencari string "strict."
  2. Hampir tidak mungkin untuk mencakup semua kemungkinan kombinasi string pencarian yang diharapkan. Misalnya, jika bidang db berisi "1 Mohamed St, Distrik Hijau", Anda mungkin TIDAK dapat mencari string "Mohamed Hijau", yang merupakan string yang memiliki kata-kata dalam urutan berbeda dari urutan yang digunakan dalam db bidang.

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.

Bilal Abdeen
sumber
-10

Kita dapat menggunakan tanda centang balik untuk mencetak nilai string. Ini harus bekerja:

where('name', '==', `${searchTerm}`)
Zach J
sumber
Terima kasih, tetapi pertanyaan ini tentang mendapatkan nilai yang tidak pasti. Misalnya, contoh yang dimaksud berhasil menemukan apakah namanya sama persis. Jika saya memiliki doc dengan nama: "Test" dan kemudian saya mencari "Test", itu berhasil. Tapi saya berharap bisa mencari "tes" atau "est" dan masih mendapatkan hasil "Test". Bayangkan kasus penggunaan dengan judul buku. Orang-orang sering kali menelusuri judul buku sebagian daripada judul persisnya seluruhnya.
tehfailsafe
13
@suulisin Anda benar, saya tidak membacanya dengan cermat karena saya ingin membagikan apa yang saya temukan. Terima kasih atas upaya Anda untuk menunjukkan hal itu, dan saya akan lebih berhati
Zach J