Lots of changes. Fully implement tkrzw
This commit is contained in:
parent
895fc2b359
commit
ce9a7ed56b
63
src/Db.zig
63
src/Db.zig
|
@ -64,6 +64,7 @@ pub fn open(path: [:0]const u8, writable: bool, params: [:0]const u8) Self {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Returrn error
|
||||||
pub fn set(self: *Self, key: []const u8, value: []const u8) void {
|
pub fn set(self: *Self, key: []const u8, value: []const u8) void {
|
||||||
const key_len = @intCast(i32, key.len);
|
const key_len = @intCast(i32, key.len);
|
||||||
const value_len = @intCast(i32, value.len);
|
const value_len = @intCast(i32, value.len);
|
||||||
|
@ -71,10 +72,68 @@ pub fn set(self: *Self, key: []const u8, value: []const u8) void {
|
||||||
_ = c.tkrzw_dbm_set(self.dbm, key.ptr, key_len, value.ptr, value_len, true);
|
_ = c.tkrzw_dbm_set(self.dbm, key.ptr, key_len, value.ptr, value_len, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(self: *Self, key: []const u8) []u8 {
|
// TODO: Returrn error
|
||||||
|
pub fn append(self: *Self, key: []const u8, value: []const u8, delim: []const u8) void {
|
||||||
|
const key_len = @intCast(i32, key.len);
|
||||||
|
const value_len = @intCast(i32, value.len);
|
||||||
|
const delim_len = @intCast(i32, delim.len);
|
||||||
|
|
||||||
|
_ = c.tkrzw_dbm_append(self.dbm, key.ptr, key_len, value.ptr, value_len, delim.ptr, delim_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn check(self: *Self, key: []const u8) bool {
|
||||||
|
const key_len = @intCast(i32, key.len);
|
||||||
|
|
||||||
|
return c.tkrzw_dbm_check(self.dbm, key.ptr, key_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
// You gotta call free on return
|
||||||
|
pub fn get(self: *Self, key: []const u8) ?[]u8 {
|
||||||
const key_len = @intCast(i32, key.len);
|
const key_len = @intCast(i32, key.len);
|
||||||
|
|
||||||
var len: i32 = 0;
|
var len: i32 = 0;
|
||||||
var ret = c.tkrzw_dbm_get(self.dbm, key.ptr, key_len, &len);
|
var ret = c.tkrzw_dbm_get(self.dbm, key.ptr, key_len, &len);
|
||||||
return ret[0..@intCast(usize, len)];
|
|
||||||
|
return if (ret) |nnret| nnret[0..@intCast(usize, len)] else return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getList(self: *Self, key: []const u8, allocator: std.mem.Allocator) !?[][:0]u8 {
|
||||||
|
const list_str = self.get(key) orelse return null;
|
||||||
|
defer Self.free(list_str.ptr);
|
||||||
|
|
||||||
|
const count = std.mem.count(u8, list_str, " ") + 1;
|
||||||
|
var buffer = try allocator.alloc([:0]u8, count);
|
||||||
|
|
||||||
|
var iter = std.mem.split(u8, list_str, " ");
|
||||||
|
var i: usize = 0;
|
||||||
|
while (iter.next()) |unit| {
|
||||||
|
defer i += 1;
|
||||||
|
|
||||||
|
buffer[i] = try allocator.dupeZ(u8, unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function to make it easier to find occurrences in lists
|
||||||
|
pub fn isInList(self: *Self, key: []const u8, needle: []const u8) bool {
|
||||||
|
if (self.get(key)) |haystack| {
|
||||||
|
defer Self.free(haystack.ptr);
|
||||||
|
|
||||||
|
if (isInStringDelim(haystack, needle, ' ')) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generic function to find occurences of a string inside another one with delimiters
|
||||||
|
fn isInStringDelim(haystack: []const u8, needle: []const u8, delim: u8) bool {
|
||||||
|
var spliter = std.mem.split(u8, haystack, &[_]u8{delim});
|
||||||
|
while (spliter.next()) |blade| {
|
||||||
|
if (std.mem.eql(u8, blade, needle)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
146
src/Item.zig
146
src/Item.zig
|
@ -2,17 +2,11 @@ const std = @import("std");
|
||||||
|
|
||||||
const json = @import("json.zig");
|
const json = @import("json.zig");
|
||||||
const Db = @import("Db.zig");
|
const Db = @import("Db.zig");
|
||||||
const sqlite = @import("sqlite");
|
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
const Tag = [:0]u8;
|
||||||
|
|
||||||
pub const Tag = struct {
|
id: ?[4:0]u8, // If null, the object hasn't been persisted
|
||||||
// :0 for C compatility
|
|
||||||
name: [:0]const u8,
|
|
||||||
value: ?[:0]const u8,
|
|
||||||
};
|
|
||||||
|
|
||||||
id: ?i64, // If null, the object hasn't been persisted
|
|
||||||
tags: ?[]Tag,
|
tags: ?[]Tag,
|
||||||
allocator: ?std.mem.Allocator = null,
|
allocator: ?std.mem.Allocator = null,
|
||||||
|
|
||||||
|
@ -22,57 +16,113 @@ pub fn deinit(self: *Self) void {
|
||||||
const tags = self.tags orelse return;
|
const tags = self.tags orelse return;
|
||||||
|
|
||||||
// Free each tag
|
// Free each tag
|
||||||
for (tags) |*tag| {
|
for (tags) |tag| {
|
||||||
// Free name
|
// Free name
|
||||||
allocator.free(tag.name);
|
allocator.free(tag);
|
||||||
|
|
||||||
// Free value
|
|
||||||
if (tag.value) |value| {
|
|
||||||
allocator.free(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Free the tags buffer
|
// Free the tags buffer
|
||||||
allocator.free(tags);
|
allocator.free(tags);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn persist(self: *Self, db: *sqlite.Db) !void {
|
const SubtagIterator = struct {
|
||||||
|
index: ?usize,
|
||||||
|
orig_tag: []const u8,
|
||||||
|
|
||||||
|
pub fn init(tag: []const u8) @This() {
|
||||||
|
return @This(){
|
||||||
|
.index = 0,
|
||||||
|
.orig_tag = tag,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(self: *@This()) ?[]const u8 {
|
||||||
|
if (self.index) |index| {
|
||||||
|
const opt_i = std.mem.indexOfScalarPos(u8, self.orig_tag, index, ':');
|
||||||
|
|
||||||
|
if (opt_i) |i| {
|
||||||
|
defer self.index = i+1;
|
||||||
|
return self.orig_tag[0..i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the nullability as a marker to not enter here again
|
||||||
|
defer self.index = null;
|
||||||
|
return self.orig_tag[0..];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn persist(self: *Self, db: *Db, allocator: std.mem.Allocator) !void {
|
||||||
// Insert only if there's no ID.
|
// Insert only if there's no ID.
|
||||||
if (self.id == null) {
|
if (self.id == null) {
|
||||||
const query = "INSERT INTO item VALUES(NULL);";
|
var opt_last_id = db.get("item:last");
|
||||||
try db.exec(query, .{}, .{});
|
defer if (opt_last_id) |i| Db.free(i.ptr);
|
||||||
|
|
||||||
|
// Get ID. Should be last+1 or "0000"
|
||||||
|
const id = if (opt_last_id) |last_id|
|
||||||
|
try Db.numEncode(Db.numDecode(last_id) + 1)
|
||||||
|
else
|
||||||
|
try Db.numEncode(0);
|
||||||
|
|
||||||
|
// Insert
|
||||||
|
db.append("item", &id, " ");
|
||||||
|
db.set("item:last", &id);
|
||||||
|
|
||||||
// Update the ID field
|
// Update the ID field
|
||||||
self.id = try Db.getLastId(db);
|
self.id = [_:0]u8{0} ** 4;
|
||||||
|
std.mem.copy(u8, self.id.?[0..], id[0..]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the tags haven't been initialized, don't touch anything
|
// If the tags haven't been initialized, don't touch anything
|
||||||
if (self.tags) |tags| {
|
if (self.tags) |tags| {
|
||||||
// Create new tags if they don't exist
|
const separator = " ";
|
||||||
// TODO: Do the "if they don't exist" part.
|
|
||||||
for (tags) |*tags_i| {
|
var item = "item:----".*;
|
||||||
const name_query = "INSERT OR IGNORE INTO tag (name) VALUES (?)";
|
std.mem.copy(u8, item[5..], self.id.?[0..]);
|
||||||
// TODO: This does work but doesn't prevent SQLite3 from crying on stdout
|
|
||||||
try db.exec(name_query, .{}, .{ .name = tags_i.name });
|
for (tags) |tag| {
|
||||||
const rel_query = "INSERT INTO item_tag (item, tag, value) VALUES (?, ?, ?);";
|
// Go through every subtag (tag -> tag:subtag -> tag:subtag:subsubtag -> ...)
|
||||||
try db.exec(rel_query, .{}, .{ .item = self.id, .tag = tags_i.name, .value = tags_i.value });
|
var iter = SubtagIterator.init(tag);
|
||||||
|
while (iter.next()) |subtag| {
|
||||||
|
// allocate "tag:<tagname>"
|
||||||
|
var tagkey = try std.mem.concat(allocator, u8, &[_][]const u8{"tag:", subtag});
|
||||||
|
defer allocator.free(tagkey);
|
||||||
|
|
||||||
|
// Append the tag to the total of tags if it doesn't exist
|
||||||
|
// "tag" => "<tag1> <tag2> <tag3> <tag4>"
|
||||||
|
if (!db.check(tagkey)) db.append("tag", subtag, separator);
|
||||||
|
|
||||||
|
// Skip if the item is already tagged with it
|
||||||
|
// TODO: Test if this works, lol
|
||||||
|
if (db.isInList(&item, subtag)) continue;
|
||||||
|
|
||||||
|
// Insert the tag into the item.
|
||||||
|
// "item:<id>" => "<tag1> <tag2> <tag3>"
|
||||||
|
// NOTE: Only assign the whole tag. Ex.: "<tag>:<subtag>" but not "<tag>"
|
||||||
|
if (tag.len == subtag.len) db.append(item[0..], tag, separator);
|
||||||
|
|
||||||
|
// "tag:<tag>" => "<id1> <id2> <id3>"
|
||||||
|
db.append(tagkey, &self.id.?, separator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getTagsById(id: i64, db: *sqlite.Db, allocator: std.mem.Allocator) !?[]Tag {
|
pub fn getById(id: []const u8, db: *Db, allocator: std.mem.Allocator) !?Self {
|
||||||
var stmt = try db.prepare("SELECT tag, value FROM item_tag WHERE item = ?");
|
var item_sel = "item:----".*;
|
||||||
defer stmt.deinit();
|
std.mem.copy(u8, item_sel[5..], id[0..]);
|
||||||
|
|
||||||
const res = try stmt.all(Tag, allocator, .{}, .{ .id = id });
|
const tags = (try db.getList(&item_sel, allocator)) orelse return null;
|
||||||
|
|
||||||
if (res.len < 1) {
|
var aid: [4:0]u8 = "0000".*;
|
||||||
// Detect wether it returns 0 because it has no tags or because it doesn't exist
|
std.mem.copy(u8, &aid, id);
|
||||||
const exists = (try db.one(i64, "SELECT COUNT(id) FROM item WHERE id = ?", .{}, .{id})).?;
|
|
||||||
return if (exists == 0) null else res;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
return Self{
|
||||||
|
.id = aid,
|
||||||
|
.tags = tags,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert into a JSON object. A call to deinit() is necessary from the caller's side
|
/// Convert into a JSON object. A call to deinit() is necessary from the caller's side
|
||||||
|
@ -81,20 +131,15 @@ pub fn toJson(self: Self) json.Obj {
|
||||||
var jobj = json.Obj.newObject();
|
var jobj = json.Obj.newObject();
|
||||||
|
|
||||||
// Add id (i64|null)
|
// Add id (i64|null)
|
||||||
jobj.objectAdd("id", if (self.id) |id| &json.Obj.newInt64(id) else null);
|
jobj.objectAdd("id", if (self.id) |id| &json.Obj.newString(&id) else null);
|
||||||
|
|
||||||
// Add tags only if they're initialized
|
// Add tags only if they're initialized
|
||||||
if (self.tags) |tags| {
|
if (self.tags) |tags| {
|
||||||
var jtags = json.Obj.newObject();
|
var jtags = json.Obj.newArray();
|
||||||
jobj.objectAdd("tags", &jtags);
|
jobj.objectAdd("tags", &jtags);
|
||||||
|
|
||||||
for (tags) |tag_i| {
|
for (tags) |tag_i| {
|
||||||
const jtag = if (tag_i.value) |value|
|
jtags.arrayAdd(&json.Obj.newString(tag_i));
|
||||||
&json.Obj.newString(value)
|
|
||||||
else
|
|
||||||
null;
|
|
||||||
|
|
||||||
jtags.objectAdd(tag_i.name, jtag);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// If nothing is found, set it to null
|
// If nothing is found, set it to null
|
||||||
|
@ -116,12 +161,12 @@ pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self {
|
||||||
tags = try tagsFromJson(jobj, allocator);
|
tags = try tagsFromJson(jobj, allocator);
|
||||||
}
|
}
|
||||||
|
|
||||||
var id: ?i64 = null;
|
var id: ?[4:0]u8 = null;
|
||||||
|
|
||||||
if (jobj.objectGet("id") catch null) |*jid| {
|
if (jobj.objectGet("id") catch null) |*jid| {
|
||||||
defer jid.deinit();
|
defer jid.deinit();
|
||||||
|
|
||||||
id = jid.getInt64();
|
std.mem.copy(u8, id, jid.getString());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: What should be done when both things are null?
|
// TODO: What should be done when both things are null?
|
||||||
|
@ -136,16 +181,15 @@ pub fn fromJson(jobj: json.Obj, allocator: std.mem.Allocator) !Self {
|
||||||
|
|
||||||
pub fn tagsFromJson(jobj: *json.Obj, allocator: std.mem.Allocator) ![]Tag {
|
pub fn tagsFromJson(jobj: *json.Obj, allocator: std.mem.Allocator) ![]Tag {
|
||||||
// Reserve space for slice of tags
|
// Reserve space for slice of tags
|
||||||
const len = @intCast(usize, jobj.objectLen());
|
const len = @intCast(usize, jobj.arrayLen());
|
||||||
var tags = try allocator.alloc(Tag, len);
|
var tags = try allocator.alloc(Tag, len);
|
||||||
|
|
||||||
var iter = jobj.objectGetIterator();
|
var iter = jobj.arrayGetIterator();
|
||||||
|
|
||||||
var i: usize = 0;
|
var i: usize = 0;
|
||||||
while (iter.next()) |*tag| {
|
while (iter.next()) |*tag| {
|
||||||
// Whe know it's not null at this point
|
// Whe know it's not null at this point
|
||||||
tags[i].name = try allocator.dupeZ(u8, tag.key);
|
tags[i] = try allocator.dupeZ(u8, tag.getString());
|
||||||
tags[i].value = if (tag.value) |*value| try allocator.dupeZ(u8, value.getString()) else null;
|
|
||||||
|
|
||||||
i += 1;
|
i += 1;
|
||||||
}
|
}
|
||||||
|
|
93
src/main.zig
93
src/main.zig
|
@ -9,60 +9,47 @@ const request = @import("request.zig");
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
|
||||||
var db = Db.open("test.tkh", true, "");
|
var db = Db.open("test.tkh", true, "");
|
||||||
db.set("test", "lol");
|
|
||||||
db.set("kaka", "jeje");
|
|
||||||
db.set("kaka", "poop");
|
|
||||||
|
|
||||||
var str = db.get("test");
|
const jsonText: [:0]const u8 =
|
||||||
defer Db.free(str.ptr);
|
\\{ "add" : [
|
||||||
std.debug.print("{s}\n", .{str});
|
\\ [
|
||||||
|
\\ "task:uned:led",
|
||||||
|
\\ "fur:dusk",
|
||||||
|
\\ "made_with:krita",
|
||||||
|
\\ "date:2022:10:01",
|
||||||
|
\\ "alunya"
|
||||||
|
\\ ],[
|
||||||
|
\\ "fur:lara",
|
||||||
|
\\ "made_with:krita",
|
||||||
|
\\ "date:2022:04:08",
|
||||||
|
\\ "cell-shading"
|
||||||
|
\\ ],[
|
||||||
|
\\ "made_with:ballpoint_pen",
|
||||||
|
\\ "date:2022:11:04",
|
||||||
|
\\ "practice",
|
||||||
|
\\ "pose_practice"
|
||||||
|
\\ ],[
|
||||||
|
\\ "fur:lidiarock1",
|
||||||
|
\\ "made_with:krita",
|
||||||
|
\\ "date:2022:10:15",
|
||||||
|
\\ "niko_(oneshot)"
|
||||||
|
\\ ]
|
||||||
|
\\] }
|
||||||
|
;
|
||||||
|
|
||||||
const num = 69420;
|
var jobj = json.Obj.newFromString(jsonText);
|
||||||
const numch = try Db.numEncode(num);
|
defer jobj.deinit();
|
||||||
std.debug.print("{}\n", .{Db.numDecode(numch[0..])});
|
|
||||||
std.debug.print("{}\n", .{num});
|
|
||||||
|
|
||||||
//var db = try Db.init();
|
try request.process(&jobj, &db);
|
||||||
//
|
|
||||||
//const jsonText: [:0]const u8 =
|
std.debug.print("\n\n", .{});
|
||||||
//\\{ "add" : [
|
|
||||||
//\\ {
|
const jsonQuery: [:0]const u8 =
|
||||||
//\\ "fur": "dusk",
|
\\{ "query" : "fur made_with:krita -fur:dusk", "limit" : 20 }
|
||||||
//\\ "made_with": "krita",
|
;
|
||||||
//\\ "date": "2022-10-01",
|
|
||||||
//\\ "alunya": null
|
var jquery = json.Obj.newFromString(jsonQuery);
|
||||||
//\\ },{
|
defer jquery.deinit();
|
||||||
//\\ "fur": "lara",
|
|
||||||
//\\ "made_with": "krita",
|
try request.process(&jquery, &db);
|
||||||
//\\ "date": "2022-04-08",
|
|
||||||
//\\ "cell-shading": null
|
|
||||||
//\\ },{
|
|
||||||
//\\ "made_with": "ballpoint_pen",
|
|
||||||
//\\ "date": "2022-11-04",
|
|
||||||
//\\ "practice": "",
|
|
||||||
//\\ "pose_practice": null
|
|
||||||
//\\ },{
|
|
||||||
//\\ "fur": "lidiarock1",
|
|
||||||
//\\ "made_with": "krita",
|
|
||||||
//\\ "date": "2022-02-15",
|
|
||||||
//\\ "niko_(oneshot)": null
|
|
||||||
//\\ }
|
|
||||||
//\\] }
|
|
||||||
//;
|
|
||||||
//
|
|
||||||
//var jobj = json.Obj.newFromString(jsonText);
|
|
||||||
//defer jobj.deinit();
|
|
||||||
//
|
|
||||||
//try request.process(&jobj, &db);
|
|
||||||
//
|
|
||||||
//std.debug.print("\n\n", .{});
|
|
||||||
//
|
|
||||||
//const jsonQuery: [:0]const u8 =
|
|
||||||
//\\{ "query" : "niko_(oneshot) made_with:krita -fur:dusk" }
|
|
||||||
//;
|
|
||||||
//
|
|
||||||
//var jquery = json.Obj.newFromString(jsonQuery);
|
|
||||||
//defer jquery.deinit();
|
|
||||||
//
|
|
||||||
//try request.process(&jquery, &db);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const sqlite = @import("sqlite");
|
|
||||||
|
|
||||||
const Db = @import("Db.zig");
|
const Db = @import("Db.zig");
|
||||||
const Item = @import("Item.zig");
|
const Item = @import("Item.zig");
|
||||||
const Tag = @import("Tag.zig");
|
const Tag = @import("Tag.zig");
|
||||||
const json = @import("json.zig");
|
const json = @import("json.zig");
|
||||||
|
|
||||||
pub fn process(jobj: *json.Obj, db: *sqlite.Db) !void {
|
pub fn process(jobj: *json.Obj, db: *Db) !void {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
@ -28,7 +27,7 @@ pub fn process(jobj: *json.Obj, db: *sqlite.Db) !void {
|
||||||
std.debug.print("{s}", .{jret.toString()});
|
std.debug.print("{s}", .{jret.toString()});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json.Obj {
|
pub fn add(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj {
|
||||||
// TODO: Maybe return error when no items in the array?
|
// TODO: Maybe return error when no items in the array?
|
||||||
|
|
||||||
// Freed by the caller
|
// Freed by the caller
|
||||||
|
@ -43,7 +42,7 @@ pub fn add(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json.
|
||||||
item.deinit();
|
item.deinit();
|
||||||
|
|
||||||
// Insert new items into the DB
|
// Insert new items into the DB
|
||||||
try item.persist(db);
|
try item.persist(db, allocator);
|
||||||
|
|
||||||
// Add item to new json array (Makes a deep copy, freed with jret.deinit())
|
// Add item to new json array (Makes a deep copy, freed with jret.deinit())
|
||||||
jret.arrayAdd(&item.toJson());
|
jret.arrayAdd(&item.toJson());
|
||||||
|
@ -52,43 +51,31 @@ pub fn add(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json.
|
||||||
return jret;
|
return jret;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn query(jobj: *json.Obj, db: *sqlite.Db, allocator: std.mem.Allocator) !json.Obj {
|
pub fn query(jobj: *json.Obj, db: *Db, allocator: std.mem.Allocator) !json.Obj {
|
||||||
_ = db;
|
const query_str = jobj.getString();
|
||||||
var jret = json.Obj.newArray();
|
var jret = json.Obj.newArray();
|
||||||
|
|
||||||
const query_string = jobj.getString();
|
// Go through each tag
|
||||||
|
var iter = std.mem.split(u8, query_str, " ");
|
||||||
|
const opt_tag = iter.next();
|
||||||
|
|
||||||
var keywords = std.ArrayList([]const u8).init(allocator);
|
if (opt_tag) |tag| {
|
||||||
var sql = std.ArrayList(u8).init(allocator);
|
// Get the tag selector: "tag:<tag>"
|
||||||
try sql.appendSlice("SELECT * FROM item_tag WHERE");
|
const tag_sel = try std.mem.concat(allocator, u8, &[_][]const u8{"tag:", tag});
|
||||||
|
defer allocator.free(tag_sel);
|
||||||
|
|
||||||
var iter = std.mem.split(u8, query_string, " ");
|
// Get the items that have that tag: "<item1> <item2> <item3>"
|
||||||
|
const tag_str = db.get(tag_sel) orelse return jret;
|
||||||
|
defer Db.free(tag_str.ptr);
|
||||||
|
|
||||||
var i: usize = 0;
|
// Iterate through the items id
|
||||||
while(iter.next()) |tag| {
|
var item_iter = std.mem.split(u8, tag_str, " ");
|
||||||
defer i += 1;
|
while (item_iter.next()) |item_id| {
|
||||||
|
const item = (try Item.getById(item_id, db, allocator)) orelse continue;
|
||||||
|
|
||||||
try keywords.append(tag);
|
jret.arrayAdd(&item.toJson());
|
||||||
|
|
||||||
// Just put AND between keywords
|
|
||||||
if (i > 0) {
|
|
||||||
try sql.appendSlice(" AND");
|
|
||||||
}
|
}
|
||||||
// Exclude tags that start with !
|
|
||||||
|
|
||||||
try sql.appendSlice(" tag ");
|
|
||||||
|
|
||||||
if (tag[0] == '-') {
|
|
||||||
try sql.append('!');
|
|
||||||
}
|
|
||||||
try sql.appendSlice("= ?");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std.debug.print("{s}\n", .{sql.items});
|
|
||||||
|
|
||||||
//var stmt = try db.prepareDynamic();
|
|
||||||
//defer stmt.deinit();
|
|
||||||
//
|
|
||||||
return jret;
|
return jret;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue