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

前端 未结 16 1564
抹茶落季
抹茶落季 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:46

    The selected answer only works for exact searches and is not natural user search behavior (searching for "apple" in "Joe ate an apple today" would not work).

    I think Dan Fein's answer above should be ranked higher. If the String data you're searching through is short, you can save all substrings of the string in an array in your Document and then search through the array with Firebase's array_contains query. Firebase Documents are limited to 1 MiB (1,048,576 bytes) (Firebase Quotas and Limits) , which is about 1 million characters saved in a document (I think 1 character ~= 1 byte). Storing the substrings is fine as long as your document isn't close to 1 million mark.

    Example to search user names:

    Step 1: Add the following String extension to your project. This lets you easily break up a string into substrings. (I found this here).

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

    Step 2: When you store a user's name, also store the result of this function as an array in the same Document. This creates all variations of the original text and stores them in an array. For example, the text input "Apple" would creates the following array: ["a", "p", "p", "l", "e", "ap", "pp", "pl", "le", "app", "ppl", "ple", "appl", "pple", "apple"], which should encompass all search criteria a user might enter. You can leave maximumStringSize as nil if you want all results, however, if there is long text, I would recommend capping it before the document size gets too big - somewhere around 15 works fine for me (most people don't search long phrases anyway).

    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.. max {
                break
            }
        }
    
        print(substringArray)
        return substringArray
    }
    

    Step 3: You can use Firebase's array_contains function!

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

提交回复
热议问题