Передача данных из модели / базы данных в канал с использованием присутствия
У меня есть простое приложение для чата, которое я построил, и я хочу иметь возможность отображать загруженные пользователем изображения (локально размещенные) рядом с именами пользователей на html-странице канала. В настоящее время я использую присутствие для отслеживания пользователей, которые вошли в канал и т. Д. Мне удалось переопределить fetch/2
с пониманием, что это позволит мне добавить пару полей карты в :metas
символ с данными модели пользователя.
Из того, что я могу сказать, основываясь на обширной IO.inspecting
различных частей каждой из функций; fetch/2
, handle_info/2
и некоторые console.logging на моем уровне JS, fetch/2
функция фактически не получает никаких данных из базы данных и не присваивает их :metas
карта.
вот мой ток fetch/2
функция:
def fetch(_topic, entries) do
query =
from u in User,
where: u.id in ^Map.keys(entries),
select: {u.id, u}
users = query |> Repo.all |> Enum.into(%{})
for {key, %{metas: metas}} <- entries, into: %{} do
{key, %{metas: metas, user: users[key]}}
end
Это в основном копируется прямо из документации. В теории, функция выше должна запросить мою модель пользователя и захватить все пользовательские данные, основанные на User.id
это передается ему через карту записей. Users[keys]
возвращается как пустой users
быть полной картой моей модели пользователя.
Кроме того, согласно документации, запрос должен выполняться только при объединении, чтобы не перегружать БД, но, похоже, он выполняется 4-5 раз при каждом обновлении страницы. Еще одна вещь, которую стоит отметить, это то, что user.id
внутри записей, похоже, строковый тип. Я не уверен, если это важно, я попытался передать целое число из слоя JS, а также с помощью Interger.parse
от фактического fetch/2
Функция изменить это безрезультатно.
Когда я проверяю карту пользователей, я получаю это:
{"1" => %MyApp.User{__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
email: "test@test.com", encrypt_pass: "$pbkdf2-
sha512$160000$ebfY956TgIXhEAF.mqLJAg$QWzBubfeiy4Xrf.EsFiU0jEZAuKvV4ZO5a
8QpeFr817C61DuaNfyo56WWzj6jak2homCFWAINbPrFtCSXUPWTw", gravatar: %
{file_name: "logo.png", updated_at: #Ecto.DateTime<2017-04-20 22:00:08>},
id: 1, inserted_at: ~N[2017-04-20 22:00:09.071000], password: nil,
updated_at: ~N[2017-04-20 22:00:09.090000]}}
Мои пользователи [ключ] возвращает пустую карту, как это %{}
и преобразование ввода в целое число выдает ошибку, (Poison.EncodeError) unable to encode value: {nil, "users"}
если я преобразую его внутри кода эликсира, и where: u.id in ^["undefined"], select: {u.id, u} (elixir) lib/enum.ex:1755: Enum."-reduce/3-lists^foldl/2-0-"/3 –
если я преобразую его из слоя JS.
Оригинал fetch/2
вывод представляет собой массив с online_at: 1492764577562
а также phx_ref: "OAyzaGE82xc="
в :metas
карта и мой идентификатор пользователя или адрес электронной почты в users
вар.
Что мне здесь не хватает? Я знаю что fetch/2
Функция выполняется только как обратный вызов Presence.list/1
функция, которую я называю в моем handle_info/2
функция канала Я тоже звоню Presence.list
в моем слое JS и сопоставление его с моим присутствием, чтобы я мог создать список имен пользователей в HTML. Я просто неправильно понимаю, как это работает, или есть какой-то другой более простой способ, которым я должен идти об этом? Если вам нужно увидеть больше кода, я могу предоставить больше.
редактировать: я гораздо лучше понимаю, что здесь происходит. моя карта записей на самом деле это:
%{"1" => %{metas: [%{online_at: 1492798247818, phx_ref: "ELHwA+gWF+0="}]}}
Таким образом, в основном, строка для идентификатора пользователя, "1"
отображается на metas
карта. Когда я пытаюсь просто взять этот ключ с карты с Map.keys(entries)
Функция не может извлечь что-либо из БД, потому что это строка, однако, когда я изменяю ее на целое число со стороны JavaScript, он выдает ошибку, потому что по какой-то причине Феникс ожидает, что этот ключ будет строковым типом, Как ни странно, если я изменю id
из id
для email
и попробуйте запросить базу данных с электронной почтой, она тоже не работает. несмотря на то, что адрес электронной почты в базе данных является строкой и metas
карта ожидает ключ строки для entries
карта.
Я собираюсь восстановить эту часть приложения с нуля и посмотреть, что вызывает эту проблему. Тогда я вернусь и посмотрю, смогу ли я исправить ошибку.
2 ответа
Я уже понял, что проблема имеет очень мало общего с моим fetch/2
сама функция, скорее, это было связано с моей реализацией модуля присутствия и канала в этом случае. В основном, fetch/2
функция вызывается 4 раза каждый раз, когда кто-то входит в чат, а два раза из четырех вызывается с пустым значением списка []
,
Очевидно, что вы не можете запросить модель Ecto с пустым списком, поэтому в этом случае выдает ошибку. Я попытался установить защиту для функции выборки, чтобы отфильтровать вызовы пустого списка, но это не показывало metas
данные карты, которые я искал, даже когда запрос был выполнен успешно.
Кроме того, другой главной проблемой была моя реализация или отсутствие реализации token
, Мне не пришлось бы передавать данные модели пользователя через функцию выборки metas
карта, если я использовал токен для присоединения к чату, а не просто user
(ака просто имя пользователя). После этого я смог успешно связать данные пользовательской модели с каналом и показать их через уровень JS и, в конечном итоге, поместить на клиент.
В любом случае, ребята, спасибо за предложения. Возможно, вы не ответили на вопрос (это была моя вина, что я задал неправильный вопрос), но вы, безусловно, помогли мне добраться туда. А также дал мне инструменты, чтобы сформировать намного лучшее понимание структуры в целом на пути.
Если / когда у меня появятся какие-либо вопросы, я постараюсь задать правильные вопросы, прежде чем публиковать их в переполнение стека, чтобы не тратить время.
Сначала вы должны проверить ключи на карте записей.
ids = Map.keys(entries)
true = Enum.all?(ids, &is_integer/1)
Ecto преобразует строки в целые числа при интерполяции в запрос:
iex(40)> Ecto.Query.from(u in Users, where: u.id in ^[1, 2, "3", nil], select: u.id) |> Repo.all()
выводит следующий журнал отладки:
[debug] QUERY OK source="users" db=0.8ms
SELECT u0."id" FROM "users" AS r0 WHERE (r0."id" = ANY($1)) [[1, 2, 3, nil]]
Обратите внимание, что она привела строку "3" к целому числу и допустила ноль.
Однако карта не будет такой доброй:
iex(42)> users = %{1 => %{name: "joe"}, 2 => %{name: "jill"}}
%{1 => %{name: "joe"}, 2 => %{name: "jill"}}
iex(43)> users["1"]
nil
Так что в коде, где вы используете ключи от entries
для поиска в базе данных и поиска на карте это может привести к различным результатам.