Google Firestore: संपत्ति मान के प्रतिस्थापन पर क्वेरी (पाठ खोज)


105

मैं एक साधारण खोज फ़ील्ड जोड़ना चाह रहा हूं, जैसे कुछ का उपयोग करना चाहते हैं

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

मैंने प्रयोग करने की कोशिश की where('name', '==', '%searchTerm%'), लेकिन यह कुछ भी वापस नहीं आया।


2
plesase u ने कोई समाधान पाया है। मैं कुछ कमी के साथ इसी तरह के दिनों के लिए खोज किया गया है
suulisin

1
फायरबेस अब इसका समर्थन करता है। कृपया अपडेट का जवाब दें: stackoverflow.com/a/52715590/2057171
अल्बर्ट रेनशॉ

1
मुझे लगता है कि स्क्रिप्ट बनाने के लिए सबसे अच्छा तरीका है कि प्रत्येक दस्तावेज़ को मैन्युअल रूप से अनुक्रमित किया जाए। फिर उन इंडेक्स को क्वेरी करें। इसे देखें: angularfirebase.com/lessons/…
Kiblawi_Rabee

जवाबों:


37

ऐसा कोई ऑपरेटर है, की अनुमति दी हैं ==, <, <=, >, >=

आप केवल उपसर्गों द्वारा फ़िल्टर कर सकते हैं, उदाहरण के लिए उन सभी चीज़ों के लिए जो बीच में शुरू होती हैं barऔर fooआप उपयोग कर सकते हैं

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

आप इसके लिए Algolia या ElasticSearch जैसी बाहरी सेवा का उपयोग कर सकते हैं ।


5
यह काफी नहीं है कि मैं क्या देख रहा हूँ। मेरे पास लंबे शीर्षक वाले उत्पादों की एक बड़ी सूची है। "रेबोक मेंस टेनिस रैकेट"। कोई उपयोगकर्ता खोज सकता है tennis, लेकिन उपलब्ध क्वेरी ऑपरेटरों के आधार पर उन परिणामों को प्राप्त करने का कोई तरीका नहीं है। संयोजन >=और <=काम नहीं करता है। बेशक मैं अल्गोलिया का उपयोग कर सकता हूं, लेकिन मैं सिर्फ फायरबेस के साथ इसका उपयोग कर सकता हूं ताकि अधिकांश प्रश्न कर
सकें और फायरस्टार

4
@tehfailsafe वैसे आपका सवाल है कि 'यदि फ़ील्ड में स्ट्रिंग है तो कैसे क्वेरी करें', और प्रतिक्रिया है 'आप ऐसा नहीं कर सकते'।
कुबा

20
@ A.Crroun क्या वास्तव में मेरे जवाब में असभ्य है?
कुबा

19
tbh यह वास्तव में आवश्यक कुछ है। मुझे समझ नहीं आ रहा है कि फायरबेस की टीम ने इस पर क्यों नहीं सोचा
दानी

2
वास्तव में आश्चर्य है कि फायरबैस क्वेरी करने में इतना कमजोर है। विश्वास नहीं हो सकता कि बहुत सारे लोग इसका उपयोग कर रहे हैं यदि यह इतनी सरल क्वेरी का समर्थन नहीं कर सकता है।
Bagusflyer

45

मैं @ कुबा के उत्तर से सहमत हूं, लेकिन फिर भी, इसे उपसर्ग द्वारा खोज के लिए पूरी तरह से काम करने के लिए एक छोटे से बदलाव को जोड़ने की आवश्यकता है। यहाँ मेरे लिए क्या काम किया

नाम से शुरू होने वाले रिकॉर्ड की खोज के लिए queryText

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

\uf8ffक्वेरी में उपयोग किया जाने वाला वर्ण यूनिकोड श्रेणी में एक बहुत उच्च कोड बिंदु है (यह एक निजी उपयोग क्षेत्र [PUA] कोड है)। क्योंकि यह यूनिकोड के अधिकांश नियमित पात्रों के बाद है, क्वेरी उन सभी मूल्यों से मेल खाती है जो इसके साथ शुरू होते हैं queryText


1
अच्छा जवाब!, यह उपसर्ग पाठ की खोज के लिए बहुत अच्छा काम करता है। पाठ में शब्द खोजने के लिए, कोशिश कर सकते हैं इस पोस्ट में बताया गया है "सरणी-इसमें शामिल है" कार्यान्वयन medium.com/@ken11zer01/...
guillefd

