SearchExpressionParser

Swift 5.0 Version License Platform Carthage compatible

解析搜索字符串(例如:您在搜索引擎中输入的内容)为可评估的表达式。

解析

您调用 Parser.parse(searchString:)。这会返回已解析的表达式组合的树状结构。您可以询问 Expression 对象它是否与给定的“haystack”(例如要搜索的文本)匹配,例如:

import SearchExpressionParser
guard let expr = try? Parser.parse(searchString: "Hello") else { fatalError() }
expr.isSatisfied(by: "Hello World!") // true

空搜索字符串会被评估为匹配任何内容的通配符。

高效的全文搜索

为了在应用程序中有效地使用搜索表达式,我发现对文本的全部小写表示形式进行操作并使用 C 的 strstr 非常有益。

因此,例如在笔记应用程序中,您应该考虑将笔记在内存中转换为小写,然后使用 C 字符串比较来进行表达式匹配。

首先,让您的文本实现 CStringExpressionSatisfiable 协议

struct Note {
    let text: String
    private let cString: [CChar]

    init(text: String) {
        self.text = text
        self.cString = text
            // Favor simple over grapheme cluster characters
            .precomposedStringWithCanonicalMapping
            .cString(using: .utf8)!
    }
}

import SearchExpressionParser

extension Note: CStringExpressionSatisfiable {
    func matches(needle: [CChar]) -> Bool {
        return strstr(self.cString, needle) != nil
    }
}

然后将此对象传递给表达式。

let warAndPeace = Note(String(contentsOf: "books/Tolstoy/War-and-Peace.txt"))
let protagonist = try! Parser.parse(searchString: "\"Pierre Bezukhov\" OR \"Pyotr Kirillovich\"")
protagonist.isSatisfied(by: warAndPeace) // true

遗憾的是,这会将实现匹配算法的负担放在您这边,但这是设计使然,以便您保留 C 字符串,而不是依赖框架为您即时转换文本——因为那样做毫无用处。与常规的 String.contains 匹配相比,这种速度提升非常值得付出几行代码的代价,后者在涉及 Emoji 表情符号时甚至会变得更慢。

运算符

运算符均为大写:ANDORNOT/!

您可以使用括号将表达式分组

!(foo OR (baz AND !bar))

... 当然,等同于

!foo OR !baz AND !foo OR !bar

到目前为止,还没有真正的运算符优先级实现,因为我在其中使用此功能的全文搜索上下文不需要它。

此嵌套项的 Expression 对象看起来像这样

// !(foo OR (baz AND !bar))
NotNode(
    OrNode(lhs: ContainsNode("foo"), 
           rhs: AndNode(lhs: ContainsNode("baz"), 
                        rhs: NotNode(ContainsNode("bar")))))

表达式

当您调用高级入口点 Parser.parse(searchString:) 时,您会获得一个符合 Expression 的对象作为返回值。

Expression 协议是

public protocol Expression {
    func isSatisfied(by satisfiable: StringExpressionSatisfiable) -> Bool
    func isSatisfied(by satisfiable: CStringExpressionSatisfiable) -> Bool
}

您可以将“haystack”传递给 isStatisfied,例如您要搜索的文本。

当单词的大小写无关紧要时,请记住,如果您让要搜索的文本符合 CStringExpressionSatisfiable 并传递那个对象,速度会快得多。有关详细信息,请参见上文。

提供的表达式有

使用此功能的应用程序

在您的应用程序中使用它?打开一个 PR 并告诉世界!

许可证

版权所有 (c) 2018-2019 Christian Tietze。根据 MIT 许可证分发。