我最近開始將現有的PHP messenger應用程序移植到elixir(使用elixir 1.3,phoenix 1.2,ecto 2.0.1和mariaex 0.7.7)。這個應用程序爲數百萬用戶提供服務,因此性能很重要我很新的藥劑所以請原諒我的問題Elixir&ecto:子查詢或側加載和規範化json輸出的視圖
的啞我有以下的數據庫模式:
每個線程都有多個thread_participants和消息。 thread_participant在鏈接線程的上下文中有關於用戶的信息(例如,當用戶上次看到此線程時)。線程由用戶創作的多條消息組成。
我想什麼JSON我的API到底返回是:
"data": {
"result": [1, 2],
"threads": {
1: {
"id": 1,
"unread_count": 2,
"starred": false,
"muted": false,
"last_seen": "2015-10-20T19:01:46",
"participants": [1, 2]
},
22: {
"id": 22,
"unread_count": 0,
"starred": true,
"muted": false,
"last_seen": "2016-06-20T12:00:00",
"participants": [1, 3]
}
},
users: {
1: {
id: 1,
name: 'John'
},
2: {
id: 2,
name: 'Dan'
},
3: {
id: 3,
name: 'Eric'
}
}
這裏是我的主題和ThreadParticipant模式:
schema "thread" do
field :created, Ecto.DateTime, usec: true, autogenerate: true
belongs_to :creator, UserAbstract
has_many :messages, ThreadMessage
has_many :participants, ThreadParticipant
has_many :users, through: [:participants, :user]
field :last_seen, Ecto.DateTime, virtual: true, default: :null
field :muted, :boolean, virtual: true, default: false
field :starred, :boolean, virtual: true, default: false
field :unread_count, :integer, virtual: true, default: 0
end
@primary_key false
schema "thread_participant" do
belongs_to :thread, Messenger.Thread, primary_key: true
belongs_to :user, Messenger.UserAbstract, primary_key: true
field :last_seen, Ecto.DateTime, usec: true, autogenerate: true
field :starred, :boolean, default: false
field :muted, :boolean, default: false
end
,我使用查詢組成的背景情況線程列表爲用戶:
def for_user(query, user_id) do
from t in query,
join: p in assoc(t, :participants),
join: message in assoc(t, :messages),
left_join: messageNew in ThreadMessage, on: messageNew.id == message.id and messageNew.created > p.last_seen,
where: p.user_id == ^user_id,
order_by: [desc: max(message.created)],
group_by: t.id,
select: %{ t | last_seen: p.last_seen, muted: p.muted,starred: p.starred, unread_count: count(messageNew.id)}
end
所以當我做
Thread |> Thread.for_user(user_id) |> Repo.all
我能夠獲得幾乎所有正確的聚集信息,但由於group_by thread.id,我錯過了參與者ID。
在純SQL我會做類似下面的代碼,然後重建我的模型代碼:
SELECT s.id, s.last_seen, s.muted, s.starred, s.last_message_date, s.unread_count, p.user_id
FROM (
SELECT t0.`id` , t2.`last_seen` , t2.`muted` , t2.`starred` , max(t1.`created`) as last_message_date, count(t3.id) as unread_count
FROM `thread` AS t0
INNER JOIN `thread_message` AS t1 ON t0.`id` = t1.`thread_id`
INNER JOIN `thread_participant` AS t2 ON (t0.`id` = t2.`thread_id`) AND (t2.`user_id` = 9854)
LEFT JOIN `thread_message` AS t3 ON t3.`id` = t1.`id` AND t3.`created` > t2.`last_seen`
GROUP BY t0.`id`
) as s
INNER JOIN `thread_participant` AS p ON p.`thread_id` = s.`id`
ORDER BY s.`last_message_date` DESC
我所有的嘗試翻譯這外生(即使使用子查詢或片斷)都失敗了(沒有最大()在子查詢,子查詢字段別名不被保存,...)
所以除了第一查詢(for_user()),我加載第二個查詢參與者:
thread_ids = Enum.map(threads, fn (x) -> x.id end)
def get_participating_user(thread_ids) do
from tp in ThreadParticipant,
join: user in assoc(tp, :user),
where: tp.thread_id in ^thread_ids,
preload: :user
end
participants = Thread.get_participating_user(thread_ids) |> Repo.all
但現在我被困在如何合併兩個結果集(把ThreadParticipants從第二個查詢中他們屬於每個線程中的參與者鍵下的第一個查詢中),然後如何輸出它,規範化,在我看來(只參加者的ID都保持在thread.participants和所有不同的用戶可根據用戶輸出)
已經停留在此幾個小時,我會很感激的任何知識,你可以分享