बस सोच रहा था, लेकिन सैद्धांतिक रूप से आप उन सभी मूल्यों से मेल खा सकते हैं जो क्वेरी के साथ समाप्त होते हैं एक और फ़ील्ड बनाकर और डेटा को सम्मिलित करते हुए ...
जोनाथन

हां @ जोनाथन, यह भी संभव है।
अंकित प्रजापति

43

जबकि कुबा का जवाब सही है, जहां तक ​​प्रतिबंध है, आप आंशिक रूप से सेट-जैसी संरचना के साथ इसका अनुकरण कर सकते हैं:

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

अब आप क्वेरी कर सकते हैं

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

यह काम करता है क्योंकि फायरस्टार स्वचालित रूप से हर क्षेत्र के लिए एक सूचकांक बनाएगा। दुर्भाग्य से यह सीधे यौगिक प्रश्नों के लिए काम नहीं करता है क्योंकि फायरस्टार स्वचालित रूप से समग्र अनुक्रम नहीं बनाता है।

आप अभी भी इसके चारों ओर शब्दों के संयोजन का काम कर सकते हैं लेकिन यह बदसूरत तेज़ हो जाता है।

आप अभी भी शायद आउटबोर्ड पूर्ण पाठ खोज के साथ बेहतर हैं


क्या क्लाउड फ़ंक्शन का उपयोग क्लाउड के साथ करना संभव है। Googleappengine/docs/standard/java/search ?
हेनरी

1
यदि आप इसे इस उत्तर के लिए अनुवर्ती के रूप में पूछ रहे हैं: AppEngine की पूर्ण पाठ खोज पूरी तरह से Firestore से अलग है और इसलिए यह सीधे आपकी मदद नहीं करेगा। आप क्लाउड फ़ंक्शन का उपयोग करके अपने डेटा को दोहरा सकते हैं, लेकिन यह अनिवार्य रूप से पूर्ण पाठ खोज के आउटबोर्ड का उपयोग करने का सुझाव क्या है। यदि आप कुछ और पूछ रहे हैं, तो कृपया एक नया प्रश्न शुरू करें।
गिल गिलबर्ट

1
फायरस्टार में, आपको उपयोग करने से पहले सभी शर्तों को अनुक्रमित करने की आवश्यकता हैwhere
हसाम

4
जैसा कि हुसम ने बताया, इन सभी क्षेत्रों को अनुक्रमित करने की आवश्यकता होगी। मैं अपने उत्पाद नाम में किसी भी शब्द की खोज सक्षम करना चाहता था। इसलिए मैंने अपने दस्तावेज़ पर 'ऑब्जेक्ट' टाइप प्रॉपर्टी बनाई, जिसमें उत्पाद के नाम के कुछ हिस्से हैं, जिनमें से प्रत्येक 'सही' मान के साथ दिया गया है, यह उम्मीद करते हुए कि खोज कहाँ ('nameSegments.tennis', '==', true) काम करते हैं, लेकिन फायरस्टार सुझाव देता है कि nameSegments.tennis के लिए एक इंडेक्स बनाया जाए, जो हर दूसरे टर्म के लिए समान हो। चूंकि अनंत संख्या में शब्द हो सकते हैं, इसलिए यह उत्तर केवल एक बहुत ही सीमित स्कैनेरियो के लिए प्रयोग करने योग्य होता है, जब सभी खोज शब्द सामने परिभाषित किए जाते हैं।
स्लावोज

2
@epeleg क्वेरी आपके लिए एक इंडेक्स बनाने के बाद काम करेगी, फिर भी यह संभव नहीं है कि आपके उत्पाद नाम में प्रत्येक संभावित शब्द के लिए इंडेक्स बनाया जाए, इसलिए उत्पाद नामों में टेक्स्ट खोज के लिए यह दृष्टिकोण मेरे मामले के लिए काम नहीं आया।
स्लाव

30

जबकि फायरबेस स्पष्ट रूप से एक स्ट्रिंग के भीतर शब्द की खोज का समर्थन नहीं करता है,

फायरबेस (अब) निम्नलिखित का समर्थन करता है जो आपके मामले और कई अन्य लोगों के लिए हल करेगा:

अगस्त 2018 तक वे array-containsक्वेरी का समर्थन करते हैं । देखें: https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html

