mayel
When using a relational DB (such an Ecto schema with a Postgres table), usually a foreign key field has to be pre-defined with a reference pointing to a specific field in a specific table.
A simple example would be a blogging app, which might have a post table with author field that references the user table.
A social network, by contrast, usually requires a graph of objects, meaning objects need to be able to refer to other objects by their ID without knowing their type.
A simple example would be likes, you might have a likes table with liked_post_id field that references the post table. But what if 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 liked_video_id, liked_image_id, ad infinitum?
We needed the flexibility to have a foreign key that can reference any referenceable object. We call our system Needle.
Note: This library was developed as part of the Bonfire project, which has some open bounties for any help improving performance:.
Besides regular schemas with universal foreign keys (Pointable), Needle provides Virtual for schemas that don’t have any fields on their own, and Mixin for storing common fields that can be re-used by multiple Pointables or Virtuals (eg. if you have multiple types that all have a name/description/body, they can share those and optionally the associated changesets and other logic).
Needle also comes with two other homegrown libraries: needle_ulid which provides a ULID datatype for Ecto (using ex_ulid) and related helpers, and exto which enables extending Ecto schema definitions in config (especially useful for adding Mixin associations to different schemas)…
- Needle Docs (which contain a more extensive intro and usage guide)
- needle | Hex
Trending in Announcing
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixirconf-us
- #ai
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 4- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
garrison
Your docs are very thorough and well-written
If I had to offer one suggestion there, I think some query examples would be helpful near the end - perhaps some real-world examples from Bonfire? But really, overall excellent docs.
Also, is there a reason you chose to go all in on ULID instead of UUIDv7, which is now standard? I assume you guys have probably been at this since before UUIDv7 was standardized, so perhaps it was too late to change?
I mention it because it probably wouldn’t be very hard to switch over since they’re both 128 bit binaries in the DB at the end of the day and have a pretty similar structure (timestamp + random). Generating a UUIDv7 in Elixir is like three lines of code, and then you can just delegate all the
Ecto.Typefunctions over toEcto.UUIDsince they’re compatible anyway.For anyone coming across this post who’s curious, the “correct” way to do this in an RDBMS is to create a parent table (e.g.
likeable) and then give each “child” table a foreign key to the parent table (e.g.likeable_idonposts). Then ausercan have alikewhich joins throughlikeableto thepost. This way the new foreign key columns always end up on the new table.Indeed, if I understand correctly the above is actually exactly what this project is doing, except with lots of tooling to generalize it to the entire schema, which is cool!
mayel
Thanks for the feedback! I’ll look for some simple query examples to add to the docs.
Regarding UUIDv7, yeah we chose ULID before it was an option, and actually have an open issue about migrating to it (and at the same time adopting prefixed “object ids”), feedback welcome: Consider using prefixed UUIDv7 instead of ULID · Issue #941 · bonfire-networks/bonfire-app · GitHub
TwistingTwists
Here are a few examples of schema that will finally be.
This is a rough idea. Feel free to correct it this is wrong.
Sample queries -
mayel
With Needle, we don’t need a separate Likeable schema because all Pointables or Virtuals can be likeable by default. Let’s revise these examples to reflect how Needle can be used.
Here’s those same schemas as you might define them with Needle:
likedfield that referencesNeedle.Pointer, allowing it to like any Pointable object.Now, let’s update our query examples to show how to use these schemas with Needle:
Next we can also add useful associations in config using Exto. Add the following to your
config.exs:Listing likes for a post using the association: