2020-12-28 05:01:07 +01:00
|
|
|
local lpeg = require('lpeg')
|
|
|
|
local etlua = require('etlua')
|
2023-03-12 17:24:22 +01:00
|
|
|
local config = require("config")
|
2020-12-28 05:01:07 +01:00
|
|
|
local args = {...}
|
|
|
|
lpeg.locale(lpeg)
|
|
|
|
local V,P,C,S,B,Cs = lpeg.V,lpeg.P,lpeg.C,lpeg.S,lpeg.B,lpeg.Cs
|
|
|
|
--Identity function
|
|
|
|
local ident = function(a) return a end
|
|
|
|
--Lowercase and capitalize first letter
|
|
|
|
local function capitalize(str)
|
|
|
|
return string.lower(str):gsub("^(.)",string.upper)
|
|
|
|
end
|
|
|
|
--Trim whitespace
|
|
|
|
local function trim(str)
|
|
|
|
return str:match("^%s*(.-)%s*$")
|
|
|
|
end
|
|
|
|
--SQL like match anywhere
|
|
|
|
local function like(str)
|
|
|
|
return "%" .. str .. "%"
|
|
|
|
end
|
|
|
|
--Tags are always trimed of whitepsace, lowercase with first letter capitalized
|
|
|
|
local function tag_fmt(str)
|
|
|
|
return capitalize(trim(str))
|
|
|
|
end
|
|
|
|
--Title match
|
|
|
|
local function title_fmt(str)
|
|
|
|
return like(trim(str))
|
|
|
|
end
|
|
|
|
--Author names are all lowercase alphanumeric, max 30 characters
|
|
|
|
local function author_fmt(str)
|
|
|
|
return like(string.lower(trim(str)))
|
|
|
|
end
|
|
|
|
local fieldnames = {
|
|
|
|
title = {name="title",type="string",fmt=title_fmt},
|
|
|
|
author = {name="author",type="string",fmt=author_fmt},
|
|
|
|
date = {name="date",type="number",fmt=tonumber},
|
|
|
|
hits = {name="hits",type="number",fmt=tonumber},
|
|
|
|
tags = {name="tag",type="string",fmt=tag_fmt},
|
|
|
|
}
|
|
|
|
local field_default = "tag"
|
|
|
|
local fields
|
|
|
|
local grammar = P{
|
|
|
|
"chunk";
|
|
|
|
whitespace = S" \t\n"^0,
|
2021-09-11 23:49:15 +02:00
|
|
|
itm = C((P(1 - (P" " * S"+-")))^0), --go until the next '+' or '-'
|
2020-12-28 05:01:07 +01:00
|
|
|
likefield = C(P"title" + P"author") * V"whitespace" * C(P"=") * V"whitespace" * V"itm",
|
|
|
|
rangeop = P"<=" + P">=" + P">" + P"<" + P"=",
|
|
|
|
rangefield = C(P"date" + P"hits") * V"whitespace" * C(V"rangeop") * V"whitespace" * C(V"itm"),
|
|
|
|
field = C(S"+-") * (V"likefield" + V"rangefield" + V"itm") / function(pn,field,expr,value)
|
|
|
|
if expr and value then
|
|
|
|
fields[field] = fields[field] or {}
|
|
|
|
table.insert(fields[field],{pn,expr,value})
|
|
|
|
else
|
|
|
|
fields.tags = fields.tags or {}
|
|
|
|
table.insert(fields.tags,{pn,"=",field})
|
|
|
|
end
|
|
|
|
end,
|
2021-09-11 23:49:15 +02:00
|
|
|
chunk = V"field" * (P" " * V"field")^0
|
2020-12-23 07:07:00 +01:00
|
|
|
|
2020-12-28 05:01:07 +01:00
|
|
|
}
|
2020-12-23 07:07:00 +01:00
|
|
|
--Grammar
|
|
|
|
--Transpile a sting with + and - into an sql query that searches tags
|
2023-03-12 17:24:22 +01:00
|
|
|
local fname = config.approot .. "pages/search_sql.etlua"
|
2020-12-28 05:01:07 +01:00
|
|
|
local sqltmpl = assert(io.open(fname))
|
|
|
|
local c = etlua.compile(sqltmpl:read("*a"),fname)
|
|
|
|
sqltmpl:close()
|
2020-12-23 07:07:00 +01:00
|
|
|
local function transpile(str)
|
2020-12-28 05:01:07 +01:00
|
|
|
str = string.lower(str)
|
|
|
|
fields = {tags={}}
|
|
|
|
table.concat({grammar:match(str)}," ")
|
|
|
|
--Sanity perform formatting on data
|
|
|
|
for field,values in pairs(fields) do
|
|
|
|
for _,value in pairs(values) do
|
|
|
|
local pn, expr, val = unpack(value)
|
|
|
|
local nval = fieldnames[field].fmt(val)
|
|
|
|
value[3] = nval
|
|
|
|
end
|
2020-12-23 07:07:00 +01:00
|
|
|
end
|
2020-12-28 05:01:07 +01:00
|
|
|
local ressql = c{
|
|
|
|
result = fields
|
|
|
|
}
|
|
|
|
return ressql, fields
|
2020-12-23 07:07:00 +01:00
|
|
|
end
|
|
|
|
|
|
|
|
return transpile
|