अब आप अपनी सभी महत्वपूर्ण शर्तों को एक फ़ील्ड के रूप में एक सरणी में सेट कर सकते हैं, फिर उन सभी दस्तावेज़ों के लिए क्वेरी कर सकते हैं जिनमें एक ऐसा सरणी होता है जिसमें 'X' होता है। आप अतिरिक्त क्वेरी के लिए तार्किक और आगे की तुलना करने के लिए उपयोग कर सकते हैं (ऐसा इसलिए है क्योंकि फायरबेस वर्तमान में कई सरणी वाले प्रश्नों के लिए मूल रूप से यौगिक प्रश्नों का समर्थन नहीं करता है, इसलिए 'एंड' सॉर्टिंग प्रश्नों को क्लाइंट के अंत में करना होगा)

इस शैली में सरणियों का उपयोग करना उन्हें समवर्ती लेखन के लिए अनुकूलित करने की अनुमति देगा जो अच्छा है! यह परीक्षण नहीं किया गया है कि यह बैच अनुरोधों का समर्थन करता है (डॉक्स नहीं कहते हैं), लेकिन मैं इसे आधिकारिक समाधान के बाद से दांव लगाऊंगा।


उपयोग:

collection("collectionPath").
    where("searchTermsArray", "array-contains", "term").get()

12
यह एक अच्छा उपाय है। हालाँकि, मुझे गलत होने पर सही करें, लेकिन मुझे लगता है कि यह आपको वह करने की अनुमति नहीं देता है जो @tehfailsafe ने माँगा है। उदाहरण के लिए, यदि आप स्ट्रिंग "एबीसी" वाले सभी नाम प्राप्त करना चाहते हैं, तो आपको सरणी-सम्‍मिलित कोई भी सफलता नहीं मिलेगी, क्योंकि यह केवल उन दस्तावेजों को लौटाएगा, जिनमें "एबीसी" का सटीक नाम है, लेकिन "एबीसीडी" या "इबाक" बाहर होगा।
यूलियन

1
@ यूलियन प्रोग्रामिंग दुनिया में, Search termआम तौर पर दोनों तरफ से अंतरिक्ष, विराम चिह्न आदि द्वारा अलग किए गए एक पूरे शब्द का अर्थ समझा जाता है। यदि आप abcdeअभी Google करते हैं, तो आपको केवल %20abcde.या ,abcde!नहीं जैसी चीज़ों के लिए परिणाम मिलेंगे abcdefghijk..। भले ही निश्चित रूप से टाइप की गई पूरी वर्णमाला इंटरनेट पर पाए जाने के लिए बहुत अधिक सामान्य है, खोज * एबीसीड के लिए नहीं है * यह एक पृथक एबेक के लिए है
अल्बर्ट रेनशॉ

1
मैं आपकी बात देख रहा हूं और मैं इससे सहमत हूं, लेकिन मुझे शायद शब्द से गुमराह किया गया था 'contains', जिसका अर्थ है कि मैं वास्तव में कई प्रोग्रामिंग भाषाओं का उल्लेख कर रहा हूं। '%searchTerm%'एक SQL दृष्टिकोण से एक ही जाता है ।
यूलियन

2
@ यूलियन हाँ मुझे लगता है कि मिलता है। Firebase NoSQL है, हालांकि यह वास्तव में इन प्रकार के कार्यों को तेज और कुशल बनाने में अच्छा है, भले ही वे वाइल्ड-कार्ड स्ट्रिंग खोज जैसी कुछ आउट-ऑफ-स्कोप समस्याओं के लिए सीमित हो सकते हैं।
अल्बर्ट रेनशॉ

2
खैर, आप हर बार दस्तावेज़ को अद्यतन करने वाले शीर्षक के रूप में छपे हुए शब्दों के प्रतिनिधित्व के साथ एक अलग क्षेत्र बना सकते हैं: ['यह', 'है', 'एक', 'शीर्षक']। और फिर खोज शीर्षक के बजाय उस फ़ील्ड पर आधारित होगी। आप इस फ़ील्ड को बनाने के लिए एक त्रिकोणीय onUpdate को ठंडा करें। खोज आधारित पाठ के लिए बहुत सारे काम हैं, लेकिन मेरे पास NoSQL के प्रदर्शन में सुधार है।
sfratini

14

प्रति Firestore डॉक्स , बादल Firestore दस्तावेज़ों में पाठ क्षेत्रों के लिए खोज देशी अनुक्रमण का समर्थन नहीं करता या। इसके अतिरिक्त, क्लाइंट-साइड के लिए खोज करने के लिए संपूर्ण संग्रह डाउनलोड करना व्यावहारिक नहीं है।

तीसरे पक्ष के खोज समाधान जैसे अल्गोलिया और इलास्टिक खोज की सिफारिश की जाती है।


46
मैंने डॉक्स पढ़ा है, हालांकि यह आदर्श नहीं है। दोष यह है कि अल्गोलिया और फायरस्टार के अलग-अलग मूल्य-निर्धारण मॉडल हैं ... मैं खुशी से फायरस्टार में 600,000 दस्तावेज रख सकता हूं (जब तक कि मैं प्रति दिन बहुत अधिक क्वेरी नहीं करता)। जब मैं उन्हें खोज करने के लिए अल्गोलिया की ओर धकेलता हूं तो मुझे अब अपने फर्स्टस्टोर के दस्तावेजों पर एक शीर्षक खोज करने में सक्षम होने के लिए हर महीने 310 डॉलर का भुगतान करना होगा।
तहफेलसेफ

2
समस्या यह है कि यह मुफ्त नहीं है
दानी

यह प्रश्न का सही उत्तर है और इसे सर्वश्रेष्ठ के रूप में स्वीकार किया जाना चाहिए।
1

12

यहाँ कुछ नोट:

1.) \uf8ff उसी तरह काम करता है जैसे~

2.) आप एक क्लॉज का उपयोग कर सकते हैं या एंड क्लॉस शुरू कर सकते हैं:

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

बिल्कुल वैसा ही है

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

3.) नहीं, यह काम नहीं करता है यदि आप रिवर्स startAt()और endAt()हर संयोजन में, हालांकि, आप एक दूसरे खोज फ़ील्ड बनाकर उसी परिणाम को प्राप्त कर सकते हैं जो उलटा है, और परिणामों का संयोजन।

उदाहरण: फ़ील्ड बनाते समय सबसे पहले आपको फ़ील्ड का उलटा संस्करण सहेजना होगा। कुछ इस तरह:

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

इसके साथ, आप एक स्ट्रिंग फ़ील्ड के अंतिम अक्षर और पहले को खोज सकते हैं , बस यादृच्छिक मध्य अक्षर या अक्षरों के समूह नहीं। यह वांछित परिणाम के करीब है। हालाँकि, यह वास्तव में हमारी मदद नहीं करेगा जब हम यादृच्छिक मध्य अक्षर या शब्द चाहते हैं। इसके अलावा, सब कुछ लोअरकेस या खोज के लिए लोअरकेस कॉपी को सहेजना याद रखें, इसलिए मामला कोई समस्या नहीं होगी।

4.) यदि आपके पास केवल कुछ शब्द हैं, तो केन टैन की विधि वह सब कुछ करेगी जो आप चाहते हैं, या कम से कम आपके द्वारा इसे थोड़ा संशोधित करने के बाद। हालांकि, केवल पाठ के एक पैराग्राफ के साथ, आप तेजी से 1MB से अधिक डेटा बनाएंगे, जो फायरस्टार के दस्तावेज़ आकार की सीमा (मुझे पता है, मैंने इसका परीक्षण किया है) से बड़ा है।

5.) यदि आप ट्रिक के साथ सरणी-\uf8ff सम्‍मिलित (या कुछ प्रकार के सरणियों) को जोड़ सकते हैं , तो आप एक व्यवहार्य खोज कर सकते हैं जो सीमाओं तक नहीं पहुँचती है। मैंने हर संयोजन की कोशिश की, यहां तक ​​कि नक्शों के साथ भी, और कोई भी नहीं। कोई भी यह पता लगाता है, इसे यहां पोस्ट करें।

6.) अगर आपको ALGOLIA और ELASTIC SEARCH से दूर होना चाहिए, और मैं आपको बिल्कुल दोषी नहीं ठहराता, तो आप हमेशा Google क्लाउड पर mySQL, postSQL, या neo4Js का उपयोग कर सकते थे। वे सभी 3 स्थापित करने में आसान हैं, और उनके पास मुफ्त टियर हैं। आपके पास डेटा को बचाने के लिए एक क्लाउड फ़ंक्शन होगा () और दूसरा onCall () फ़ंक्शन डेटा को खोजने के लिए। सरल ... ish। क्यों नहीं बस mySQL के लिए स्विच? पाठ्यक्रम का वास्तविक समय डेटा! जब कोई व्यक्ति वास्तविक समय के डेटा के लिए वेबसर्क के साथ डीजीग्राफ लिखता है , तो मुझे गिनें!

अल्गोलिया और इलास्टिकसर्च का निर्माण केवल खोज के लिए किया गया था, इसलिए इसमें कुछ भी त्वरित नहीं है ... लेकिन आप इसके लिए भुगतान करते हैं। Google, आप हमें Google से दूर क्यों करते हैं, और क्या आप MongoDB noSQL का अनुसरण नहीं करते हैं और खोजों की अनुमति देते हैं?

