Skip to content

Instantly share code, notes, and snippets.

@zeroasterisk
Last active Aug 4, 2021
Embed
What would you like to do?
Example Elixir+Ecto schema to mirror a Mongo DB with complicated data structures
defmodule Eltoroportal.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias Eltoroportal.Accounts.User
@primary_key {:_id, :string, autogenerate: false}
schema "users" do
field :username, :string
# field :created, :date # <-- ? mongo translation?
# field :addresses, {:array, :map} # ? array of sub-documents - this fits the docs fairly well
field :emails, {:array, Email}, on_replace: :update
# field :profile, :map # ? sub-document with static keys, should be easy to map (multi-layer)
# field :roles, :map # ? sub-document with dynamic keys, and array of strings
embeds_one :services, Service, on_replace: :update
timestamps()
end
@doc false
def changeset(%User{} = user, attrs) do
user
|> cast(attrs, [:username])
|> cast_embed(:services)
|> validate_required([:username])
end
defmodule Service do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "" do
embeds_one :password, ServicePassword, on_replace: :update
embeds_one :google, ServiceGoogle, on_replace: :update
end
def changeset(struct, params) do
struct
|> cast(params, [])
|> cast_embed(:password)
|> cast_embed(:google)
end
end
defmodule ServicePassword do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "" do
field :bcrypt, :string
end
def changeset(struct, params) do
struct
|> cast(params, [:bcrypt])
end
end
defmodule ServiceGoogle do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "" do
field :id, :string
field :accessToken, :string
field :idToken, :string
field :expiresAt, :integer
field :scope, {:array, :string}
field :email, :string
field :verified_email, :boolean
field :name, :string
field :given_name, :string
field :family_name, :string
field :locale, :string
field :gender, :string
field :picture, :string
end
def changeset(struct, params) do
struct
|> cast(params, [
:id,
:accessToken,
:idToken,
:expiresAt,
:scope,
:email,
:verified_email,
:name,
:given_name,
:family_name,
:locale,
:gender,
:picture
])
end
end
defmodule Email do
use Ecto.Schema
import Ecto.Changeset
@primary_key false
schema "" do
field :label, :string
field :address, :string # regEx: SimpleSchema.RegEx.Email,
field :service, :string # ?
field :verified, :boolean
end
def changeset(struct, params) do
struct
|> cast(params, [
:label,
:address,
:service,
:verified,
])
|> update_change(:address, &String.downcase/1)
|> validate_required([:address])
|> validate_format(:address, ~r/([\w]+)@([\w]+).([\w]{2,32})/)
# |> validate_unique(:address, on: Repo)
end
end
end
{
"_id" : "1122334455667788",
"username": "alanblount",
"created" : ISODate("2017-01-01T00:00:00.000-04:00"),
"emails" : [
{
"address" : "alan@example.com",
"verified" : true,
"service" : "google",
"label" : "primary"
}
],
"addresses": [
{ "type": 1, "address": "123 n. 1st st", city: "Louisville", state: "KY", zip: "40206" }
],
"profile" : {
"name" : "Alan Blount",
"email" : "alan@example.com",
"avatar" : "https://lh4.googleusercontent.com/-GRaizMd9Jbo/AAAAAAAAAAI/AAAAAAAAACM/_tkoXy1kU2c/photo.jpg",
"lastLogin" : ISODate("2017-06-14T13:11:32.402-04:00"),
"agreed" : true,
"company" : "Somewhere",
"approved" : true,
"mapcenter" : {
"lat" : 34.1835891424516,
"lng" : -84.342645
}
},
"roles": {
"groupname_1": ["value1", "value2"],
"groupname_2": ["something"],
},
"services" : {
"password" : {
"bcrypt" : "$2a$10$sg/xxxxxxxxxxxxxxxx"
},
"google" : {
"id" : "1111111111111111",
"accessToken" : "xxxx.xxxxxxxxx-xxxxxxx-xxxxxxx-x-xxxxxxxx",
"idToken" : "xxxxxxxxxxx",
"expiresAt" : 1492357827963.0,
"scope" : [
"https://www.googleapis.com/auth/userinfo.email",
"https://www.googleapis.com/auth/userinfo.profile"
],
"email" : "alan@example.com",
"verified_email" : true,
"name" : "Alan Blount",
"given_name" : "Alan",
"family_name" : "Blount",
"picture" : "https://lh4.googleusercontent.com/-GRaizMd9Jbo/AAAAAAAAAAI/AAAAAAAAAFI/W7JfoXRl4T0/photo.jpg",
"locale" : "en",
"gender" : "male"
},
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment