Basit bir arama alanı eklemek istiyorum, gibi bir şey kullanmak istiyorum
collectionRef.where('name', 'contains', 'searchTerm')
Kullanmayı denedim where('name', '==', '%searchTerm%')
, ama hiçbir şey döndürmedi.
Basit bir arama alanı eklemek istiyorum, gibi bir şey kullanmak istiyorum
collectionRef.where('name', 'contains', 'searchTerm')
Kullanmayı denedim where('name', '==', '%searchTerm%')
, ama hiçbir şey döndürmedi.
Yanıtlar:
Böyle operatör yok, evcil olanlar ==
, <
, <=
, >
, >=
.
Yalnızca öneklere göre filtreleyebilirsiniz, örneğin, aralarında başlayan bar
ve foo
kullanabileceğiniz her şey için
collectionRef.where('name', '>=', 'bar').where('name', '<=', 'foo')
Bunun için Algolia veya ElasticSearch gibi harici hizmetleri kullanabilirsiniz .
tennis
, ancak mevcut sorgu operatörlerine göre bu sonuçları almanın bir yolu yoktur. Birleştiriyor >=
ve <=
çalışmıyor. Elbette Algolia'yı kullanabilirim, ancak çoğu soruyu yapmak için Firebase ile de kullanabilirim ve Firestore'a
@ Kuba'nın cevabına katılıyorum, Ancak yine de, öneke göre arama için mükemmel bir şekilde çalışması için küçük bir değişiklik eklemesi gerekiyor. burada benim için işe yarayan
İsim ile başlayan kayıtları aramak için queryText
collectionRef.where('name', '>=', queryText).where('name', '<=', queryText+ '\uf8ff')
.
\uf8ff
Sorguda kullanılan karakter , Unicode aralığında çok yüksek bir kod noktasıdır (Özel Kullanım Alanı [PUA] kodudur). Unicode'daki çoğu normal karakterden sonra olduğu için, sorgu ile başlayan tüm değerlerle eşleşir queryText
.
Kuba'nın cevabı, kısıtlamalar söz konusu olduğunda doğru olsa da, bunu set benzeri bir yapıyla kısmen taklit edebilirsiniz:
{
'terms': {
'reebok': true,
'mens': true,
'tennis': true,
'racket': true
}
}
Şimdi ile sorgulayabilirsiniz
collectionRef.where('terms.tennis', '==', true)
Bu işe yarar çünkü Firestore her alan için otomatik olarak bir dizin oluşturacaktır. Maalesef bu, doğrudan bileşik sorgular için çalışmaz çünkü Firestore otomatik olarak bileşik dizinler oluşturmaz.
Yine de kelime kombinasyonlarını saklayarak bu sorunu çözebilirsiniz, ancak bu çirkinleşir.
Dıştan takmalı bir tam metin aramasıyla muhtemelen daha iyi durumdasınız .
where
Firebase, bir dize içinde bir terimin aranmasını açıkça desteklemese de,
Firebase, (şimdi) sizin durumunuz ve diğerleri için çözecek olan aşağıdakileri desteklemektedir:
Ağustos 2018 itibariyle array-contains
sorguyu destekliyorlar . Bakınız: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
Artık tüm anahtar terimlerinizi alan olarak bir diziye ayarlayabilir ve ardından 'X' içeren bir diziye sahip tüm belgeleri sorgulayabilirsiniz. Ek sorgular için daha fazla karşılaştırma yapmak için mantıksal VE'yi kullanabilirsiniz . (Bunun nedeni, firebase'in şu anda birden fazla dizi içeren sorgu için bileşik sorguları yerel olarak desteklememesidir, bu nedenle 'VE' sıralama sorgularının istemci tarafında yapılması gerekir)
Dizileri bu tarzda kullanmak, eşzamanlı yazmalar için optimize edilmelerine izin verir ki bu da güzeldir! Toplu istekleri destekleyip desteklemediğini test etmedim (dokümanlar söylemez) ancak resmi bir çözüm olduğu için yaptığına bahse girerim.
collection("collectionPath").
where("searchTermsArray", "array-contains", "term").get()
Search term
tipik olarak her iki tarafta boşluk, noktalama vb. İle ayrılmış tam bir terim olarak anlaşılır. Eğer google Eğer abcde
şu anda yalnızca gibi şeyler için sonuçlar bulacaksınız %20abcde.
ya ,abcde!
ama abcdefghijk..
. Her ne kadar yazılan alfabenin tamamı internette çok daha yaygın olsa da, arama abcde için değil * izole bir abcde için
'contains'
demek istediğini anlıyorum ve buna katılıyorum, ama muhtemelen kelimeyle yanıldım , bu da birçok programlama dilinde tam olarak bahsettiğim anlamına geliyor. Aynı şey '%searchTerm%'
SQL açısından da geçerli.
Başına Firestore docs , Bulut Firestore yerli indeksleme desteklemiyor veya belgelerde metin alanları arayın. Ek olarak, istemci tarafında alanları aramak için bir koleksiyonun tamamını indirmek pratik değildir.
Algolia ve Elastic Search gibi üçüncü taraf arama çözümleri önerilir.
1.) \uf8ff
aynı şekilde çalışır~
2.) Where cümlesini veya start end cümlelerini kullanabilirsiniz:
ref.orderBy('title').startAt(term).endAt(term + '~');
tamamen aynı
ref.where('title', '>=', term).where('title', '<=', term + '~');
3.) Hayır, tersine çevirirseniz startAt()
ve endAt()
her kombinasyonda çalışmaz , ancak tersine ikinci bir arama alanı oluşturarak ve sonuçları birleştirerek aynı sonucu elde edebilirsiniz.
Örnek: Öncelikle, alan oluşturulduğunda alanın tersine çevrilmiş bir versiyonunu kaydetmelisiniz. Bunun gibi bir şey:
// 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);
}
Bununla, rastgele orta harfleri veya harf gruplarını değil, bir dize alanının son harflerini ve ilkini arayabilirsiniz . Bu istenen sonuca daha yakın. Ancak, rastgele orta harfler veya kelimeler istediğimizde bu bize gerçekten yardımcı olmayacaktır. Ayrıca, arama için her şeyi küçük harfli veya küçük harfli bir kopyayı kaydetmeyi unutmayın, böylece büyük / küçük harf sorunu bir sorun olmaz.
4.) Yalnızca birkaç kelimeniz varsa, Ken Tan'ın Yöntemi istediğiniz her şeyi yapacaktır veya en azından biraz değiştirdikten sonra. Ancak, yalnızca bir paragraf metniyle katlanarak 1MB'den fazla veri oluşturacaksınız, bu da firestore'un belge boyutu sınırından daha büyüktür (biliyorum, test ettim).
5.) Dizi içeren (veya bir dizi diziyi) \uf8ff
hile ile birleştirebilseydiniz , sınırlara ulaşmayan uygun bir arama yapabilirsiniz. Haritalarla bile her kombinasyonu denedim ve hayır. Bunu anlarsanız, buraya gönderin.
6.) ALGOLIA ve ELASTIC SEARCH'ten uzaklaşmanız gerekiyorsa ve sizi hiç suçlamıyorum, Google Cloud'da her zaman mySQL, postSQL veya neo4J'leri kullanabilirsiniz. Bunların 3'ü de kurulumu kolaydır ve ücretsiz katmanları vardır. Verileri onCreate () kaydetmek için bir bulut işlevine ve verileri aramak için başka bir onCall () işlevine sahip olursunuz. Basit ... ish. O halde neden mySQL'e geçmiyorsunuz? Tabii ki gerçek zamanlı veriler! Birisi gerçek zamanlı veriler için web çoraplarıyla DGraph yazdığında beni de hesaba katın!
Algolia ve ElasticSearch yalnızca arama özellikli dbs olarak tasarlandı, bu yüzden bu kadar hızlı bir şey yok ... ama parasını ödüyorsunuz. Google, neden bizi Google'dan uzaklaştırıyorsun ve MongoDB noSQL'i takip edip aramalara izin vermiyor musun?
GÜNCELLEME - BİR ÇÖZÜM YARATTIM:
Geç cevap ama hala bir cevap arayanlar için, Diyelim ki bir kullanıcı koleksiyonumuz var ve koleksiyonun her bir belgesinde bir "kullanıcı adı" alanımız var, bu nedenle kullanıcı adının "al" ile başladığı bir belge bulmak istiyorsanız gibi bir şey yapabiliriz
FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")
Eminim Firebase, dizedeki herhangi bir dizini [i] startAt yakalamak için yakında "string-contains" ile çıkacaktır ... Ancak web'leri araştırdım ve başka biri tarafından verilerinizi ayarlayan bu çözümü buldum bu
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
})
bunun gibi sorgu
firebase
.firestore()
.collection("clubs")
.where(
"titleAsArray",
"array-contains",
this.state.userQuery.toLowerCase()
)
Algolia gibi üçüncü taraf bir hizmeti kullanmak istemiyorsanız, Firebase Cloud Functions harika bir alternatiftir. Bir girdi parametresi alabilen, sunucu tarafındaki kayıtlar üzerinden işleyebilen ve ardından kriterlerinize uyanları döndürebilen bir işlev oluşturabilirsiniz.
Seçilen yanıt yalnızca tam aramalar için işe yarar ve doğal kullanıcı arama davranışı değildir ("Joe bugün bir elma yedi" içinde "elma" araması işe yaramaz).
Dan Fein'in yukarıdaki cevabının daha üst sıralarda yer alması gerektiğini düşünüyorum. Aradığınız Dize verileri kısaysa dizenin tüm alt dizelerini Belgenizdeki bir diziye kaydedebilir ve ardından Firebase'in array_contains sorgusuyla dizi içinde arama yapabilirsiniz. Firebase Belgeleri, bir belgede kaydedilen yaklaşık 1 milyon karakter olan 1 MiB (1.048.576 bayt) ( Firebase Kotaları ve Sınırları ) ile sınırlıdır (sanırım 1 karakter ~ = 1 bayt). Belgeniz 1 milyon işarete yakın olmadığı sürece alt dizeleri saklamak sorun değildir.
Kullanıcı adlarını aramak için örnek:
Adım 1: Aşağıdaki String uzantısını projenize ekleyin. Bu, bir dizeyi kolayca alt dizelere ayırmanıza olanak tanır. ( Bunu burada buldum ).
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])
}
Adım 2: Bir kullanıcının adını kaydettiğinizde, bu işlevin sonucunu da aynı Belgede bir dizi olarak saklayın. Bu, orijinal metnin tüm varyasyonlarını oluşturur ve bunları bir dizide saklar. Örneğin, "Apple" metin girişi şu diziyi oluşturur: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le "," uygulama "," ppl "," ple "," appl "," pple "," apple "], bir kullanıcının girebileceği tüm arama kriterlerini kapsamalıdır. Tüm sonuçları istiyorsanız maximumStringSize'ı nil olarak bırakabilirsiniz, ancak uzun bir metin varsa, belge boyutu çok büyük olmadan önce onu kapatmanızı tavsiye ederim - 15 civarı benim için iyi çalışıyor (çoğu insan zaten uzun cümleleri aramıyor ).
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
}
3. Adım: Firebase'in array_contains işlevini kullanabilirsiniz!
[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....
Aslında bunu Firestore'da yapmanın en iyi çözümünün tüm alt dizeleri bir diziye koymak ve sadece bir array_contains sorgusu yapmak olduğunu düşünüyorum. Bu, alt dize eşlemesi yapmanızı sağlar. Tüm alt dizeleri depolamak biraz fazla olabilir, ancak arama terimleriniz kısaysa çok makul.
Bu sorunu yeni yaşadım ve oldukça basit bir çözüm buldum.
String search = "ca";
Firestore.instance.collection("categories").orderBy("name").where("name",isGreaterThanOrEqualTo: search).where("name",isLessThanOrEqualTo: search+"z")
İsGreaterThanOrEqualTo, aramamızın başlangıcını filtrelememize ve isLessThanOrEqualTo'nun sonuna bir "z" ekleyerek aramamızı sonraki belgelere geçmeyecek şekilde sınırlandırmamıza izin verir.
Firestore ile tam metin araması uygulayabilirsiniz, ancak yine de normalde olduğundan daha fazla okumaya mal olacak ve ayrıca verileri belirli bir şekilde girmeniz ve dizine eklemeniz gerekecek, bu nedenle bu yaklaşımda, firebase bulut işlevlerini kullanarak belirteçleştirin ve ardından h(x)
aşağıdakileri karşılayan doğrusal bir hash işlevi seçerken giriş metninizi hashleyin - if x < y < z then h(x) < h (y) < h(z)
. Simgeleştirme için, işlevinizin soğuk başlama zamanını düşük tutmak için bazı hafif NLP Kitaplıkları seçebilirsiniz; bu, gereksiz sözcükleri cümlenizden çıkarabilir. Daha sonra Firestore'da küçüktür ve büyüktür operatörüyle bir sorgu çalıştırabilirsiniz. Verilerinizi de depolarken, saklamadan önce metne hash uyguladığınızdan emin olmalısınız ve düz metni değiştirirseniz hashing değeri de değişecek gibi düz metni depolamalısınız.
Bu benim için mükemmel çalıştı ancak performans sorunlarına neden olabilir.
Firestore'u sorgularken bunu yapın:
Future<QuerySnapshot> searchResults = collectionRef
.where('property', isGreaterThanOrEqualTo: searchQuery.toUpperCase())
.getDocuments();
Bunu FutureBuilder'ınızda yapın:
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);
})
};
Bugün itibariyle, sorunun cevabı olarak uzmanlar tarafından önerilen temelde 3 farklı geçici çözüm bulunmaktadır.
Hepsini denedim. Her biriyle olan deneyimlerimi belgelemenin yararlı olabileceğini düşündüm.
Yöntem-A: (dbField "> =" searchString) & (dbField "<=" searchString + "\ uf8ff") kullanarak
@Kuba & @Ankit Prajapati tarafından önerildi
.where("dbField1", ">=", searchString)
.where("dbField1", "<=", searchString + "\uf8ff");
A.1 Firestore sorguları, tek bir alanda yalnızca aralık filtreleri (>, <,> =, <=) gerçekleştirebilir. Birden çok alanda aralık filtrelerine sahip sorgular desteklenmez. Bu yöntemi kullanarak, db üzerindeki başka herhangi bir alanda, örneğin bir tarih alanında bir aralık operatörüne sahip olamazsınız.
A.2. Bu yöntem aynı anda birden çok alanda arama yapmak için ÇALIŞMAZ. Örneğin, dosyalardan herhangi birinde (ad, notlar ve adres) bir arama dizesi olup olmadığını kontrol edemezsiniz.
Yöntem-B: Haritadaki her giriş için "true" ile arama dizelerinin MAP'ini kullanma ve sorgularda "==" operatörünü kullanma
@Gil Gilbert tarafından önerildi
document1 = {
'searchKeywordsMap': {
'Jam': true,
'Butter': true,
'Muhamed': true,
'Green District': true,
'Muhamed, Green District': true,
}
}
.where(`searchKeywordsMap.${searchString}`, "==", true);
B.1 Açıktır ki, bu yöntem veri veritabanına her kaydedildiğinde ekstra işlem gerektirir ve daha da önemlisi, arama dizelerinin haritasını depolamak için fazladan alan gerektirir.
B.2 Bir Firestore sorgusu yukarıdaki gibi tek bir koşula sahipse, önceden indeks oluşturulmasına gerek yoktur. Bu çözüm, bu durumda gayet iyi çalışır.
B.3 Ancak, sorgunun başka bir koşulu varsa, örneğin (durum === "aktif"), kullanıcının girdiği her "arama dizisi" için bir indeks gerekli gibi görünür. Başka bir deyişle, bir kullanıcı "Reçel" için arama yaparsa ve başka bir kullanıcı "Tereyağı" için arama yaparsa, önceden "Reçel" dizesi için ve "Tereyağı" için başka bir dizin oluşturulmalıdır. Tüm olasılıkları tahmin edemediğiniz sürece kullanıcıların arama dizeleri, bu ÇALIŞMAZ - sorgunun başka koşulları olması durumunda!
.where(searchKeywordsMap["Jam"], "==", true); // requires an index on searchKeywordsMap["Jam"]
.where("status", "==", "active");
** Yöntem-C: ARRAY arama dizelerini ve "dizi içerir" operatörünü kullanma
@Albert Renshaw tarafından önerildi ve @Nick Carducci tarafından gösterildi
document1 = {
'searchKeywordsArray': [
'Jam',
'Butter',
'Muhamed',
'Green District',
'Muhamed, Green District',
]
}
.where("searchKeywordsArray", "array-contains", searchString);
C.1 Yöntem-B'ye benzer şekilde, bu yöntem veri veritabanına her kaydedildiğinde fazladan işlem gerektirir ve daha da önemlisi, arama dizgilerinin dizisini depolamak için fazladan alan gerektirir.
C.2 Firestore sorguları, bir bileşik sorguda en fazla bir "dizi içerir" veya "dizi içerir-herhangi bir" cümlesi içerebilir.
Genel Sınırlamalar:
Her şeye uyan tek bir çözüm yok. Her geçici çözümün kendi sınırlamaları vardır. Umarım yukarıdaki bilgiler, bu geçici çözümler arasındaki seçim sürecinde size yardımcı olabilir.
Firestore sorgu koşullarının listesi için lütfen https://firebase.google.com/docs/firestore/query-data/queries belgelerine bakın. .
@Jonathan tarafından önerilen https://fireblog.io/blog/post/firestore-full-text-search'ü denemedim .
Bir dizenin değerini yazdırmak için geri tik işaretini kullanabiliriz. Bu çalışmalı:
where('name', '==', `${searchTerm}`)