diff --git a/src/plugins/messageStorage/sqlite.js b/src/plugins/messageStorage/sqlite.js index 758e0054..6ce93211 100644 --- a/src/plugins/messageStorage/sqlite.js +++ b/src/plugins/messageStorage/sqlite.js @@ -205,9 +205,12 @@ class MessageStorage { return Promise.resolve([]); } + // Using the '@' character to escape '%' and '_' in patterns. + const escapedSearchTerm = query.searchTerm.replace(/([%_@])/g, "@$1"); + let select = - 'SELECT msg, type, time, network, channel FROM messages WHERE type = "message" AND json_extract(msg, "$.text") LIKE ?'; - const params = [`%${query.searchTerm}%`]; + 'SELECT msg, type, time, network, channel FROM messages WHERE type = "message" AND json_extract(msg, "$.text") LIKE ? ESCAPE \'@\''; + const params = [`%${escapedSearchTerm}%`]; if (query.networkUuid) { select += " AND network = ? "; diff --git a/test/plugins/sqlite.js b/test/plugins/sqlite.js index 4bf2c423..4dadef14 100644 --- a/test/plugins/sqlite.js +++ b/test/plugins/sqlite.js @@ -181,6 +181,68 @@ describe("SQLite Message Storage", function () { } }); + it("should search messages with escaped wildcards", function () { + function assertResults(query, expected) { + return store + .search({ + searchTerm: query, + networkUuid: "this-is-a-network-guid2", + }) + .then((messages) => { + expect(messages.results.map((i) => i.text)).to.deep.equal(expected); + }); + } + + const originalMaxHistory = Helper.config.maxHistory; + + try { + Helper.config.maxHistory = 3; + + store.index( + {uuid: "this-is-a-network-guid2"}, + {name: "#channel"}, + new Msg({ + time: 123456790, + text: `foo % bar _ baz`, + }) + ); + + store.index( + {uuid: "this-is-a-network-guid2"}, + {name: "#channel"}, + new Msg({ + time: 123456791, + text: `foo bar x baz`, + }) + ); + + store.index( + {uuid: "this-is-a-network-guid2"}, + {name: "#channel"}, + new Msg({ + time: 123456792, + text: `bar @ baz`, + }) + ); + + return ( + store + .getMessages({uuid: "this-is-a-network-guid2"}, {name: "#channel"}) + // .getMessages() waits for store.index() transactions to commit + .then(() => assertResults("foo", ["foo % bar _ baz", "foo bar x baz"])) + .then(() => assertResults("%", ["foo % bar _ baz"])) + .then(() => assertResults("foo % bar ", ["foo % bar _ baz"])) + .then(() => assertResults("_", ["foo % bar _ baz"])) + .then(() => assertResults("bar _ baz", ["foo % bar _ baz"])) + .then(() => assertResults("%%", [])) + .then(() => assertResults("@%", [])) + .then(() => assertResults("@", ["bar @ baz"])) + ); + } finally { + Helper.config.maxHistory = originalMaxHistory; + } + }); + it("should close database", function (done) { store.close((err) => { expect(err).to.be.null;