all repos — litestore @ aa96542a6a02be9e968255edbeee4c8e18e37059

A minimalist nosql document store.

Implemented index API.
h3rald h3rald@h3rald.com
Mon, 13 Jan 2020 10:56:17 +0100
commit

aa96542a6a02be9e968255edbeee4c8e18e37059

parent

c8dc3309c747fe1427dcf0546d0a6835e1ee3ccb

M src/litestorepkg/lib/api_v5.nimsrc/litestorepkg/lib/api_v5.nim

@@ -129,7 +129,10 @@ currentArr = currentArr + 1

tables.add "json_each(documents.data, '$1') AS arr$2" % [x[0], $currentArr] return "arr$1.value == $2" % [$currentArr, x[2]] else: - return "json_extract(documents.data, '$1') $2 $3 " % x + var arr = @[x[0], x[1], x[2]] + if x[1] == "like": + arr[2] = x[2].replace('*', '%') + return "json_extract(documents.data, '$1') $2 $3 " % arr return resAndClauses.join(" AND ") options.tables = options.tables & tables options.jsonFilter = resOrClauses.join(" OR ")

@@ -352,6 +355,16 @@ else:

result.content = $doc result.code = Http200 +proc getIndex*(LS: LiteStore, id: string, options = newQueryOptions(), req: LSRequest): LSResponse = + let doc = LS.store.retrieveIndex(id, options) + result.headers = ctJsonHeader() + setOrigin(LS, req, result.headers) + if doc == newJObject(): + result = resIndexNotFound(id) + else: + result.content = $doc + result.code = Http200 + proc getRawDocument*(LS: LiteStore, id: string, options = newQueryOptions(), req: LSRequest): LSResponse = let doc = LS.store.retrieveRawDocument(id, options) result.headers = ctJsonHeader()

@@ -415,6 +428,31 @@ setOrigin(LS, req, result.headers)

result.content = content.pretty result.code = Http200 +proc getIndexes*(LS: LiteStore, options: QueryOptions = newQueryOptions(), req: LSRequest): LSResponse = + var options = options + let t0 = cpuTime() + let docs = LS.store.retrieveIndexes(options) + let orig_limit = options.limit + let orig_offset = options.offset + options.limit = 0 + options.offset = 0 + options.select = @["COUNT(name)"] + let total = LS.store.countIndexes(prepareSelectIndexesQuery(options), options.like.replace("*", "%")) + var content = newJObject() + if options.like != "": + content["like"] = %(options.like.decodeURL) + if orig_limit > 0: + content["limit"] = %orig_limit + if orig_offset > 0: + content["offset"] = %orig_offset + content["total"] = %total + content["execution_time"] = %(cputime()-t0) + content["results"] = docs + result.headers = ctJsonHeader() + setOrigin(LS, req, result.headers) + result.content = content.pretty + result.code = Http200 + proc getRawDocuments*(LS: LiteStore, options: QueryOptions = newQueryOptions(), req: LSRequest): LSResponse = var options = options let t0 = cpuTime()

@@ -720,6 +758,16 @@ else:

return LS.getTags(options, req) except: return resError(Http400, "Bad Request - $1" % getCurrentExceptionMsg()) + of "indexes": + var options = newQueryOptions() + try: + parseQueryOptions(req.url.query, options); + if id != "": + return LS.getIndex(id, options, req) + else: + return LS.getIndexes(options, req) + except: + return resError(Http400, "Bad Request - $1" % getCurrentExceptionMsg()) of "info": if id != "": return resError(Http404, "Info '$1' not found." % id)

@@ -736,11 +784,13 @@

proc put*(req: LSRequest, LS: LiteStore, resource: string, id = ""): LSResponse = if id != "": if resource == "indexes": + var field = "" try: - let field = parseJson(req.body.strip)["field"].getStr - return LS.putIndex(id, field, req) + echo req.body + field = parseJson(req.body.strip)["field"].getStr except: return resError(Http400, "Bad Request - Invalid JSON body - $1" % getCurrentExceptionMsg()) + return LS.putIndex(id, field, req) else: # Assume docs var ct = "text/plain" if req.headers.hasKey("Content-Type"):
M src/litestorepkg/lib/core.nimsrc/litestorepkg/lib/core.nim

@@ -127,13 +127,50 @@ store.begin()