अद्यतन - मैंने एक समाधान बनाया:

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


एक महान अवलोकन और बहुत उपयोगी।
रेडफिल्टर

बहुत बढ़िया! एक अच्छी तरह से संरचित और सूचनात्मक प्रतिक्रिया के लिए upvote।
जंगल का राजा

10

देर से जवाब लेकिन किसी के लिए जो अभी भी एक उत्तर की तलाश में है, मान लें कि हमारे पास उपयोगकर्ताओं का एक संग्रह है और संग्रह के प्रत्येक दस्तावेज़ में हमारे पास "उपयोगकर्ता नाम" फ़ील्ड है, इसलिए यदि कोई दस्तावेज़ ढूंढना चाहते हैं, जहां उपयोगकर्ता नाम "अल" से शुरू होता है हम ऐसा कुछ कर सकते हैं

 FirebaseFirestore.getInstance().collection("users").whereGreaterThanOrEqualTo("username", "al")

यह एक महान एक आसान समाधान धन्यवाद है। लेकिन क्या हो अगर आप एक से ज्यादा फील्ड चेक करना चाहते हैं। OR द्वारा "नाम" और "विवरण" जुड़ा हुआ है?
इसे

मुझे नहीं लगता कि आप दो क्षेत्रों के आधार पर क्वेरी कर सकते हैं, दुख की बात है जब यह
क्वैबिंग की

1
पुष्टि की, @MoTahir। फायरस्टार में कोई "या" नहीं है।
रैप

वह समाधान "अल" से शुरू होने वाले उपयोगकर्ता नाम से मेल नहीं खाता ... उदाहरण के लिए "हैलो" का मिलान होगा ("हैलो"> "अल")
antoine129

OR द्वारा क्वेरी करना केवल दो खोज परिणामों के संयोजन की बात है। उन परिणामों को क्रमबद्ध करना एक अलग समस्या है ...
जोनाथन

7

मुझे यकीन है कि Firebase किसी भी इंडेक्स को कैप्चर करने के लिए जल्द ही "string- होते हैं" के साथ आएगा [i] स्ट्रिंग में शुरू करें ... लेकिन मैंने जाले पर शोध किया है और पाया है कि इस उपाय को किसी और ने सोचा था कि अपना डेटा सेट करें यह

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

यहां छवि विवरण दर्ज करें

इस तरह क्वेरी

firebase
.firestore()
.collection("clubs")
.where(
 "titleAsArray",
 "array-contains",
 this.state.userQuery.toLowerCase()
)

अनुशंसित नहीं है। चूंकि दस्तावेज़ों में 20k लाइन्स की सीमा होती है, इसलिए आप इसे इस तरह से उपयोग नहीं कर सकते, जब तक उर यकीन न हो कि उर दस्तावेज़ ऐसी सीमा तक कभी नहीं पहुंचेंगे
संदीप

यह इस समय सबसे अच्छा विकल्प है, और क्या अनुशंसित है?
निक कार्डुची

1
@ संदीप मुझे पूरा यकीन है कि आकार 1 एमबी आकार और प्रति दस्तावेज़ गहराई के 20 स्तरों तक सीमित है। 20k लाइन्स से आपका क्या मतलब है? यह वर्तमान में सबसे अच्छा समाधान है अगर अल्गोलिया या इलास्टिक खोज का उपयोग करना टेबल से बाहर है
ppicom

6

अगर आप अल्गोलिया जैसी थर्ड पार्टी सर्विस का इस्तेमाल नहीं करना चाहते हैं, तो फायरबेस क्लाउड फंक्शन्स एक बेहतरीन विकल्प हैं। आप एक फ़ंक्शन बना सकते हैं जो एक इनपुट पैरामीटर प्राप्त कर सकता है, रिकॉर्ड सर्वर-साइड के माध्यम से प्रक्रिया कर सकता है और फिर उन लोगों को वापस कर सकता है जो आपके मानदंडों से मेल खाते हैं।


1
Android के बारे में क्या?
प्रतीक बुटानी

क्या आप प्रस्ताव कर रहे हैं कि लोग एक संग्रह में हर एक रिकॉर्ड पर पुनरावृति करें
डार्कनॉरॉन

ज़रुरी नहीं। I ग्राहक।
रैप

3
आपको अभी भी उन्हें खोजने के लिए सभी दस्तावेजों को पढ़ना होगा, जो खर्चों को बढ़ाता है और समय के लिए महंगा है।
जोनाथन

3

मैं वास्तव में लगता है कि फायरस्टार के भीतर ऐसा करने का सबसे अच्छा समाधान सभी सब्सट्रिंग को एक सरणी में रखना है, और बस array_container क्वेरी करना है। यह आपको सबस्ट्रिंग मिलान करने की अनुमति देता है। सभी सबस्ट्रिंग्स को स्टोर करने के लिए थोड़ा ओवरकिल लेकिन अगर आपके खोज शब्द कम हैं तो यह बहुत ही उचित है।


3

चयनित उत्तर केवल सटीक खोजों के लिए काम करता है और यह प्राकृतिक उपयोगकर्ता खोज व्यवहार नहीं है ("जो एक सेब आज खाया" में "सेब" की खोज करना) काम नहीं करेगा।

मुझे लगता है कि ऊपर दिए गए डैन फ़िन के उत्तर को उच्च स्थान दिया जाना चाहिए। यदि आपके द्वारा खोजा जा रहा स्ट्रिंग डेटा कम है, तो आप अपने दस्तावेज़ में एक सरणी में स्ट्रिंग के सभी सबस्ट्रिंग को बचा सकते हैं और फिर फायरबेस के array_contains क्वेरी के साथ सरणी के माध्यम से खोज सकते हैं। फायरबेस डॉक्यूमेंट्स 1 MiB (1,048,576 बाइट्स) ( Firebase Quotas and Limits ) तक सीमित हैं , जो एक डॉक्यूमेंट में सेव किए गए लगभग 1 मिलियन कैरेक्टर हैं (मुझे लगता है कि 1 कैरेक्टर ~ = 1 बाइट)। जब तक आपका दस्तावेज़ 1 मिलियन अंक के करीब नहीं होता, तब तक सबस्ट्रिंग को स्टोर करना ठीक है।

उपयोगकर्ता नाम खोजने के लिए उदाहरण:

चरण 1: अपनी परियोजना के लिए निम्नलिखित स्ट्रिंग एक्सटेंशन जोड़ें। यह आपको आसानी से सबस्ट्रिंग में एक स्ट्रिंग को तोड़ने देता है। ( मैंने यहां पाया )।

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

चरण 2: जब आप किसी उपयोगकर्ता का नाम संग्रहीत करते हैं, तो इस फ़ंक्शन के परिणाम को उसी दस्तावेज़ में एक सरणी के रूप में संग्रहीत करते हैं। यह मूल पाठ के सभी रूपों को बनाता है और उन्हें एक सरणी में संग्रहीत करता है। उदाहरण के लिए, पाठ इनपुट "Apple" निम्न सरणी बनाएगा: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le "," ऐप "," ppl "," ple "," appl "," pple "," apple "], जो एक उपयोगकर्ता द्वारा दर्ज किए जाने वाले सभी खोज मानदंडों को शामिल करना चाहिए। आप अधिकतम परिणाम छोड़ सकते हैं यदि आप सभी परिणाम चाहते हैं, तो निल के रूप में, हालांकि, अगर कोई लंबा पाठ है, तो मैं इसे दस्तावेज़ आकार से पहले कैपिंग करने की सिफारिश करूंगा। )।

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: आप फायरबेस के array_contains फ़ंक्शन का उपयोग कर सकते हैं!

[yourDatabasePath].whereField([savedSubstringArray], arrayContains: searchText).getDocuments....

2

मुझे बस यह समस्या थी और एक बहुत ही सरल समाधान के साथ आया।

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

IsGraitThanOrEqualTo हमें हमारी खोज की शुरुआत को फ़िल्टर करने देता है और isLessThanOrEqualTo के अंत में एक "z" जोड़कर हम अपनी खोज को अगले दस्तावेज़ों में रोल न करने के लिए कैप करते हैं।


3
Ive ने इस समाधान की कोशिश की लेकिन मेरे लिए यह केवल तभी काम करता है जब पूर्ण स्ट्रिंग दर्ज किया जाता है। उदाहरण के लिए, यदि मैं "फ्री" शब्द प्राप्त करना चाहता हूं, अगर मैं "fr" टाइप करना शुरू करता हूं तो कुछ भी वापस नहीं आएगा। एक बार जब मैं "नि: शुल्क" टाइप करता हूं तो शब्द मुझे इसका स्नैपशॉट देता है।
क्रिस

