author: Chenzhu-Xie name: Library/xczphysics/CONFIG/Picker/Tags tags: meta/library

pageDecoration.prefix: "🔖 "

Practical tag search handles multi-tag intersections, which is both the advantage and the drawbacks (not one-to-one) of tags (Webs) over file systems (Trees).

One item can have many tags, and one tag can match many items.

Both are Webs, yet Tag is Star-like Set-theory, good at Filtering, Intersection, Classification Wiki is Mesh-like Graph-theory, good at Navigation, Association,

Multiple Tags

  1. 实用的 标签检索 应 自带多选 找交集 https://marijnhaverbeke.nl/blog #💡 而不是 只 pick 1 tag(像下面的 tag picker)或 QUERY/Tags/Tag-Page_Navigator|找并集

CMD: Tags Picker 2

关于去重: - 下面的代码是:收集完标签后,再一次性去重的 - 可以收集一次、去重一次,这样 效率会更高些(储存、处理得均更少) - 像 Library/xczphysics/CONFIG/Mouse/History_+_Center#Logic behind|Click HistoryLibrary/xczphysics/CONFIG/Picker/Table#Implementation 3|Table Picker 一样 - 当然,我们更需要的是 Ah_LMYqd2CE #youtube SQL 中的 DUPLICATE 关键字 - SELECT DUPLICATE ssn FROM payments; - 发现 luaQuerry 有个类似的关键字distinct query_collection.ts #github - 通过 js silverbullet_c5736e4c 5f8d 4885 b3df 4767775ae5fb #deepwiki 中找寻 怎么在 js 中调用 SB 的 editor. API #项目经验

-- priority: 11
command.define {
  name = "Navigate: Tags Picker",
  key = "Ctrl-Alt-T",
  run = function()
    local selectedNames = {}
    
    while true do
      local potentialTags = {}
      
      if #selectedNames == 0 then
        potentialTags = query[from index.tag "tag" select {name = _.name}](from index.tag "tag" select {name = _.name})
      else
        
        local q = query[[from index.tag(selectedNames[1])]]
        
        for i = 2, #selectedNames do
          q = query[[
            from q where table.includes(_.tags, selectedNames[i])
          ]]
        end
        
        local tagSet = {}
        for _, obj in ipairs(q) do
          if obj.tags and type(obj.tags) == "table" then
            for _, t in ipairs(obj.tags) do
              tagSet[t] = true
            end
          end
        end
        
        -- convert Set to list for Picker
        for tagName, _ in pairs(tagSet) do
          table.insert(potentialTags, {name = tagName})
        end
        
        table.sort(potentialTags, function(a, b) return a.name < b.name end)
      end

      local availableOptions = {}
      for _, tagObj in ipairs(potentialTags) do
        if not table.includes(selectedNames, tagObj.name) then
          table.insert(availableOptions, tagObj)
        end
      end

      if #availableOptions == 0 then
        break
      end

      local description = "Select a Tag"
      local placeholder = "🔖 a Tag"
      if #selectedNames > 0 then
        description = "Selected Tags ⏺️:" .. table.concat(selectedNames, ", ") .. " ➕ (ESC to Go)"
        placeholder = string.rep("🔖", #selectedNames) .. " Filter next tag..."
      end

      local selection = editor.filterBox("🤏 Pick", availableOptions, description, placeholder)
      
      if selection then
        table.insert(selectedNames, selection.name)
      else
        if #selectedNames == 0 then
          return
        else
          break
        end
      end
    end

    local targetPage = "tags:" .. table.concat(selectedNames, ",")
    editor.navigate(targetPage)
  end
}

CMD: Tags Picker 1

-- priority: 11
command.define {
  name = "Navigate: Tags Picker",
  key = "Ctrl-Alt-T",
  run = function()
    local allTags = query[from index.tag "tag" select {name = _.name}](from index.tag "tag" select {name = _.name})
    local selectedNames = {}
    while true do
      local availableOptions = {}
      for _, tagObj in ipairs(allTags) do
        if not table.includes(selectedNames, tagObj.name) then
          table.insert(availableOptions, tagObj)
        end
      end
      if #availableOptions == 0 then
        break
      end
      local description = "Select a Tag"
      local placeholder = "🔖 a Tag"
      if #selectedNames > 0 then
        description = "Selected Tags ⏺️:" .. table.concat(selectedNames, ", ") .. " ➕ (ESC to Go)"
        placeholder = string.rep("🔖", #selectedNames) .. " a Tag"
      end
      local selection = editor.filterBox("🤏 Pick", availableOptions, description, placeholder)
      if selection then
        table.insert(selectedNames, selection.name)
      else
        if #selectedNames == 0 then
          return
        else
          break
        end
      end
    end
    local targetPage = "tags:" .. table.concat(selectedNames, ",")
    editor.navigate(targetPage)
  end
}

Virtual Page

-- priority: 11
virtualPage.define {
  pattern = "tags:(.+)",
  run = function(inputString)
    local rawTags = inputString:split(",")
    local Tags = {}
    for _, t in ipairs(rawTags) do
      local cleanTag = t:trim()
      if cleanTag ~= "" then
        table.insert(Tags, cleanTag)
      end
    end

    if #Tags == 0 then return "No tags specified." end

    local text = ""
    local tagName = Tags[1]
    local allObjects = query[
      from index.tag(tagName)
      order by ref
    ](
      from index.tag(tagName)
      order by ref
    )
    
    if #Tags == 1 then
      text = "# Objects tagged with: " .. tagName .. "\n"
      local tagParts = tagName:split("/")
      local parentTags = {}
      for i in ipairs(tagParts) do
        local slice = table.pack(table.unpack(tagParts, 1, i))
        if i ~= #tagParts then
          table.insert(parentTags, {name=table.concat(slice, "/")})
        end
      end
      if #parentTags > 0 then
        text = text .. "## Parent tags\n"
          .. template.each(parentTags, templates.tagItem)
      end
      local subTags = query[
        from index.tag "tag"
        where string.startsWith(_.name, tagName .. "/")
        select {name=_.name}
      ](
        from index.tag "tag"
        where string.startsWith(_.name, tagName .. "/")
        select {name=_.name}
      )
      if #subTags > 0 then
        text = text .. "## Child tags\n"
          .. template.each(subTags, templates.tagItem)
      end
    else
      text = "# Objects tagged with: " .. table.concat(Tags, ", ") .. "\n"
      for i = 2, #Tags do
        allObjects = query[[
          from allObjects
          where table.includes(_.tags, Tags[i])
        ]]
      end
    end
    
    local taggedPages = {}
    local taggedTasks = {}
    local taggedItems = {}
    local taggedData = {}
    local taggedParagraphs = {}

    -- improve performance 
    for _, obj in ipairs(allObjects) do
      if obj.itags and table.includes(obj.itags, "page") then
        table.insert(taggedPages, obj)
      end
      if obj.itags and table.includes(obj.itags, "task") then
        table.insert(taggedTasks, obj)
      end
      if obj.itags and table.includes(obj.itags, "item") then
        table.insert(taggedItems, obj)
      end
      if obj.itags and table.includes(obj.itags, "data") then
        table.insert(taggedData, obj)
      end
      if obj.itags and table.includes(obj.itags, "paragraph") then
        table.insert(taggedParagraphs, obj)
      end
    end

    if #taggedPages > 0 then
      text = text .. "## Pages\n"
        .. template.each(taggedPages, templates.pageItem)
    end
    
    if #taggedTasks > 0 then
      text = text .. "## Tasks\n"
        .. template.each(taggedTasks, templates.taskItem)
    end
    
    if #taggedItems > 0 then
      text = text .. "## Items\n"
        .. template.each(taggedItems, templates.itemItem)
    end
    
    if #taggedData > 0 then
      text = text .. "## Data\n"
        .. markdown.objectsToTable(taggedData) .. "\n"
    end
    
    if #taggedParagraphs > 0 then
      text = text .. "## Paragraphs\n"
        .. template.each(taggedParagraphs, templates.paragraphItem)
    end

    return text
  end
}

Single Tag

  1. https://community.silverbullet.md/t/quickly-search-open-tag-virtual-page/1104/2?u=chenzhu-xie

${query[from index.tag "tag" select {name = _.name}](from index.tag "tag" select {name = _.name})} 中的 name 不含重复元素, 是个 set 集合。

${query[[from index.tag "tag"} 中的 name 含重复的元素。

  1. https://community.silverbullet.md/t/quickly-search-open-tag-virtual-page/1104/15

  2. official one: silverbullet 2 3 released share libraries library manager and repositories #community #silverbullet

-- priority: 11
command.define {
  name = "Navigate: Tag Picker",
  key = "Ctrl-Alt-t",
  run = function()
    local tags = query[from index.tag "tag" select {name = _.name}](from index.tag "tag" select {name = _.name})
    local sel = editor.filterBox("🤏 Pick", tags, "Select a Tag", "🔖 a Tag")
    if sel then editor.navigate("tag:" .. sel.name) end
  end
}

Community Version

  1. https://community.silverbullet.md/t/quickly-search-open-tag-virtual-page/1104/14?u=chenzhu-xie
command.define {  
  name = "Search All",
  key = "Ctrl-Shift-t",
  run = function()  
    -- Query all object types  
    local allHeaders = query[from index.tag "header" ](from index.tag "header" )
    local allPages = query[from index.tag "page" ](from index.tag "page" )
    local allItems = query[from index.tag "item" ](from index.tag "item" )
    local allParagraph = query[from index.tag "paragraph" ](from index.tag "paragraph" )
      
    -- Combine all results  
    local all = {}  
    for _, item in ipairs(allHeaders) do  
      table.insert(all, item)  
    end  
    for _, item in ipairs(allPages) do  
      table.insert(all, item)  
    end  
    for _, item in ipairs(allItems) do  
      table.insert(all, item)  
    end  
    for _, item in ipairs(allParagraph) do  
      table.insert(all, item)  
    end  
      
    -- Create filter options  
    local options = {}  
    for _, item in ipairs(all) do  
      table.insert(options, {  
        name = item.text or item.name or item.page,  
        description = item.page,  
        page = item.ref  
      })  
    end  
      
    -- Show filter box  
    local selected = editor.filterBox(  
      "Full Text Search",  
      options,  
      "Select the search"  
    )  
      
    if selected then  
      -- Find the original data item  
      local data = nil  
      for _, item in ipairs(all) do  
        if (item.text or item.name or item.page) == selected.name then  
          data = item  
          break  
        end  
      end  
        
      if data then  
        editor.navigate(data.ref)  
      end  
    end  
  end  
}