Google Firestore: Query on substring of a property value (text search)

前端 未结 16 1359
抹茶落季
抹茶落季 2020-11-22 12:08

I am looking to add a simple search field, would like to use something like

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

I tried

相关标签:
16条回答
  • 2020-11-22 12:50

    This worked for me perfectly but might cause performance issues.

    Do this when querying firestore:

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

    Do this in your 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 讨论(0)
  • 2020-11-22 12:51

    If you don't want to use a third-party service like Algolia, Firebase Cloud Functions are a great alternative. You can create a function that can receive an input parameter, process through the records server-side and then return the ones that match your criteria.

    0 讨论(0)
  • 2020-11-22 12:53

    I'm sure Firebase will come out with "string-contains" soon to capture any index[i] startAt in the string... But I’ve researched the webs and found this solution thought of by someone else set up your data like this

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

    query like this

    firebase
    .firestore()
    .collection("clubs")
    .where(
     "titleAsArray",
     "array-contains",
     this.state.userQuery.toLowerCase()
    )
    
    0 讨论(0)
  • 2020-11-22 12:56

    A few notes here:

    1.) \uf8ff works the same way as ~

    2.) You can use a where clause or start end clauses:

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

    is exactly the same as

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

    3.) No, it does not work if you reverse startAt() and endAt() in every combination, however, you can achieve the same result by creating a second search field that is reversed, and combining the results.

    Example: First you have to save a reversed version of the field when the field is created. Something like this:

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

    With this, you can search the last letters of a string field and the first, just not random middle letters or groups of letters. This is closer to the desired result. However, this won't really help us when we want random middle letters or words. Also, remember to save everything lowercase, or a lowercase copy for searching, so case won't be an issue.

    4.) If you have only a few words, Ken Tan's Method will do everything you want, or at least after you modify it slightly. However, with only a paragraph of text, you will exponentially create more than 1MB of data, which is bigger than firestore's document size limit (I know, I tested it).

    5.) If you could combine array-contains (or some form of arrays) with the \uf8ff trick, you might could have a viable search that does not reach the limits. I tried every combination, even with maps, and a no go. Anyone figures this out, post it here.

    6.) If you must get away from ALGOLIA and ELASTIC SEARCH, and I don't blame you at all, you could always use mySQL, postSQL, or neo4Js on Google Cloud. They are all 3 easy to set up, and they have free tiers. You would have one cloud function to save the data onCreate() and another onCall() function to search the data. Simple...ish. Why not just switch to mySQL then? The real-time data of course! When someone writes DGraph with websocks for real-time data, count me in!

    Algolia and ElasticSearch were built to be search-only dbs, so there is nothing as quick... but you pay for it. Google, why do you lead us away from Google, and don't you follow MongoDB noSQL and allow searches?

    UPDATE - I CREATED A SOLUTION:

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

    0 讨论(0)
提交回复
热议问题