store.db.exec(query) store.commit() -proc retrieveIndexes*(store: Datastore): JsonNode = - let raw_indexes= store.db.getAllRows(SQL_GET_DOCUMENTS_INDEXES) +proc retrieveIndex*(store: Datastore, id: string, options: QueryOptions = newQueryOptions()): JsonNode = + var options = options + options.single = true + let query = prepareSelectIndexesQuery(options) + let raw_index = store.db.getRow(query.sql, "json_index_" & id) + var matches: array[0..0, string] + let fieldPeg = peg"'CREATE INDEX json_index_test ON documents(json_extract(data, \'' {[^']+}" + discard raw_index[1].match(fieldPeg, matches) + return %[("id", %raw_index[0].replace("json_index_", "")), ("field", %matches[0])] + +proc retrieveIndexes*(store: Datastore, options: QueryOptions = newQueryOptions()): JsonNode = + var query = prepareSelectIndexesQuery(options) + var raw_indexes: seq[Row] + if (options.like.len > 0): + echo options.like + if (options.like[options.like.len-1] == '*' and options.like[0] != '*'): + let str = "json_index_" & options.like.substr(0, options.like.len-2) + echo str + raw_indexes = store.db.getAllRows(query.sql, str, str & "{") + else: + let str = "json_index_" & options.like.replace("*", "%") + echo str, "---" + raw_indexes = store.db.getAllRows(query.sql, str) + else: + raw_indexes = store.db.getAllRows(query.sql) var indexes = newSeq[JsonNode](0) for index in raw_indexes: - # TODO: parse field - indexes.add(%[("id", %index[0]), ("field", %(index[1]))]) + var matches: array[0..0, string] + let fieldPeg = peg"'CREATE INDEX json_index_test ON documents(json_extract(data, \'' {[^']+}" + discard index[1].match(fieldPeg, matches) + indexes.add(%[("id", %index[0]), ("field", %matches[0])]) return %indexes + +proc countIndexes*(store: Datastore, q = "", like = ""): int64 = + var query = SQL_COUNT_INDEXES + if q.len > 0: + query = q.sql + if like.len > 0: + if (like[like.len-1] == '%' or like[like.len-1] == '*'): + let str = like.substr(0, like.len-2) + return store.db.getRow(query, str, str & "{")[0].parseInt + else: + return store.db.getRow(query, like)[0].parseInt + return store.db.getRow(query)[0].parseInt # Manage Tags
M src/litestorepkg/lib/queries.nimsrc/litestorepkg/lib/queries.nim

@@ -29,8 +29,6 @@ SQL_REINDEX* = sql"REINDEX"

SQL_OPTIMIZE* = sql"INSERT INTO searchdata(searchdata) VALUES('optimize')" SQL_REBUILD* = sql"INSERT INTO searchdata(searchdata) VALUES('rebuild')" - SQL_GET_DOCUMENTS_INDEXES* = sql"select name, sql from sqlite_master where type = 'index' and tbl_name = 'documents' and name LIKE 'json_index_%'" - SQL_VACUUM* = sql"VACUUM" const SQL_CREATE_SEARCHDATA_TABLE* = sql"""

@@ -169,6 +167,10 @@ """

const SQL_COUNT_TAGS* = sql""" SELECT COUNT(DISTINCT tag_id) FROM tags +""" + +const SQL_COUNT_INDEXES* = sql""" +SELECT COUNT(DISTINCT name) FROM from sqlite_master WHERE type = 'index' AND tbl_name = 'documents' AND name LIKE 'json_index_%' """ const SQL_COUNT_DOCUMENTS* = sql"""
M src/litestorepkg/lib/utils.nimsrc/litestorepkg/lib/utils.nim

@@ -148,12 +148,37 @@ result = result & "FROM tags "

if options.single: result = result & "WHERE tag_id = ?" elif options.like.len > 0: - if options.like[options.like.len-1] == '*': + if options.like[options.like.len-1] == '*' and options.like[0] != '*': result = result & "WHERE tag_id BETWEEN ? AND ? " else: result = result & "WHERE tag_id LIKE ? " if group: result = result & "GROUP BY tag_id " + if options.limit > 0: + result = result & "LIMIT " & $options.limit & " " + if options.offset > 0: + result = result & "OFFSET " & $options.offset & " " + LOG.debug(result.replace("$", "$$")) + +# select name, sql from sqlite_master where type = 'index' and tbl_name = 'documents' and name LIKE 'json_index_%' +proc prepareSelectIndexesQuery*(options: QueryOptions): string = + var group = true + if options.select.len > 0 and options.select[0] == "COUNT(name)": + result = "SELECT COUNT(DISTINCT name) " + result = result & "FROM sqlite_master WHERE type = 'index' AND tbl_name = 'documents' " + group = false + else: + result = "SELECT name, sql " + result = result & "FROM sqlite_master WHERE type = 'index' AND tbl_name = 'documents' " + if options.single: + result = result & "AND name = ?" + if options.like.len > 0: + if options.like[options.like.len-1] == '*' and options.like[0] != '*': + result = result & "AND name BETWEEN ? AND ? " + else: + result = result & "AND name LIKE ? " + else: + result = result & "AND name LIKE 'json_index_%' " if options.limit > 0: result = result & "LIMIT " & $options.limit & " " if options.offset > 0:

@@ -277,6 +302,9 @@ resError(Http404, "Document '$1' not found." % id)

proc resTagNotFound*(id: string): LSResponse = resError(Http404, "Tag '$1' not found." % id) + +proc resIndexNotFound*(id: string): LSResponse = + resError(Http404, "Index '$1' not found." % id) proc eWarn*() = var e = getCurrentException()