क्या आप समान कोड प्रारूप का उपयोग कर रहे हैं? और क्या शब्द फायरस्टार में एक स्ट्रिंग है? मुझे पता है कि आप documentId द्वारा फ़िल्टर नहीं कर सकते।
जैकब बोंक

0

फायरस्टार के साथ आप एक पूर्ण पाठ खोज को लागू कर सकते हैं, लेकिन इसमें अभी भी अधिक से अधिक रीड की लागत होगी अन्यथा, और आपको एक विशेष तरीके से डेटा दर्ज करने और अनुक्रमित करने की आवश्यकता होगी, इसलिए इस दृष्टिकोण में आप फायरबेस क्लाउड फ़ंक्शन का उपयोग कर सकते हैं एक रेखीय हैश फ़ंक्शन का चयन करते समय टोकन और फिर अपने इनपुट पाठ को हैश करें h(x)जो निम्न को संतुष्ट करता है - यदि x < y < z then h(x) < h (y) < h(z)। टोकेनाइजेशन के लिए आप कुछ हल्के एनएलपी लाइब्रेरी चुन सकते हैं ताकि आपके फंक्शन का कोल्ड स्टार्ट टाइम कम रहे जो आपके वाक्य से अनावश्यक शब्दों को हटा सके। फिर आप फायरस्टार में ऑपरेटर से कम और अधिक के साथ एक क्वेरी चला सकते हैं। अपने डेटा को संग्रहीत करते समय, आपको यह सुनिश्चित करना होगा कि आपने पाठ को संग्रहीत करने से पहले हैश किया है, और सादे पाठ को भी संग्रहीत किया है जैसे कि आप सादे पाठ को बदलते हैं हैश मान भी बदल जाएगा।


0

यह मेरे लिए पूरी तरह से काम करता है लेकिन प्रदर्शन के मुद्दों का कारण हो सकता है।

फायरस्टार की क्वेरी करते समय ऐसा करें:

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

इसे अपने 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);
            })
   };

0

आज तक, मूल रूप से 3 अलग-अलग वर्कअराउंड हैं, जो विशेषज्ञों द्वारा प्रश्न के उत्तर के रूप में सुझाए गए थे।

मैंने उन सभी को आजमाया है। मैंने सोचा कि उनमें से प्रत्येक के साथ अपने अनुभव का दस्तावेजीकरण करना उपयोगी हो सकता है।

विधि-ए: का उपयोग: (dbField "> =" searchString) और (dbField "<=" searchString + "\ uf8ff")

सुझाव @Kuba & @Ankit प्रजापति द्वारा

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

A.1 फायरस्टार क्वेरी केवल एक क्षेत्र पर रेंज फ़िल्टर (>, <,> =, <=) कर सकती हैं। कई क्षेत्रों पर रेंज फ़िल्टर वाली क्वेरीज़ समर्थित नहीं हैं। इस विधि का उपयोग करके, आप db पर किसी अन्य फ़ील्ड में रेंज ऑपरेटर नहीं रख सकते, उदाहरण के लिए दिनांक फ़ील्ड।

.2। यह विधि एक साथ कई क्षेत्रों में खोज करने के लिए काम नहीं करती है। उदाहरण के लिए, यदि कोई खोज स्ट्रिंग दायर किए गए (नाम, नोट्स और पते) में से किसी में है, तो आप जांच नहीं कर सकते।

विधि-बी: मानचित्र में प्रत्येक प्रविष्टि के लिए "सच" के साथ खोज स्ट्रिंग्स के एक एमएपी का उपयोग करना, और प्रश्नों में "==" ऑपरेटर का उपयोग करना

@ गिल गिलबर्ट द्वारा सुझाया गया

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

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

B.1 जाहिर है, इस विधि को हर बार डेटा को db में सहेजने के लिए अतिरिक्त प्रसंस्करण की आवश्यकता होती है, और इससे भी महत्वपूर्ण बात यह है कि खोज स्ट्रिंग्स के मानचित्र को संग्रहीत करने के लिए अतिरिक्त स्थान की आवश्यकता होती है।

B.2 यदि फायरस्टार क्वेरी में ऊपर की तरह एक ही स्थिति है, तो पहले से कोई सूचकांक बनाने की आवश्यकता नहीं है। यह समाधान इस मामले में ठीक काम करेगा।

