Qureying for Frontmatter

I have an odd use-case where I’d like my frontmatter to be both useful (ie query-able) and somewhat pretty, so I devised this to do so. if Silverbullet has a better (built-in?) method I"m all ears!

function needsQuotes(str)
  str = tostring(str)
  -- Check if string needs quotes for YAML 
  -- Leading/trailing whitespace  
  if str:match("^%s") or str:match("%s$") then return true end 
  -- Special YAML chars
  if str:match("[:#{}%[%]&*!|>'\"%@`]") then return true end
  -- Special starting chars
  if str:match("^[%-?:,]") then return true end  
  -- Numbers
  if str:match("^%d+$") or str:match("^%d+%.%d+$") then return true end  
  -- Booleans/null
  if str:lower():match("^(true|false|yes|no|null)$") then return true end  
  return false
end

function tableToYAML(tbl, indent)
  indent = indent or 0
  local result = {}
  local spaces = string.rep("  ", indent)

  for k, v in pairs(tbl) do
    if type(v) == "table" then
      if #v > 0 and type(v[1]) ~= "table" then
        table.insert(result, spaces .. k .. ":")
        for _, item in ipairs(v) do
          local itemStr = tostring(item)
          if needsQuotes(itemStr) then
            table.insert(result, spaces .. '  - "' .. itemStr:gsub('"', '\\"') .. '"')
          else
            table.insert(result, spaces .. "  - " .. itemStr)
          end
        end
      elseif #v > 0 then
        table.insert(result, spaces .. k .. ":")
        for _, item in ipairs(v) do
          table.insert(result, spaces .. "  -")
          table.insert(result, tableToYAML(item, indent + 2))
        end
      else
        table.insert(result, spaces .. k .. ":")
        table.insert(result, tableToYAML(v, indent + 1))
      end
    else
      local valStr = tostring(v)
      if needsQuotes(valStr) then
        table.insert(result, spaces .. k .. ': "' .. valStr:gsub('"', '\\"') .. '"')
      else
        table.insert(result, spaces .. k .. ": " .. valStr)
      end
    end
  end

  return table.concat(result, "\n")
end

function getAttribute(attribute)
  results = query[[
    from index.tag "page" 
    where name == (editor.getCurrentPage())
  ]]

  local value = results[1]
  for key in attribute:gmatch("[^.]+") do
    if value == nil then return "" end
    value = value[key]
  end

  if value == nil then return "" end
  if type(value) ~= "table" then return tostring(value) end
  return tableToYAML(value)
end

function getAttributeFromPage(attribute, pageName)
  results = query[[
    from index.tag "page" 
    where name == pageName
  ]]

  local value = results[1]
  for key in attribute:gmatch("[^.]+") do
    if value == nil then return "" end
    value = value[key]
  end

  if value == nil then return "" end
  if type(value) ~= "table" then return tostring(value) end
  return tableToYAML(value)
end

This allows querying frontmatter easily on both the current page and on other pages, using ${getAttribute("attribute", "/Page/Name"} and ${getAttribute("attribute"}.

I’ve also found it useful for “Shared Frontmatter”, in that I have multiple categories of a template that “builds” on the frontmatter from other sources.

Below is an example template:

---
forPrefix: "Person/Personal Contact"
command: "Template: Person: Personal Contact"
suggestedName: "Person/FirstName MI LastName"
confirmName: false
openIfExists: true
tags: meta/template/page
---
---
${getAttributeFromPage("person.default", "Library/Personal/Page Templates/Shared Frontmatter")}
${getAttributeFromPage("person.personal-contact", "Library/Personal/Page Templates/Shared Frontmatter")}
tags: person, person/personal-contact

---

 # ${"$"}{getAttribute("pageDecoration.prefix")} ${"$"}{getAttribute("contact.name.first")} ${"$"}{getAttribute("contact.name.middle")} ${"$"}{getAttribute("contact.name.last")}
  * ⛓️‍💥 Relation:  ${"$"}{getAttribute("contact.relation")}
  * 🏚️ Address: ${"$"}{getAttribute("personal.address")}
  * ☎️ Phone:  ${"$"}{getAttribute("personal.phone-number")}
  * 📧 E-Mail:  ${"$"}{getAttribute("personal.email")}
  * 🏢 Employment:  ${"$"}{getAttribute("personal.employment")}

This pulls in frontmatter for both my “contact” (the default template) and “personal-contact” frontmatter, in /Library/Personal/Page Templates/Shared Frontmatter:

---
person:
  default:
    pageDecoration: 
      prefix: "👤 "
      disableTOC: true
      cssClasses: 
        - person-decoration
    contact:
      name:
        first:  FirstName
        middle: MI
        last: LastName
      relation: How do you know this person?
  personal-contact:
    personal:
      status: true
      address: "[[Location/Address]]"
      email: [email protected]
      employment:  "[[Location/Company/Name]]"
      phone-number: 1-123-123-1234
---

The resulting page would look like this:

Complete with clickable links to their address and place of employment :slight_smile:

I’m sharing because others may find it useful.

Thanks!

See silverbullet-libraries/OrganizationChart.md at main · malys/silverbullet-libraries · GitHub

Organizationxxx.md generates from person.md front matter organization schema and VCF files. It could be interesting for you