bonfire-app/database.html
2024-04-16 21:21:08 +00:00

508 lines
66 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="ExDoc v0.31.2">
<meta name="project" content="bonfire_umbrella v0.9.10-cooperation-beta.62">
<title>Bonfire's Database - an intro — bonfire_umbrella v0.9.10-cooperation-beta.62</title>
<link rel="stylesheet" href="dist/html-elixir-JKHCEBPC.css" />
<script src="dist/handlebars.runtime-NWIB6V2M.js"></script>
<script src="dist/handlebars.templates-A7S2WMC7.js"></script>
<script src="dist/sidebar_items-0AD831F9.js"></script>
<script src="docs_config.js"></script>
<script async src="dist/html-JRPQ5PR6.js"></script>
</head>
<body data-type="extras" class="page-extra">
<script>
try {
var settings = JSON.parse(localStorage.getItem('ex_doc:settings') || '{}');
if (settings.theme === 'dark' ||
((settings.theme === 'system' || settings.theme == null) &&
window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.body.classList.add('dark')
}
} catch (error) { }
</script>
<div class="main">
<button id="sidebar-menu" class="sidebar-button sidebar-toggle" aria-label="toggle sidebar" aria-controls="sidebar">
<i class="ri-menu-line ri-lg" title="Collapse/expand sidebar"></i>
</button>
<div class="background-layer"></div>
<nav id="sidebar" class="sidebar">
<div class="sidebar-header">
<div class="sidebar-projectInfo">
<a href="https://bonfirenetworks.org" class="sidebar-projectImage">
<img src="assets/logo.png" alt="bonfire_umbrella" />
</a>
<div>
<a href="https://bonfirenetworks.org" class="sidebar-projectName" translate="no">
bonfire_umbrella
</a>
<div class="sidebar-projectVersion" translate="no">
v0.9.10-cooperation-beta.62
</div>
</div>
</div>
<ul id="sidebar-listNav" class="sidebar-listNav" role="tablist">
<li>
<button id="extras-list-tab-button" role="tab" data-type="extras" aria-controls="extras-tab-panel" aria-selected="true" tabindex="0">
Pages
</button>
</li>
<li>
<button id="modules-list-tab-button" role="tab" data-type="modules" aria-controls="modules-tab-panel" aria-selected="false" tabindex="-1">
Modules
</button>
</li>
</ul>
</div>
<div id="extras-tab-panel" class="sidebar-tabpanel" role="tabpanel" aria-labelledby="extras-list-tab-button">
<ul id="extras-full-list" class="full-list"></ul>
</div>
<div id="modules-tab-panel" class="sidebar-tabpanel" role="tabpanel" aria-labelledby="modules-list-tab-button" hidden>
<ul id="modules-full-list" class="full-list"></ul>
</div>
</nav>
<main class="content">
<output role="status" id="toast"></output>
<div class="content-outer">
<div id="content" class="content-inner">
<div class="top-search">
<div class="search-settings">
<form class="search-bar" action="search.html">
<label class="search-label">
<span class="sr-only">Search documentation of bonfire_umbrella</span>
<input name="q" type="text" class="search-input" placeholder="Press / to search" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" />
</label>
<button type="submit" class="search-button" aria-label="Submit Search">
<i class="ri-search-2-line ri-lg" aria-hidden="true" title="Submit search"></i>
</button>
<button type="button" tabindex="-1" class="search-close-button" aria-hidden="true">
<i class="ri-close-line ri-lg" title="Cancel search"></i>
</button>
</form>
<div class="autocomplete">
</div>
<button class="icon-settings display-settings">
<i class="ri-settings-3-line"></i>
<span class="sr-only">Settings</span>
</button>
</div>
</div>
<h1>
<a href="https://github.com/bonfire-networks/bonfire-app/blob/main/docs/DATABASE.md#L1" title="View Source" class="icon-action" rel="help">
<i class="ri-code-s-slash-line" aria-hidden="true"></i>
<span class="sr-only">View Source</span>
</a>
<span>Bonfire's Database - an intro</span>
</h1>
<p>Bonfire uses the excellent PostgreSQL database for most data storage. PostgreSQL allows us to make a wide range of queries and to make them relatively fast while upholding data integrity guarantees.</p><p>Postgres is a relational schema-led database - it expects you to pre-define tables and the fields in each table (represented in tabular form, i.e. as a collection of tables with each table consisting of a set of rows and columns). Fields can contain data or a reference to a row in another table. </p><p>This usually means that a field containing a reference has to be pre-defined with a foreign key pointing to a specific field (typically a primary key, like an ID column) <em>in a specific table</em>. </p><p>A simple example would be a blogging app, which might have a <code class="inline">post</code> table with <code class="inline">author</code> field that references the <code class="inline">user</code> table.</p><p>A social network, by contrast, is actually a graph of objects. Objects need to be able to refer to other objects by their ID without knowing their type. </p><p>A simple example would be likes, you might have a <code class="inline">likes</code> table with <code class="inline">liked_post_id</code> field that references the <code class="inline">post</code> table. But you don't just have posts that can be liked, but also videos, images, polls, etc, each with their own table, but probably do not want to have to add <code class="inline">liked_video_id</code>, <code class="inline">liked_image_id</code>, etc?</p><p>We needed the flexibility to have a foreign key that can reference any referenceable object. We call our system <a href="https://hexdocs.pm/needle/0.7.2/Needle.html"><code class="inline">Needle</code></a>.</p><p>This guide is a brief introduction to Needle. It assumes some foundational knowledge:</p><ul><li><p>Basic understanding of how relational databases like Postgresql work, in particular:</p><ul><li>Tables being made up of fields.</li><li>What a primary key is and why it's useful.</li><li>Foreign keys and relationships between tables (1 to 1, 1 to Many, Many to 1, Many to Many).</li><li>Views as virtual tables backed by a SQL query.</li></ul></li><li><p>Basic understanding of Elixir (enough to follow the examples).</p></li><li><p>Basic working knowledge of the <a href="https://hexdocs.pm/ecto/Ecto.html">Ecto</a> database library (schema and migration definitions)</p></li></ul><h2 id="identifying-objects-the-ulid-type" class="section-heading">
<a href="#identifying-objects-the-ulid-type" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Identifying objects - the ULID type</span>
</h2>
<p>All referenceable objects in the system have a unique ID (primary key) whose type is the <a href="https://github.com/ulid/spec"><code class="inline">ULID</code></a>. It's a lot like a <code class="inline">UUID</code> in that you can generate unique ones independently of the database. It's also a little different, being made up of two parts:</p><ul><li>The current timestamp, to millisecond precision.</li><li>Strong random padding for uniqueness.</li></ul><p>This means that it naturally sorts by time to the millisecond (close enough for us), giving us a performance advantage on queries ordered by a separate creation datetime field (by contrast, UUIDv4 is randomly distributed).</p><p>If you've only worked with integer primary keys before, you are probably used to letting the database dispense an ID for you. With <code class="inline">ULID</code> (or <code class="inline">UUID</code>), IDs can be known <em>before</em> they are stored, greatly easing the process of storing a graph of data and allowing us to do more of the preparation work outside of a transaction for increased performance.</p><p>In PostgreSQL, we actually store <code class="inline">ULID</code>s as <code class="inline">UUID</code> columns, thanks to both being the same size (and the lack of a <code class="inline">ULID</code> column type shipping with postgresql). You mostly will not notice this because it's handled for you, but there are a few places it can come up:</p><ul><li>Ecto debug and error output may show either binary values or UUID-formatted values.</li><li>Hand-written SQL may need to convert table IDs to the <code class="inline">UUID</code> format before use.</li></ul><h2 id="it-s-just-a-table" class="section-heading">
<a href="#it-s-just-a-table" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">It's just a table</span>
</h2>
<p>The <a href="https://hexdocs.pm/needle/0.7.2/Needle.html"><code class="inline">Needle</code></a> system is mostly based around a single table represented by the <a href="https://hexdocs.pm/needle/0.7.2/Needle.Pointer.html"><code class="inline">Needle.Pointer</code></a> schema with the following fields:</p><ul><li><code class="inline">id</code> (ULID) - the database-wide unique id for the object, primary key.</li><li><code class="inline">table_id</code> (ULID) - identifies the type of the object, references <a href="https://hexdocs.pm/needle/0.7.2/Needle.Table.html"><code class="inline">Needle.Table</code></a>.</li><li><code class="inline">deleted_at</code> (timestamp, default: <code class="inline">null</code>) - when the object was deleted.</li></ul><p>Every object that is stored in the system will have a record in this table. It may also have records in other tables (handy for storing more than 3 fields about the object!).</p><p>Don't worry about <a href="https://hexdocs.pm/needle/0.7.2/Needle.Table.html"><code class="inline">Needle.Table</code></a> for now, just know that every object type will have a record there so <code class="inline">Needle.Pointer.table_id</code> can reference it.</p><h2 id="mixins-storing-data-about-objects" class="section-heading">
<a href="#mixins-storing-data-about-objects" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Mixins - storing data about objects</span>
</h2>
<p>Mixins are tables which contain extra information on behalf of objects. Each object can choose to
record or not record information for each mixin. Sample mixins include:</p><ul><li>user profile (containing a name, location and summary)</li><li>post content (containing the title, summary, and/or html body of a post or message)</li><li>created (containing the id of the object creator)</li></ul><p>In this way, they are reusable across different object types. One mixin may (or may not) be used by any number of objects. This is mostly driven by the type of the object we are storing, but can also be driven by user input.</p><p>Mixins are just tables too! The only requirement is they have a <code class="inline">ULID</code> primary key which references <a href="https://hexdocs.pm/needle/0.7.2/Needle.Pointer.html"><code class="inline">Needle.Pointer</code></a>. The developer of the mixin is free to put whatever other fields they want in the table, so long as they have that primary-key-as-reference (which will be automatically added for you by the <code class="inline">mixin_schema</code> macro). </p><p>Here is a sample mixin definition for a user profile:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Profile</span><span class="w"> </span><span class="k" data-group-id="8797683516-1">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Needle.Mixin</span><span class="p">,</span><span class="w">
</span><span class="ss">otp_app</span><span class="p">:</span><span class="w"> </span><span class="ss">:bonfire_data_social</span><span class="p">,</span><span class="w">
</span><span class="ss">source</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;bonfire_data_social_profile&quot;</span><span class="w">
</span><span class="n">mixin_schema</span><span class="w"> </span><span class="k" data-group-id="8797683516-2">do</span><span class="w">
</span><span class="n">field</span><span class="w"> </span><span class="ss">:name</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="w">
</span><span class="n">field</span><span class="w"> </span><span class="ss">:summary</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="w">
</span><span class="n">field</span><span class="w"> </span><span class="ss">:website</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="w">
</span><span class="n">field</span><span class="w"> </span><span class="ss">:location</span><span class="p">,</span><span class="w"> </span><span class="ss">:string</span><span class="w">
</span><span class="k" data-group-id="8797683516-2">end</span><span class="w">
</span><span class="k" data-group-id="8797683516-1">end</span></code></pre><p>Aside from <code class="inline">use</code>ing <a href="https://hexdocs.pm/needle/0.7.2/Needle.Mixin.html"><code class="inline">Needle.Mixin</code></a> instead of <a href="https://hexdocs.pm/ecto/3.11.2/Ecto.Schema.html"><code class="inline">Ecto.Schema</code></a> and calling <code class="inline">mixin_schema</code> instead of
<code class="inline">schema</code>, pretty similar to a standard Ecto schema, right? </p><p>The arguments to <code class="inline">use Needle.Mixin</code> are:</p><ul><li><code class="inline">otp_app</code>: the OTP app name to use when loading dynamic configuration, e.g. the current extension or app (required)</li><li><code class="inline">source</code>: the underlying table name to use in the database</li></ul><p>We will cover dynamic configuration later. For now, you can use the OTP app that includes the module.</p><h2 id="multimixins" class="section-heading">
<a href="#multimixins" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Multimixins</span>
</h2>
<p>Multimixins are like mixins, except that where an object may have 0 or 1 of a particular mixins, an object may have any number of a particular multimixin.</p><p>For this to work, a multimixin must have a <em>compound primary key</em> which must contain an <code class="inline">id</code> column referencing <a href="https://hexdocs.pm/needle/0.7.2/Needle.Pointer.html"><code class="inline">Needle.Pointer</code></a> and at least one other field which will collectively be unique.</p><p>An example multimixin is used for publishing an item to feeds:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.FeedPublish</span><span class="w"> </span><span class="k" data-group-id="1705459229-1">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Needle.Mixin</span><span class="p">,</span><span class="w">
</span><span class="ss">otp_app</span><span class="p">:</span><span class="w"> </span><span class="ss">:bonfire_data_social</span><span class="p">,</span><span class="w">
</span><span class="ss">source</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;bonfire_data_social_feed_publish&quot;</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Needle.Pointer</span><span class="w">
</span><span class="n">mixin_schema</span><span class="w"> </span><span class="k" data-group-id="1705459229-2">do</span><span class="w">
</span><span class="n">belongs_to</span><span class="w"> </span><span class="ss">:feed</span><span class="p">,</span><span class="w"> </span><span class="nc">Pointer</span><span class="p">,</span><span class="w"> </span><span class="ss">primary_key</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="k" data-group-id="1705459229-2">end</span><span class="w">
</span><span class="k" data-group-id="1705459229-1">end</span></code></pre><p>Notice that this looks very similar to defining a mixin. Indeed, the only difference is the <code class="inline">primary_key: true</code> in this line, which adds a second field to the compound primary key.
This results in ecto recording a compound primary key of <code class="inline">(id, feed_id)</code> for the schema (the id is added for you as with regular mixins).</p><h2 id="declaring-object-types" class="section-heading">
<a href="#declaring-object-types" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Declaring Object Types</span>
</h2>
<h3 id="picking-a-table-id" class="section-heading">
<a href="#picking-a-table-id" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Picking a table id</span>
</h3>
<p>The first step to declaring a type is picking a unique table ID in ULID format. You could just generate one at the terminal, but since these IDs are special, we tend to assign a synthetic ULID that are readable as words so they stand out in debug output.</p><p>For example, the ID for the <code class="inline">Feed</code> table is: <code class="inline">1TFEEDS0NTHES0V1S0FM0RTA1S</code>, which can be read as &quot;It feeds on the souls of mortals&quot;. Feel free to have a little fun coming up with them, it makes debug output a little more cheery! The rules are:</p><ul><li>The alphabet is <a href="https://en.wikipedia.org/wiki/Base32#Crockford's_Base32">Crockford's Base32</a>.</li><li>They must be 26 characters in length.</li><li>The first character must be a digit in the range 0-7.</li></ul><p>To help you with this, the <a href="https://hexdocs.pm/needle_ulid/0.3.0/Needle.ULID.html#synthesise!/1"><code class="inline">Needle.ULID.synthesise!/1</code></a> method takes an alphanumeric binary and tries to return you it transliterated into a valid ULID. Example usage:</p><pre><code class="makeup elixir" translate="no"><span class="gp unselectable">iex(1)&gt; </span><span class="nc">Needle.ULID</span><span class="o">.</span><span class="n">synthesise!</span><span class="p" data-group-id="6781550288-1">(</span><span class="s">&quot;itfeedsonthesouls&quot;</span><span class="p" data-group-id="6781550288-1">)</span><span class="w">
</span><span class="mi">11</span><span class="p">:</span><span class="mi">20</span><span class="p">:</span><span class="mf">28.299</span><span class="w"> </span><span class="p" data-group-id="6781550288-2">[</span><span class="n">error</span><span class="p" data-group-id="6781550288-2">]</span><span class="w"> </span><span class="nc">Too</span><span class="w"> </span><span class="n">short</span><span class="p">,</span><span class="w"> </span><span class="n">need</span><span class="w"> </span><span class="mi">9</span><span class="w"> </span><span class="n">chars</span><span class="o">.</span><span class="w">
</span><span class="ss">:ok</span><span class="w">
</span><span class="gp unselectable">iex(2)&gt; </span><span class="nc">Needle.ULID</span><span class="o">.</span><span class="n">synthesise!</span><span class="p" data-group-id="6781550288-3">(</span><span class="s">&quot;itfeedsonthesoulsofmortalsandothers&quot;</span><span class="p" data-group-id="6781550288-3">)</span><span class="w">
</span><span class="mi">11</span><span class="p">:</span><span class="mi">20</span><span class="p">:</span><span class="mf">31.819</span><span class="w"> </span><span class="p" data-group-id="6781550288-4">[</span><span class="n">warn</span><span class="p" data-group-id="6781550288-4">]</span><span class="w"> </span><span class="nc">Too</span><span class="w"> </span><span class="n">long</span><span class="p">,</span><span class="w"> </span><span class="n">chopping</span><span class="w"> </span><span class="n">off</span><span class="w"> </span><span class="n">last</span><span class="w"> </span><span class="mi">9</span><span class="w"> </span><span class="n">chars</span><span class="w">
</span><span class="s">&quot;1TFEEDS0NTHES0V1S0FM0RTA1S&quot;</span><span class="w">
</span><span class="gp unselectable">iex(3)&gt; </span><span class="nc">Needle.ULID</span><span class="o">.</span><span class="n">synthesise!</span><span class="p" data-group-id="6781550288-5">(</span><span class="s">&quot;itfeedsonthesoulsofmortals&quot;</span><span class="p" data-group-id="6781550288-5">)</span><span class="w">
</span><span class="s">&quot;1TFEEDS0NTHES0V1S0FM0RTA1S&quot;</span><span class="w">
</span><span class="gp unselectable">iex(4)&gt; </span><span class="nc">Needle.ULID</span><span class="o">.</span><span class="n">synthesise!</span><span class="p" data-group-id="6781550288-6">(</span><span class="s">&quot;gtfeedsonthesoulsofmortals&quot;</span><span class="p" data-group-id="6781550288-6">)</span><span class="w">
</span><span class="mi">11</span><span class="p">:</span><span class="mi">21</span><span class="p">:</span><span class="mf">03.268</span><span class="w"> </span><span class="p" data-group-id="6781550288-7">[</span><span class="n">warn</span><span class="p" data-group-id="6781550288-7">]</span><span class="w"> </span><span class="nc">First</span><span class="w"> </span><span class="n">character</span><span class="w"> </span><span class="n">must</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">digit</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">range</span><span class="w"> </span><span class="mi">0</span><span class="o">-</span><span class="mi">7</span><span class="p">,</span><span class="w"> </span><span class="n">replacing</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="mi">7</span><span class="w">
</span><span class="s">&quot;7TFEEDS0NTHES0V1S0FM0RTA1S&quot;</span></code></pre><h3 id="virtuals" class="section-heading">
<a href="#virtuals" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Virtuals</span>
</h3>
<p>Virtuals are the simplest and most common type of object. Here's a definition of block:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Block</span><span class="w"> </span><span class="k" data-group-id="7516508216-1">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Needle.Virtual</span><span class="p">,</span><span class="w">
</span><span class="ss">otp_app</span><span class="p">:</span><span class="w"> </span><span class="ss">:bonfire_data_social</span><span class="p">,</span><span class="w">
</span><span class="ss">table_id</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;310CK1NGSTVFFAV01DSSEE1NG1&quot;</span><span class="p">,</span><span class="w">
</span><span class="ss">source</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;bonfire_data_social_block&quot;</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Edges.Edge</span><span class="w">
</span><span class="n">virtual_schema</span><span class="w"> </span><span class="k" data-group-id="7516508216-2">do</span><span class="w">
</span><span class="n">has_one</span><span class="w"> </span><span class="ss">:edge</span><span class="p">,</span><span class="w"> </span><span class="nc">Edge</span><span class="p">,</span><span class="w"> </span><span class="ss">foreign_key</span><span class="p">:</span><span class="w"> </span><span class="ss">:id</span><span class="w">
</span><span class="k" data-group-id="7516508216-2">end</span><span class="w">
</span><span class="k" data-group-id="7516508216-1">end</span></code></pre><p>It should look quite similar to a mixin definition, except that we <code class="inline">use</code> <a href="https://hexdocs.pm/needle/0.7.2/Needle.Virtual.html"><code class="inline">Needle.Virtual</code></a> this time (passing an additional <code class="inline">table_id</code> argument) and we call the <code class="inline">virtual_schema</code> macro.</p><p>The primary limitation of a virtual is that you cannot put extra fields into one. This also means that <code class="inline">belongs_to</code> is not generally permitted because it results in adding a field. <code class="inline">has_one</code> and <code class="inline">has_many</code> work just fine as they do not cause the creation of fields in the schema.</p><p>This is not usually a problem, as extra fields can be put into mixins or multimixins as appropriate.</p><p>Under the hood, a virtual has a view (in this example, called <code class="inline">bonfire_data_social_block</code>). It looks like a table with just an id, but it's populated with all the ids of blocks that are not deleted. When the view is inserted into, a record is created in the <code class="inline">pointers</code> table for you transparently. When you delete from the view, the corresponding <code class="inline">pointers</code> entry is marked deleted for you.</p><h3 id="pointables" class="section-heading">
<a href="#pointables" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Pointables</span>
</h3>
<p>The other, lesser used, type of object is called the Pointable. The major difference is that unlike the simple case of virtuals, pointables are not backed by views, but by tables.</p><p>When a record is inserted into a pointable table, a copy is made in the <code class="inline">pointers</code> table for you transparently. When you delete from the table, the the corresponding <code class="inline">pointers</code> entry is marked deleted for you. In these ways, they behave very much like virtuals. By having a table, however, we are free to add new fields.</p><p>Pointables pay for this flexibility by being slightly more expensive than virtuals:</p><ul><li>Records must be inserted into/deleted from two tables (the pointable's table and the <code class="inline">pointers</code> table).</li><li>The pointable table needs its own primary key index.</li></ul><p>Here is a definition of a pointable type (indicating an ActivityPub activity whose type we don't recognise, stored as a JSON blob):</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.APActivity</span><span class="w"> </span><span class="k" data-group-id="3803622675-1">do</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Needle.Pointable</span><span class="p">,</span><span class="w">
</span><span class="ss">otp_app</span><span class="p">:</span><span class="w"> </span><span class="ss">:bonfire_data_social</span><span class="p">,</span><span class="w">
</span><span class="ss">table_id</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;30NF1REAPACTTAB1ENVMBER0NE&quot;</span><span class="p">,</span><span class="w">
</span><span class="ss">source</span><span class="p">:</span><span class="w"> </span><span class="s">&quot;bonfire_data_social_apactivity&quot;</span><span class="w">
</span><span class="n">pointable_schema</span><span class="w"> </span><span class="k" data-group-id="3803622675-2">do</span><span class="w">
</span><span class="n">field</span><span class="w"> </span><span class="ss">:json</span><span class="p">,</span><span class="w"> </span><span class="ss">:map</span><span class="w">
</span><span class="k" data-group-id="3803622675-2">end</span><span class="w">
</span><span class="k" data-group-id="3803622675-1">end</span></code></pre><p>The choice of using a pointable instead of a virtual combined with one or more mixins is ultimately up to you.</p><h2 id="writing-migrations" class="section-heading">
<a href="#writing-migrations" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Writing Migrations</span>
</h2>
<p>Migrations are typically included along with the schemas as public APIs you can call within your project's migrations.</p><h3 id="virtuals-1" class="section-heading">
<a href="#virtuals-1" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Virtuals</span>
</h3>
<p>Most virtuals are incredibly simple to migrate for:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Post.Migration</span><span class="w"> </span><span class="k" data-group-id="6137854443-1">do</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Post</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_post</span><span class="p" data-group-id="6137854443-2">(</span><span class="p" data-group-id="6137854443-2">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">migrate_virtual</span><span class="p" data-group-id="6137854443-3">(</span><span class="nc">Post</span><span class="p" data-group-id="6137854443-3">)</span><span class="w">
</span><span class="k" data-group-id="6137854443-1">end</span></code></pre><p>If you need to do more work, it can be a little trickier. Here's an example for <code class="inline">block</code>, which also creates a unique index on another table:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Block.Migration</span><span class="w"> </span><span class="k" data-group-id="2254210110-1">do</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Bonfire.Data.Edges.Edge.Migration</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Block</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_block_view</span><span class="p" data-group-id="2254210110-2">(</span><span class="p" data-group-id="2254210110-2">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">migrate_virtual</span><span class="p" data-group-id="2254210110-3">(</span><span class="nc">Block</span><span class="p" data-group-id="2254210110-3">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_block_unique_index</span><span class="p" data-group-id="2254210110-4">(</span><span class="p" data-group-id="2254210110-4">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">migrate_type_unique_index</span><span class="p" data-group-id="2254210110-5">(</span><span class="nc">Block</span><span class="p" data-group-id="2254210110-5">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_block</span><span class="p" data-group-id="2254210110-6">(</span><span class="n">dir</span><span class="w"> </span><span class="o">\\</span><span class="w"> </span><span class="n">direction</span><span class="p" data-group-id="2254210110-7">(</span><span class="p" data-group-id="2254210110-7">)</span><span class="p" data-group-id="2254210110-6">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_block</span><span class="p" data-group-id="2254210110-8">(</span><span class="ss">:up</span><span class="p" data-group-id="2254210110-8">)</span><span class="w"> </span><span class="k" data-group-id="2254210110-9">do</span><span class="w">
</span><span class="n">migrate_block_view</span><span class="p" data-group-id="2254210110-10">(</span><span class="p" data-group-id="2254210110-10">)</span><span class="w">
</span><span class="n">migrate_block_unique_index</span><span class="p" data-group-id="2254210110-11">(</span><span class="p" data-group-id="2254210110-11">)</span><span class="w">
</span><span class="k" data-group-id="2254210110-9">end</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_block</span><span class="p" data-group-id="2254210110-12">(</span><span class="ss">:down</span><span class="p" data-group-id="2254210110-12">)</span><span class="w"> </span><span class="k" data-group-id="2254210110-13">do</span><span class="w">
</span><span class="n">migrate_block_unique_index</span><span class="p" data-group-id="2254210110-14">(</span><span class="p" data-group-id="2254210110-14">)</span><span class="w">
</span><span class="n">migrate_block_view</span><span class="p" data-group-id="2254210110-15">(</span><span class="p" data-group-id="2254210110-15">)</span><span class="w">
</span><span class="k" data-group-id="2254210110-13">end</span><span class="w">
</span><span class="k" data-group-id="2254210110-1">end</span></code></pre><p>Notice how we have to write our <code class="inline">up</code> and <code class="inline">down</code> versions separately to get the correct ordering of operations. </p><h3 id="pointables-1" class="section-heading">
<a href="#pointables-1" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Pointables</span>
</h3>
<p>As of now, pointables are a little trickier to define flexibly than virtuals because we want to
preserve the ability for the user to define extra fields in config. There are some questions about
how useful this is in practice, so we might go for a simpler option in future.</p><p>Example:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.APActivity.Migration</span><span class="w"> </span><span class="k" data-group-id="9190162428-1">do</span><span class="w">
</span><span class="na">@moduledoc</span><span class="w"> </span><span class="no">false</span><span class="w">
</span><span class="kn">use</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.APActivity</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">make_apactivity_table</span><span class="p" data-group-id="9190162428-2">(</span><span class="n">exprs</span><span class="p" data-group-id="9190162428-2">)</span><span class="w"> </span><span class="k" data-group-id="9190162428-3">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="9190162428-4">do</span><span class="w">
</span><span class="kn">require</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="nc">Needle.Migration</span><span class="o">.</span><span class="n">create_pointable_table</span><span class="p" data-group-id="9190162428-5">(</span><span class="nc">Bonfire.Data.Social.APActivity</span><span class="p" data-group-id="9190162428-5">)</span><span class="w"> </span><span class="k" data-group-id="9190162428-6">do</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:json</span><span class="p">,</span><span class="w"> </span><span class="ss">:jsonb</span><span class="w">
</span><span class="k">unquote_splicing</span><span class="p" data-group-id="9190162428-7">(</span><span class="n">exprs</span><span class="p" data-group-id="9190162428-7">)</span><span class="w">
</span><span class="k" data-group-id="9190162428-6">end</span><span class="w">
</span><span class="k" data-group-id="9190162428-4">end</span><span class="w">
</span><span class="k" data-group-id="9190162428-3">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_apactivity_table</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_apactivity_table</span><span class="p" data-group-id="9190162428-8">(</span><span class="p" data-group-id="9190162428-9">[</span><span class="p" data-group-id="9190162428-9">]</span><span class="p" data-group-id="9190162428-8">)</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_apactivity_table</span><span class="p" data-group-id="9190162428-10">(</span><span class="p" data-group-id="9190162428-11">[</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">body</span><span class="p" data-group-id="9190162428-11">]</span><span class="p" data-group-id="9190162428-10">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_apactivity_table</span><span class="p" data-group-id="9190162428-12">(</span><span class="n">body</span><span class="p" data-group-id="9190162428-12">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">drop_apactivity_table</span><span class="p" data-group-id="9190162428-13">(</span><span class="p" data-group-id="9190162428-13">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">drop_pointable_table</span><span class="p" data-group-id="9190162428-14">(</span><span class="nc">APActivity</span><span class="p" data-group-id="9190162428-14">)</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">maa</span><span class="p" data-group-id="9190162428-15">(</span><span class="ss">:up</span><span class="p" data-group-id="9190162428-15">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_apactivity_table</span><span class="p" data-group-id="9190162428-16">(</span><span class="p" data-group-id="9190162428-17">[</span><span class="p" data-group-id="9190162428-17">]</span><span class="p" data-group-id="9190162428-16">)</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">maa</span><span class="p" data-group-id="9190162428-18">(</span><span class="ss">:down</span><span class="p" data-group-id="9190162428-18">)</span><span class="w"> </span><span class="k" data-group-id="9190162428-19">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.APActivity.Migration</span><span class="o">.</span><span class="n">drop_apactivity_table</span><span class="p" data-group-id="9190162428-20">(</span><span class="p" data-group-id="9190162428-20">)</span><span class="w">
</span><span class="k" data-group-id="9190162428-19">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">migrate_apactivity</span><span class="p" data-group-id="9190162428-21">(</span><span class="p" data-group-id="9190162428-21">)</span><span class="w"> </span><span class="k" data-group-id="9190162428-22">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="9190162428-23">do</span><span class="w">
</span><span class="k">if</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">direction</span><span class="p" data-group-id="9190162428-24">(</span><span class="p" data-group-id="9190162428-24">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="ss">:up</span><span class="p">,</span><span class="w">
</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="9190162428-25">(</span><span class="n">maa</span><span class="p" data-group-id="9190162428-26">(</span><span class="ss">:up</span><span class="p" data-group-id="9190162428-26">)</span><span class="p" data-group-id="9190162428-25">)</span><span class="p">,</span><span class="w">
</span><span class="ss">else</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="9190162428-27">(</span><span class="n">maa</span><span class="p" data-group-id="9190162428-28">(</span><span class="ss">:down</span><span class="p" data-group-id="9190162428-28">)</span><span class="p" data-group-id="9190162428-27">)</span><span class="w">
</span><span class="k" data-group-id="9190162428-23">end</span><span class="w">
</span><span class="k" data-group-id="9190162428-22">end</span><span class="w">
</span><span class="k" data-group-id="9190162428-1">end</span></code></pre><h3 id="mixins" class="section-heading">
<a href="#mixins" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Mixins</span>
</h3>
<p>Mixins look much like pointables:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Profile.Migration</span><span class="w"> </span><span class="k" data-group-id="6659042844-1">do</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.Profile</span><span class="w">
</span><span class="c1"># create_profile_table/{0,1}</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">make_profile_table</span><span class="p" data-group-id="6659042844-2">(</span><span class="n">exprs</span><span class="p" data-group-id="6659042844-2">)</span><span class="w"> </span><span class="k" data-group-id="6659042844-3">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="6659042844-4">do</span><span class="w">
</span><span class="kn">require</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="nc">Needle.Migration</span><span class="o">.</span><span class="n">create_mixin_table</span><span class="p" data-group-id="6659042844-5">(</span><span class="nc">Bonfire.Data.Social.Profile</span><span class="p" data-group-id="6659042844-5">)</span><span class="w"> </span><span class="k" data-group-id="6659042844-6">do</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:name</span><span class="p">,</span><span class="w"> </span><span class="ss">:text</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:summary</span><span class="p">,</span><span class="w"> </span><span class="ss">:text</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:website</span><span class="p">,</span><span class="w"> </span><span class="ss">:text</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:location</span><span class="p">,</span><span class="w"> </span><span class="ss">:text</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:icon_id</span><span class="p">,</span><span class="w"> </span><span class="n">strong_pointer</span><span class="p" data-group-id="6659042844-7">(</span><span class="nc">Bonfire.Files.Media</span><span class="p" data-group-id="6659042844-7">)</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:image_id</span><span class="p">,</span><span class="w"> </span><span class="n">strong_pointer</span><span class="p" data-group-id="6659042844-8">(</span><span class="nc">Bonfire.Files.Media</span><span class="p" data-group-id="6659042844-8">)</span><span class="w">
</span><span class="k">unquote_splicing</span><span class="p" data-group-id="6659042844-9">(</span><span class="n">exprs</span><span class="p" data-group-id="6659042844-9">)</span><span class="w">
</span><span class="k" data-group-id="6659042844-6">end</span><span class="w">
</span><span class="k" data-group-id="6659042844-4">end</span><span class="w">
</span><span class="k" data-group-id="6659042844-3">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_profile_table</span><span class="p" data-group-id="6659042844-10">(</span><span class="p" data-group-id="6659042844-10">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_profile_table</span><span class="p" data-group-id="6659042844-11">(</span><span class="p" data-group-id="6659042844-12">[</span><span class="p" data-group-id="6659042844-12">]</span><span class="p" data-group-id="6659042844-11">)</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_profile_table</span><span class="p" data-group-id="6659042844-13">(</span><span class="p" data-group-id="6659042844-14">[</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="6659042844-15">{</span><span class="bp">_</span><span class="p">,</span><span class="w"> </span><span class="bp">_</span><span class="p">,</span><span class="w"> </span><span class="n">body</span><span class="p" data-group-id="6659042844-15">}</span><span class="p" data-group-id="6659042844-14">]</span><span class="p" data-group-id="6659042844-13">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_profile_table</span><span class="p" data-group-id="6659042844-16">(</span><span class="n">body</span><span class="p" data-group-id="6659042844-16">)</span><span class="w">
</span><span class="c1"># drop_profile_table/0</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">drop_profile_table</span><span class="p" data-group-id="6659042844-17">(</span><span class="p" data-group-id="6659042844-17">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">drop_mixin_table</span><span class="p" data-group-id="6659042844-18">(</span><span class="nc">Profile</span><span class="p" data-group-id="6659042844-18">)</span><span class="w">
</span><span class="c1"># migrate_profile/{0,1}</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">mp</span><span class="p" data-group-id="6659042844-19">(</span><span class="ss">:up</span><span class="p" data-group-id="6659042844-19">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_profile_table</span><span class="p" data-group-id="6659042844-20">(</span><span class="p" data-group-id="6659042844-21">[</span><span class="p" data-group-id="6659042844-21">]</span><span class="p" data-group-id="6659042844-20">)</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">mp</span><span class="p" data-group-id="6659042844-22">(</span><span class="ss">:down</span><span class="p" data-group-id="6659042844-22">)</span><span class="w"> </span><span class="k" data-group-id="6659042844-23">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="6659042844-24">do</span><span class="w">
</span><span class="nc">Bonfire.Data.Social.Profile.Migration</span><span class="o">.</span><span class="n">drop_profile_table</span><span class="p" data-group-id="6659042844-25">(</span><span class="p" data-group-id="6659042844-25">)</span><span class="w">
</span><span class="k" data-group-id="6659042844-24">end</span><span class="w">
</span><span class="k" data-group-id="6659042844-23">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">migrate_profile</span><span class="p" data-group-id="6659042844-26">(</span><span class="p" data-group-id="6659042844-26">)</span><span class="w"> </span><span class="k" data-group-id="6659042844-27">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="6659042844-28">do</span><span class="w">
</span><span class="k">if</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">direction</span><span class="p" data-group-id="6659042844-29">(</span><span class="p" data-group-id="6659042844-29">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="ss">:up</span><span class="p">,</span><span class="w">
</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="6659042844-30">(</span><span class="n">mp</span><span class="p" data-group-id="6659042844-31">(</span><span class="ss">:up</span><span class="p" data-group-id="6659042844-31">)</span><span class="p" data-group-id="6659042844-30">)</span><span class="p">,</span><span class="w">
</span><span class="ss">else</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="6659042844-32">(</span><span class="n">mp</span><span class="p" data-group-id="6659042844-33">(</span><span class="ss">:down</span><span class="p" data-group-id="6659042844-33">)</span><span class="p" data-group-id="6659042844-32">)</span><span class="w">
</span><span class="k" data-group-id="6659042844-28">end</span><span class="w">
</span><span class="k" data-group-id="6659042844-27">end</span><span class="w">
</span><span class="k" data-group-id="6659042844-1">end</span></code></pre><h3 id="multimixins-1" class="section-heading">
<a href="#multimixins-1" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">Multimixins</span>
</h3>
<p>Similar to mixins:</p><pre><code class="makeup elixir" translate="no"><span class="kd">defmodule</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.FeedPublish.Migration</span><span class="w"> </span><span class="k" data-group-id="7605404722-1">do</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="w">
</span><span class="kn">import</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="kn">alias</span><span class="w"> </span><span class="nc">Bonfire.Data.Social.FeedPublish</span><span class="w">
</span><span class="na">@feed_publish_table</span><span class="w"> </span><span class="nc">FeedPublish</span><span class="o">.</span><span class="c">__schema__</span><span class="p" data-group-id="7605404722-2">(</span><span class="ss">:source</span><span class="p" data-group-id="7605404722-2">)</span><span class="w">
</span><span class="c1"># create_feed_publish_table/{0,1}</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">make_feed_publish_table</span><span class="p" data-group-id="7605404722-3">(</span><span class="n">exprs</span><span class="p" data-group-id="7605404722-3">)</span><span class="w"> </span><span class="k" data-group-id="7605404722-4">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="7605404722-5">do</span><span class="w">
</span><span class="kn">require</span><span class="w"> </span><span class="nc">Needle.Migration</span><span class="w">
</span><span class="nc">Needle.Migration</span><span class="o">.</span><span class="n">create_mixin_table</span><span class="p" data-group-id="7605404722-6">(</span><span class="nc">Bonfire.Data.Social.FeedPublish</span><span class="p" data-group-id="7605404722-6">)</span><span class="w"> </span><span class="k" data-group-id="7605404722-7">do</span><span class="w">
</span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">add</span><span class="w"> </span><span class="ss">:feed_id</span><span class="p">,</span><span class="w">
</span><span class="nc">Needle.Migration</span><span class="o">.</span><span class="n">strong_pointer</span><span class="p" data-group-id="7605404722-8">(</span><span class="p" data-group-id="7605404722-8">)</span><span class="p">,</span><span class="w"> </span><span class="ss">primary_key</span><span class="p">:</span><span class="w"> </span><span class="no">true</span><span class="w">
</span><span class="k">unquote_splicing</span><span class="p" data-group-id="7605404722-9">(</span><span class="n">exprs</span><span class="p" data-group-id="7605404722-9">)</span><span class="w">
</span><span class="k" data-group-id="7605404722-7">end</span><span class="w">
</span><span class="k" data-group-id="7605404722-5">end</span><span class="w">
</span><span class="k" data-group-id="7605404722-4">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_feed_publish_table</span><span class="p" data-group-id="7605404722-10">(</span><span class="p" data-group-id="7605404722-10">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_feed_publish_table</span><span class="p" data-group-id="7605404722-11">(</span><span class="p" data-group-id="7605404722-12">[</span><span class="p" data-group-id="7605404722-12">]</span><span class="p" data-group-id="7605404722-11">)</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">create_feed_publish_table</span><span class="p" data-group-id="7605404722-13">(</span><span class="p" data-group-id="7605404722-14">[</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="p" data-group-id="7605404722-15">{</span><span class="bp">_</span><span class="p">,</span><span class="w"> </span><span class="bp">_</span><span class="p">,</span><span class="w"> </span><span class="n">body</span><span class="p" data-group-id="7605404722-15">}</span><span class="p" data-group-id="7605404722-14">]</span><span class="p" data-group-id="7605404722-13">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">make_feed_publish_table</span><span class="p" data-group-id="7605404722-16">(</span><span class="n">body</span><span class="p" data-group-id="7605404722-16">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">drop_feed_publish_table</span><span class="p" data-group-id="7605404722-17">(</span><span class="p" data-group-id="7605404722-17">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">drop_pointable_table</span><span class="p" data-group-id="7605404722-18">(</span><span class="nc">FeedPublish</span><span class="p" data-group-id="7605404722-18">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_feed_publish_feed_index</span><span class="p" data-group-id="7605404722-19">(</span><span class="n">dir</span><span class="w"> </span><span class="o">\\</span><span class="w"> </span><span class="n">direction</span><span class="p" data-group-id="7605404722-20">(</span><span class="p" data-group-id="7605404722-20">)</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="w"> </span><span class="o">\\</span><span class="w"> </span><span class="p" data-group-id="7605404722-21">[</span><span class="p" data-group-id="7605404722-21">]</span><span class="p" data-group-id="7605404722-19">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_feed_publish_feed_index</span><span class="p" data-group-id="7605404722-22">(</span><span class="ss">:up</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p" data-group-id="7605404722-22">)</span><span class="p">,</span><span class="w">
</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">create_if_not_exists</span><span class="p" data-group-id="7605404722-23">(</span><span class="n">index</span><span class="p" data-group-id="7605404722-24">(</span><span class="na">@feed_publish_table</span><span class="p">,</span><span class="w"> </span><span class="p" data-group-id="7605404722-25">[</span><span class="ss">:feed_id</span><span class="p" data-group-id="7605404722-25">]</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p" data-group-id="7605404722-24">)</span><span class="p" data-group-id="7605404722-23">)</span><span class="w">
</span><span class="kd">def</span><span class="w"> </span><span class="nf">migrate_feed_publish_feed_index</span><span class="p" data-group-id="7605404722-26">(</span><span class="ss">:down</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p" data-group-id="7605404722-26">)</span><span class="p">,</span><span class="w">
</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">drop_if_exists</span><span class="p" data-group-id="7605404722-27">(</span><span class="n">index</span><span class="p" data-group-id="7605404722-28">(</span><span class="na">@feed_publish_table</span><span class="p">,</span><span class="w"> </span><span class="p" data-group-id="7605404722-29">[</span><span class="ss">:feed_id</span><span class="p" data-group-id="7605404722-29">]</span><span class="p">,</span><span class="w"> </span><span class="n">opts</span><span class="p" data-group-id="7605404722-28">)</span><span class="p" data-group-id="7605404722-27">)</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">mf</span><span class="p" data-group-id="7605404722-30">(</span><span class="ss">:up</span><span class="p" data-group-id="7605404722-30">)</span><span class="w"> </span><span class="k" data-group-id="7605404722-31">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="7605404722-32">do</span><span class="w">
</span><span class="nc">Bonfire.Data.Social.FeedPublish.Migration</span><span class="o">.</span><span class="n">create_feed_publish_table</span><span class="p" data-group-id="7605404722-33">(</span><span class="p" data-group-id="7605404722-33">)</span><span class="w">
</span><span class="nc">Bonfire.Data.Social.FeedPublish.Migration</span><span class="o">.</span><span class="n">migrate_feed_publish_feed_index</span><span class="p" data-group-id="7605404722-34">(</span><span class="p" data-group-id="7605404722-34">)</span><span class="w">
</span><span class="k" data-group-id="7605404722-32">end</span><span class="w">
</span><span class="k" data-group-id="7605404722-31">end</span><span class="w">
</span><span class="kd">defp</span><span class="w"> </span><span class="nf">mf</span><span class="p" data-group-id="7605404722-35">(</span><span class="ss">:down</span><span class="p" data-group-id="7605404722-35">)</span><span class="w"> </span><span class="k" data-group-id="7605404722-36">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="7605404722-37">do</span><span class="w">
</span><span class="nc">Bonfire.Data.Social.FeedPublish.Migration</span><span class="o">.</span><span class="n">migrate_feed_publish_feed_index</span><span class="p" data-group-id="7605404722-38">(</span><span class="p" data-group-id="7605404722-38">)</span><span class="w">
</span><span class="nc">Bonfire.Data.Social.FeedPublish.Migration</span><span class="o">.</span><span class="n">drop_feed_publish_table</span><span class="p" data-group-id="7605404722-39">(</span><span class="p" data-group-id="7605404722-39">)</span><span class="w">
</span><span class="k" data-group-id="7605404722-37">end</span><span class="w">
</span><span class="k" data-group-id="7605404722-36">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">migrate_feed_publish</span><span class="p" data-group-id="7605404722-40">(</span><span class="p" data-group-id="7605404722-40">)</span><span class="w"> </span><span class="k" data-group-id="7605404722-41">do</span><span class="w">
</span><span class="k">quote</span><span class="w"> </span><span class="k" data-group-id="7605404722-42">do</span><span class="w">
</span><span class="k">if</span><span class="w"> </span><span class="nc">Ecto.Migration</span><span class="o">.</span><span class="n">direction</span><span class="p" data-group-id="7605404722-43">(</span><span class="p" data-group-id="7605404722-43">)</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="ss">:up</span><span class="p">,</span><span class="w">
</span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="7605404722-44">(</span><span class="n">mf</span><span class="p" data-group-id="7605404722-45">(</span><span class="ss">:up</span><span class="p" data-group-id="7605404722-45">)</span><span class="p" data-group-id="7605404722-44">)</span><span class="p">,</span><span class="w">
</span><span class="ss">else</span><span class="p">:</span><span class="w"> </span><span class="k">unquote</span><span class="p" data-group-id="7605404722-46">(</span><span class="n">mf</span><span class="p" data-group-id="7605404722-47">(</span><span class="ss">:down</span><span class="p" data-group-id="7605404722-47">)</span><span class="p" data-group-id="7605404722-46">)</span><span class="w">
</span><span class="k" data-group-id="7605404722-42">end</span><span class="w">
</span><span class="k" data-group-id="7605404722-41">end</span><span class="w">
</span><span class="kd">defmacro</span><span class="w"> </span><span class="nf">migrate_feed_publish</span><span class="p" data-group-id="7605404722-48">(</span><span class="n">dir</span><span class="p" data-group-id="7605404722-48">)</span><span class="p">,</span><span class="w"> </span><span class="ss">do</span><span class="p">:</span><span class="w"> </span><span class="n">mf</span><span class="p" data-group-id="7605404722-49">(</span><span class="n">dir</span><span class="p" data-group-id="7605404722-49">)</span><span class="w">
</span><span class="k" data-group-id="7605404722-1">end</span></code></pre><h2 id="more-examples" class="section-heading">
<a href="#more-examples" class="hover-link">
<i class="ri-link-m" aria-hidden="true"></i>
</a>
<span class="text">More examples</span>
</h2>
<p>Take a look at a few of the migrations in our data libraries. Between them, they cover most
scenarios by now:</p><ul><li><a href="https://github.com/bonfire-networks/bonfire_data_social/">bonfire_data_social</a></li><li><a href="https://github.com/bonfire-networks/bonfire_data_access_control/">bonfire_data_access_control</a></li><li><a href="https://github.com/bonfire-networks/bonfire_data_identity/">bonfire_data_identity</a></li><li><a href="https://github.com/bonfire-networks/bonfire_data_edges/">bonfire_data_edges</a> (feat. bonus triggers)</li></ul><p>If you want to know exactly what's happening, you may want to read the code for
<a href="https://github.com/bonfire-networks/needle/blob/main/lib/migration.ex">Needle.Migration</a>.</p>
<div class="bottom-actions">
<div class="bottom-actions-item">
<a href="bonfire-flavoured-elixir.html" class="bottom-actions-button" rel="prev">
<span class="subheader">
← Previous Page
</span>
<span class="title">
Bonfire-flavoured Elixir
</span>
</a>
</div>
<div class="bottom-actions-item">
<a href="boundaries.html" class="bottom-actions-button" rel="next">
<span class="subheader">
Next Page →
</span>
<span class="title">
Boundaries & Access Control
</span>
</a>
</div>
</div>
<footer class="footer">
<p>
<span class="line">
<button class="a-main footer-button display-quick-switch" title="Search HexDocs packages">
Search HexDocs
</button>
<a href="bonfire_umbrella.epub" title="ePub version">
Download ePub version
</a>
</span>
</p>
<p class="built-using">
Built using
<a href="https://github.com/elixir-lang/ex_doc" title="ExDoc" target="_blank" rel="help noopener" translate="no">ExDoc</a> (v0.31.2) for the
<a href="https://elixir-lang.org" title="Elixir" target="_blank" translate="no">Elixir programming language</a>
</p>
</footer>
</div>
</div>
</main>
</div>
</body>
</html>