B.3 हालाँकि, यदि क्वेरी में एक और शर्त है, उदाहरण के लिए (स्थिति === "सक्रिय"), तो ऐसा लगता है कि उपयोगकर्ता "एंट्री स्ट्रिंग" के लिए एक सूचकांक आवश्यक है। दूसरे शब्दों में, यदि कोई उपयोगकर्ता "Jam" की खोज करता है और दूसरा उपयोगकर्ता "Butter" की खोज करता है, तो स्ट्रिंग "Jam" के लिए पहले से ही एक इंडेक्स बनाया जाना चाहिए, और "Butter" के लिए एक अन्य, आदि जब तक आप सभी संभव भविष्यवाणी नहीं कर सकते। उपयोगकर्ताओं के खोज के तार, यह काम नहीं करता है - क्वेरी के मामले में अन्य शर्तें हैं!

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

** विधि-सी: खोज स्ट्रिंग के एआरएवाई का उपयोग करते हुए, और "सरणी-समाहित" ऑपरेटर

@ अल्बर्ट रेनशॉ द्वारा सुझाए गए और @ निक कार्डुडी द्वारा प्रदर्शित

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

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

C.1 मेथड-बी के समान, इस विधि के लिए हर बार डेटा को अतिरिक्त प्रोसेसिंग की आवश्यकता होती है, db पर सहेजा जाता है, और इससे भी महत्वपूर्ण बात यह है कि सर्च स्ट्रिंग्स के एरे को स्टोर करने के लिए अतिरिक्त स्थान की आवश्यकता होती है।

C.2 फायरस्टार क्वेरी में यौगिक क्वेरी में अधिकांश एक "सरणी-सम्‍मिलित" या "एरेस-सम्‍मिलित" कोई भी क्‍लॉज शामिल हो सकता है।

सामान्य सीमाएं:

  1. इन समाधानों में से कोई भी आंशिक तारों की खोज का समर्थन नहीं करता है। उदाहरण के लिए, यदि एक db फ़ील्ड में "1 पीटर सेंट, ग्रीन डिस्ट्रिक्ट" शामिल है, तो आप स्ट्रिंग को "सख्त" नहीं खोज सकते।
  2. अपेक्षित खोज स्ट्रिंग्स के सभी संभावित संयोजनों को कवर करना लगभग असंभव है। उदाहरण के लिए, यदि एक db फ़ील्ड में "1 मोहम्मद सेंट, ग्रीन डिस्ट्रिक्ट" शामिल है, तो आप स्ट्रिंग "ग्रीन मोहम्मद" की खोज करने में सक्षम नहीं हो सकते हैं, जो एक स्ट्रिंग है जो db में उपयोग किए गए ऑर्डर की तुलना में एक अलग क्रम में शब्द है मैदान।

कोई एक समाधान नहीं है जो सभी को फिट बैठता है। प्रत्येक वर्कअराउंड की अपनी सीमाएँ होती हैं। मुझे उम्मीद है कि उपरोक्त जानकारी आपको इन वर्कअराउंड के बीच चयन प्रक्रिया के दौरान मदद कर सकती है।

फायरस्टार क्वेरी शर्तों की एक सूची के लिए, कृपया दस्तावेज़ देखें https://firebase.google.com/docs/firestore/query-data/queries

मैंने https://fireblog.io/blog/post/firestore-full-text-search की कोशिश नहीं की है , जो @Jonathan द्वारा सुझाया गया है।


-10

हम स्ट्रिंग के मूल्य को प्रिंट करने के लिए बैक-टिक का उपयोग कर सकते हैं। यह काम करना चाहिए:

where('name', '==', `${searchTerm}`)

धन्यवाद, लेकिन यह प्रश्न गैर-सटीक मान प्राप्त करने के बारे में है। उदाहरण के लिए, प्रश्न में उदाहरण काम करता है यदि नाम सटीक है। अगर मैंने नाम के साथ डॉक्टर किया है: "टेस्ट" और फिर मैं "टेस्ट" की खोज करता हूं, तो यह काम करता है। लेकिन मैं "tes" या "est" की खोज करने में सक्षम होने की उम्मीद करूंगा और अभी भी "परीक्षण" परिणाम प्राप्त करूंगा। पुस्तक के शीर्षक के साथ उपयोग के मामले की कल्पना करें। लोग अक्सर पूरी तरह से सटीक शीर्षक के बजाय आंशिक पुस्तक खिताब की तलाश करते हैं।
तेहफेलसेफ

13
@suulisin आप सही हैं, मैंने इसे ध्यान से नहीं पढ़ा क्योंकि मुझे जो कुछ मिला था उसे साझा करने के लिए उत्सुक था। इस बात को इंगित करने के आपके प्रयास के लिए धन्यवाद, और मैं अधिक सावधान रहूंगा
Zach J
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.