I am writing a twitter clone project to see how it is done. I want to be able to create mentions to users from the post. I have these schemas
defmodule TweetClone.Accounts.User do
use Ecto.Schema
defmodule TweetClone.Accounts.User do
use Ecto.Schema
schema "users" do
field :nickname, :string
# ... other things
many_to_many :mentioned_statuses, Status, join_through: TweetClone.Statuses.Mentions
end
end
defmodule TweetClone.Statuses.Mention do
use Ecto.Schema
import Ecto.Changeset
schema "mentions" do
belongs_to :user, TweetClone.Accounts.User
belongs_to :status, TweetClone.Statuses.Status
timestamps()
end
def changeset(%__MODULE__{} = mention, %{user: user, status: status}) do
mention
|> change()
|> put_assoc(:user, user)
|> put_assoc(:status, status)
end
end
defmodule TweetClone.Statuses.Status do
use Ecto.Schema
schema "statuses" do
# ... various fields
many_to_many :mentioned_users, User, join_through: TweetClone.Statuses.Mention
end
def changeset(status, attrs) do
status
# ... various pipes
|> mention_users()
end
defp mention_users(changeset) do
text = get_change(changeset, :text, "")
mentioned_nicknames =
Regex.scan(~r/@[\w.@_-]+/u, text)
|> Enum.map(fn nickname ->
nickname
|> hd()
|> String.trim_leading("@")
end)
mentioned_users = Repo.all(from u in User, where: u.nickname in ^mentioned_nicknames)
put_assoc(changeset, :mentioned_users, mentioned_users)
end
end
This works but when I do in iex
iex(49)> attrs = %{sender: user1, text: "Hello, @john2 @john3"}
iex(50)> Statuses.create_status(attrs)
[debug] QUERY OK source="users" db=2.8ms queue=0.2ms idle=1988.4ms
SELECT u0."id", u0."nickname", u0."email", u0."password_hash", u0."confirmed_at", u0."reset_sent_at", u0."inserted_at", u0."updated_at" FROM "users" AS u0 WHERE (u0."nickname" = ANY($1)) [["john2", "john3"]]
[debug] QUERY OK db=4.2ms queue=0.3ms idle=1991.8ms
begin []
[debug] QUERY OK db=6.4ms
INSERT INTO "statuses" ("sender_id","text","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [1, "Hello, @john2 @john3", ~N[2020-05-31 17:41:42], ~N[2020-05-31 17:41:42]]
[debug] QUERY OK db=9.5ms
INSERT INTO "mentions" ("status_id","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [71, 2, ~N[2020-05-31 17:41:42], ~N[2020-05-31 17:41:42]]
[debug] QUERY OK db=2.7ms
INSERT INTO "mentions" ("status_id","user_id","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" [71, 3, ~N[2020-05-31 17:41:42], ~N[2020-05-31 17:41:42]]
[debug] QUERY OK db=3.2ms
commit []
{:ok,
%TweetClone.Statuses.Status{
...
It performs one insert for the Status
and two other inserts for the Mention
rows. Is there any way to minimize those insert operations?