Skip to content

Commit

Permalink
fix(lmdb): restore the removed tag function
Browse files Browse the repository at this point in the history
  • Loading branch information
chobits committed Nov 21, 2024
1 parent 7d2f2c1 commit dff5c0d
Show file tree
Hide file tree
Showing 5 changed files with 845 additions and 50 deletions.
324 changes: 324 additions & 0 deletions kong/db/strategies/off/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,318 @@ local function page_for_prefix(self, prefix, size, offset, options, follow)
end


-- AI generated function:
--
-- This function filters a list of items based on a set of tags and a condition
-- ("and"/"or").
--
-- @items : a Lua array, where each item has a `tags` array indicating its
-- associated tags.
-- @tags : a Lua array containing tag names, e.g., { "admin", "public" }.
-- @tags_cond : specifies the condition for tag matching: "and" requires all tags
-- to match, while "or" requires at least one tag to match.
local function filter_tag_items(items, tags, tags_cond)
assert(tags_cond == "and" or tags_cond == "or")

-- Define the filter logic
local function matches_condition(item_tags, tags, tags_cond)
local matches = {}
for _, tag in ipairs(tags) do
matches[tag] = false
end

-- Mark matches
for _, item_tag in ipairs(item_tags) do
if matches[item_tag] ~= nil then
matches[item_tag] = true
end
end

-- Evaluate the condition
if tags_cond == "and" then
for _, matched in pairs(matches) do
if not matched then
return false
end
end
return true
end

if tags_cond == "or" then
for _, matched in pairs(matches) do
if matched then
return true
end
end
return false
end
end

-- Filter the items
local filtered_items = {}
for _, item in ipairs(items) do
if item.tags and matches_condition(item.tags, tags, tags_cond) then
table.insert(filtered_items, item)
end
end

return filtered_items
end


-- Tags are a global concept across workspaces, there we don't need to handle
-- ws_id here.
local function page_for_tags(self, size, offset, options)
-- /tags
-- /tags/:tags
if self.schema.name == "tags" then

local matched_tags = nil

if options.tags then
matched_tags = {}
for _, tag in ipairs(options.tags) do
matched_tags[tag] = true
end
end

-- Each page operation retrieves the entities of only one DAO type.
local offset_name, offset_token

if offset then
offset_name, offset_token = offset:match("^([^|]+)|(.+)")
if not offset_name then
return nil, self.errors:invalid_offset(offset, "bad offset string")
end

if offset_token == "nil" then
offset_token = nil

else
offset_token = decode_base64(offset_token)
if not offset_token then
return nil, self.errors:invalid_offset(offset_token, "bad base64 encoding")
end
end
end

local dao

if offset_token then
dao = kong.db.daos[offset_name]
else
offset_name, dao = next(kong.db.daos, offset_name)
end

local prefix = item_key_prefix(offset_name, "*")

local rows, err

-- TODO: need a more elegant solution, page_for_prefix will use
-- self.schema to search entities
local orig_schema = self.schema -- use dao schema instead
self.schema = dao.schema

rows, err, offset_token = page_for_prefix(self, prefix, size, offset_token,
options, true)

self.schema = orig_schema -- restore schema

if not rows then
return nil, err
end

local items = {}
for _, item in ipairs(rows) do
for _, tag in ipairs(item.tags or {}) do
-- TODO: Could item.id be used as entity_id precisely?
if not matched_tags or matched_tags[tag] then
local item = { tag = tag, entity_name = name, entity_id = item.id }
table.insert(items, item)
end
end
end

if not offset_token and not next(kong.db.daos, offset_name) then -- end
offset = nil
else
offset = offset_name .. "|" .. (offset_token or "nil")
end

return items, nil, offset
end

-- /:entitiy?tags=:tags
-- search all key-values: <entity_name>|*|*|<pk_string> => actual item key
local prefix = item_key_prefix(self.schema.name, "*") -- "<entity_name>|*|*|"
local items, err, offset = page_for_prefix(self, prefix, size, offset,
options, true)
if not items then
return nil, err
end

items = filter_tag_items(items, options.tags, options.tags_cond)

return items, err, offset
end


