221 lines
No EOL
6.3 KiB
Elixir
221 lines
No EOL
6.3 KiB
Elixir
defmodule MebeEngine.DB do
|
|
require Logger
|
|
alias MebeWeb.Utils
|
|
|
|
alias Calendar.DateTime
|
|
|
|
@moduledoc """
|
|
Stuff related to storing the blog data to memory (ETS).
|
|
"""
|
|
|
|
# Table for meta information, like the counts of posts and names
|
|
# of authors
|
|
@meta_table :mebeweb_meta
|
|
|
|
# Table for storing pages by slug
|
|
@page_table :mebeweb_pages
|
|
|
|
# Table for sequential retrieval of posts (for list pages)
|
|
@post_table :mebeweb_posts
|
|
|
|
# Table for quick retrieval of single post (with key)
|
|
@single_post_table :mebeweb_single_posts
|
|
|
|
# Table for storing posts with tag as first element of key
|
|
@tag_table :mebeweb_tags
|
|
|
|
# Table for storing posts by specific authors
|
|
@author_table :mebeweb_authors
|
|
|
|
# Table for storing menu data
|
|
@menu_table :mebeweb_menu
|
|
|
|
def init() do
|
|
# Only create tables if they don't exist already
|
|
if (:ets.info @meta_table) == :undefined do
|
|
:ets.new @meta_table, [:named_table, :set, :protected, read_concurrency: true]
|
|
:ets.new @page_table, [:named_table, :set, :protected, read_concurrency: true]
|
|
:ets.new @post_table, [:named_table, :ordered_set, :protected, read_concurrency: true]
|
|
:ets.new @single_post_table, [:named_table, :set, :protected, read_concurrency: true]
|
|
:ets.new @tag_table, [:named_table, :ordered_set, :protected, read_concurrency: true]
|
|
:ets.new @menu_table, [:named_table, :ordered_set, :protected, read_concurrency: true]
|
|
|
|
if Utils.get_conf(:multi_author_mode) do
|
|
:ets.new @author_table, [:named_table, :ordered_set, :protected, read_concurrency: true]
|
|
end
|
|
end
|
|
end
|
|
|
|
def destroy() do
|
|
:ets.delete_all_objects @meta_table
|
|
:ets.delete_all_objects @page_table
|
|
:ets.delete_all_objects @post_table
|
|
:ets.delete_all_objects @single_post_table
|
|
:ets.delete_all_objects @tag_table
|
|
end
|
|
|
|
def insert_count(:all, count) do
|
|
insert_meta(:all, :all, count)
|
|
end
|
|
|
|
def insert_count(type, key, count) do
|
|
insert_meta(type, key, count)
|
|
end
|
|
|
|
def insert_menu(menu) do
|
|
# Format for ETS because it needs a tuple
|
|
menu = Enum.map menu, fn menuitem -> {menuitem.slug, menuitem} end
|
|
:ets.insert @menu_table, menu
|
|
end
|
|
|
|
def insert_posts(posts) do
|
|
ordered_posts = Enum.map posts, fn post ->
|
|
{{year, month, day}, _} = DateTime.to_erl(post.datetime)
|
|
{{year, month, day, post.order}, post}
|
|
end
|
|
|
|
single_posts = Enum.map posts, fn post ->
|
|
{{year, month, day}, _} = DateTime.to_erl(post.datetime)
|
|
{{year, month, day, post.slug}, post}
|
|
end
|
|
|
|
:ets.insert @post_table, ordered_posts
|
|
:ets.insert @single_post_table, single_posts
|
|
|
|
if Utils.get_conf(:multi_author_mode) do
|
|
author_posts = Enum.filter(posts, fn post -> Map.has_key?(post.extra_headers, "author") end)
|
|
|> Enum.map(fn post ->
|
|
{{year, month, day}, _} = DateTime.to_erl(post.datetime)
|
|
|
|
author_slug = Utils.get_author(post) |> Utils.slugify
|
|
{{author_slug, year, month, day, post.order}, post}
|
|
end)
|
|
|
|
:ets.insert @author_table, author_posts
|
|
end
|
|
end
|
|
|
|
def insert_page(page) do
|
|
:ets.insert @page_table, {page.slug, page}
|
|
end
|
|
|
|
def insert_tag_posts(tags) do
|
|
tag_posts = Enum.reduce(Map.keys(tags), [], fn tag, acc ->
|
|
Enum.reduce(tags[tag], acc, fn post, inner_acc ->
|
|
{{year, month, day}, _} = DateTime.to_erl(post.datetime)
|
|
[{{tag, year, month, day, post.order}, post} | inner_acc]
|
|
end)
|
|
end)
|
|
|
|
:ets.insert @tag_table, tag_posts
|
|
end
|
|
|
|
def insert_author_posts(authors) do
|
|
author_posts = Enum.reduce(Map.keys(authors), [], fn author_slug, acc ->
|
|
Enum.reduce(authors[author_slug], acc, fn post, inner_acc ->
|
|
{{year, month, day}, _} = DateTime.to_erl(post.datetime)
|
|
[{{author_slug, year, month, day, post.order}, post} | inner_acc]
|
|
end)
|
|
end)
|
|
|
|
:ets.insert @author_table, author_posts
|
|
end
|
|
|
|
def insert_author_names(author_names_map) do
|
|
author_names = Enum.reduce(Map.keys(author_names_map), [], fn author_slug, acc ->
|
|
[{{:author_name, author_slug}, author_names_map[author_slug]} | acc]
|
|
end)
|
|
|
|
:ets.insert @meta_table, author_names
|
|
end
|
|
|
|
|
|
def get_menu() do
|
|
case :ets.match @menu_table, :"$1" do
|
|
[] -> []
|
|
results -> format_menu results
|
|
end
|
|
end
|
|
|
|
def get_reg_posts(first, last) do
|
|
get_post_list(@post_table, [{:"$1", [], [:"$_"]}], first, last)
|
|
end
|
|
|
|
def get_tag_posts(tag, first, last) do
|
|
get_post_list(@tag_table, [{{{tag, :_, :_, :_, :_}, :"$1"}, [], [:"$_"]}], first, last)
|
|
end
|
|
|
|
def get_author_posts(author_slug, first, last) do
|
|
get_post_list(@author_table, [{{{author_slug, :_, :_, :_, :_}, :"$1"}, [], [:"$_"]}], first, last)
|
|
end
|
|
|
|
def get_year_posts(year, first, last) do
|
|
get_post_list(@post_table, [{{{year, :_, :_, :_}, :"$1"}, [], [:"$_"]}], first, last)
|
|
end
|
|
|
|
def get_month_posts(year, month, first, last) do
|
|
get_post_list(@post_table, [{{{year, month, :_, :_}, :"$1"}, [], [:"$_"]}], first, last)
|
|
end
|
|
|
|
def get_page(slug) do
|
|
case :ets.match_object @page_table, {slug, :"$1"} do
|
|
[{_, page}] -> page
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
def get_post(year, month, day, slug) do
|
|
case :ets.match_object @single_post_table, {{year, month, day, slug}, :"$1"} do
|
|
[{_, post}] -> post
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
def get_count(:all) do
|
|
get_count(:all, :all)
|
|
end
|
|
|
|
def get_count(type, key) do
|
|
get_meta(type, key, 0)
|
|
end
|
|
|
|
def get_author_name(author_slug) do
|
|
get_meta(:author_name, author_slug, author_slug)
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
defp insert_meta(type, key, value) do
|
|
:ets.insert @meta_table, {{type, key}, value}
|
|
end
|
|
|
|
defp get_meta(type, key, default) do
|
|
case :ets.match_object @meta_table, {{type, key}, :"$1"} do
|
|
[{{_, _}, value}] -> value
|
|
[] -> default
|
|
end
|
|
end
|
|
|
|
# Combine error handling of different post listing functions
|
|
defp get_post_list(table, matchspec, first, last) do
|
|
case :ets.select_reverse table, matchspec, first + last do
|
|
:"$end_of_table" -> []
|
|
|
|
{result, _} ->
|
|
{_, result} = Enum.split result, first
|
|
ets_to_data result
|
|
end
|
|
end
|
|
|
|
# Remove key from data returned from ETS
|
|
defp ets_to_data(data) do
|
|
Enum.map data, fn {_, actual} -> actual end
|
|
end
|
|
|
|
# Format menu results (convert [{slug, %MenuItem{}}] to %MenuItem{})
|
|
defp format_menu(results) do
|
|
for [{_, result}] <- results, do: result
|
|
end
|
|
end |