-- AI generated function:
--
-- This function filters a list of items based on a set of tags and a condition
-- ("and"/"or").
--
-- @items : a Lua array, where each item has a `tags` array indicating its
-- associated tags.
-- @tags : a Lua array containing tag names, e.g., { "admin", "public" }.
-- @tags_cond : specifies the condition for tag matching: "and" requires all tags
-- to match, while "or" requires at least one tag to match.
local function filter_tag_items(items, tags, tags_cond)
assert(tags_cond == "and" or tags_cond == "or")

-- Define the filter logic
local function matches_condition(item_tags, tags, tags_cond)
local matches = {}
for _, tag in ipairs(tags) do
matches[tag] = false
end

-- Mark matches
for _, item_tag in ipairs(item_tags) do
if matches[item_tag] ~= nil then
matches[item_tag] = true
end
end

-- Evaluate the condition
if tags_cond == "and" then
for _, matched in pairs(matches) do
if not matched then
return false
end
end
return true
end

if tags_cond == "or" then
for _, matched in pairs(matches) do
if matched then
return true
end
end
return false
end
end

-- Filter the items
local filtered_items = {}
for _, item in ipairs(items) do
if item.tags and matches_condition(item.tags, tags, tags_cond) then
table.insert(filtered_items, item)
end
end

return filtered_items
end


-- Tags are a global concept across workspaces, there we don't need to handle
-- ws_id here.
local function page_for_tags(self, size, offset, options)
-- /tags
-- /tags/:tags
if self.schema.name == "tags" then

local matched_tags = nil

if options.tags then
matched_tags = {}
for _, tag in ipairs(options.tags) do
matched_tags[tag] = true
end
end

-- Each page operation retrieves the entities of only one DAO type.
local offset_name, offset_token

if offset then
offset_name, offset_token = offset:match("^([^|]+)|(.+)")
if not offset_name then
return nil, self.errors:invalid_offset(offset, "bad offset string")
end

if offset_token == "nil" then
offset_token = nil

else
offset_token = decode_base64(offset_token)
if not offset_token then
return nil, self.errors:invalid_offset(offset_token, "bad base64 encoding")
end
end
end

local dao

if offset_token then
dao = kong.db.daos[offset_name]
else
offset_name, dao = next(kong.db.daos, offset_name)
end

local prefix = item_key_prefix(offset_name, "*")

local rows, err

-- TODO: need a more elegant solution, page_for_prefix will use
-- self.schema to search entities
local orig_schema = self.schema -- use dao schema instead
self.schema = dao.schema

rows, err, offset_token = page_for_prefix(self, prefix, size, offset_token,
options, true)

self.schema = orig_schema -- restore schema

if not rows then
return nil, err
end

local items = {}
for _, item in ipairs(rows) do
for _, tag in ipairs(item.tags or {}) do
-- TODO: Could item.id be used as entity_id precisely?
if not matched_tags or matched_tags[tag] then
local item = { tag = tag, entity_name = name, entity_id = item.id }
table.insert(items, item)
end
end
end

if not offset_token and not next(kong.db.daos, offset_name) then -- end
offset = nil
else
offset = offset_name .. "|" .. (offset_token or "nil")
end

return items, nil, offset
end

-- /:entitiy?tags=:tags
-- search all key-values: <entity_name>|*|*|<pk_string> => actual item key
local prefix = item_key_prefix(self.schema.name, "*") -- "<entity_name>|*|*|"
local items, err, offset = page_for_prefix(self, prefix, size, offset,
options, true)
if not items then
return nil, err
end

items = filter_tag_items(items, options.tags, options.tags_cond)

return items, err, offset
end


local function page(self, size, offset, options)
local schema = self.schema
local ws_id = workspace_id(schema, options)
Expand All @@ -178,6 +490,16 @@ local function page(self, size, offset, options)
offset = token
end

-- Used by /:entity?tags=:tag endpoint
if options and options.tags then
return page_for_tags(self, size, offset, options)
end

-- Used by /:entity?tags=:tag endpoint
if options and options.tags then
return page_for_tags(self, size, offset, options)
end

return page_for_prefix(self, prefix, size, offset, options, need_follow(ws_id))
end

Expand Down Expand Up @@ -253,6 +575,8 @@ do
_mt.upsert_by_field = unsupported_by("create or update")
_mt.delete_by_field = unsupported_by("remove")
_mt.truncate = function() return true end

_mt.page_for_tags = page_for_tags
end


Expand Down
Loading

0 comments on commit dff5c0d

Please sign in to comment.