Комментарии 34
как минимум — полезно
0
Спасибо за пост, идея расширить виды связей очень полезна может быть для соцсетей. А не считаете ли вы избыточным хранение в базе записи инвертированного близнеца?
+1
Приблизительно этого вопроса я очень ждал.
Создание записи близнеца в БД с обратным расположением id пользователей позволяет выполнить выборку социальных связей пользователя за один запрос. Однако, это требует контроля состояний обеих записей. pending, accepted, rejected — должны меняться синхронно, что требует дополнительных манипуляций.
Я выбрал другой подход — запись одна. Контроля состояний не требуется. Однако, как видите, требуется два запроса на выборку всех связей.
Что выбрать?
Если честно — то в этом то и суть поста.
Я не знаю что выгоднее в production решениях, школьный рельсовый сайт с посещением 3000 пользователей в месяц не позволит дать мне никаких рекомендаций. Я надеялся, что кто-нибудь поделится опытом. Ради этого то все и затевалось.
Создание записи близнеца в БД с обратным расположением id пользователей позволяет выполнить выборку социальных связей пользователя за один запрос. Однако, это требует контроля состояний обеих записей. pending, accepted, rejected — должны меняться синхронно, что требует дополнительных манипуляций.
Я выбрал другой подход — запись одна. Контроля состояний не требуется. Однако, как видите, требуется два запроса на выборку всех связей.
Что выбрать?
Если честно — то в этом то и суть поста.
Я не знаю что выгоднее в production решениях, школьный рельсовый сайт с посещением 3000 пользователей в месяц не позволит дать мне никаких рекомендаций. Я надеялся, что кто-нибудь поделится опытом. Ради этого то все и затевалось.
+1
Интересный пост.
Сразу вопрос: учитывая что запись в БД и само отношение между пользователями является уникальным в комбинации «отправитель: получатель: контекст», отправитель может иметь несколько записей с одним и тем же получателем, но в разных контекстах. Как вы планируете визуализацию этих связей?
>Я надеялся, что кто-нибудь поделится опытом.
Для школьного сайта с небольшим посещением такие решения может быть и подходят, не вижу смысла волноваться. Но, в production большого сервиса правильнее будет денормализовать БД. Пусть будет запись близнец, но не 2 запроса в ДБ.
Сразу вопрос: учитывая что запись в БД и само отношение между пользователями является уникальным в комбинации «отправитель: получатель: контекст», отправитель может иметь несколько записей с одним и тем же получателем, но в разных контекстах. Как вы планируете визуализацию этих связей?
>Я надеялся, что кто-нибудь поделится опытом.
Для школьного сайта с небольшим посещением такие решения может быть и подходят, не вижу смысла волноваться. Но, в production большого сервиса правильнее будет денормализовать БД. Пусть будет запись близнец, но не 2 запроса в ДБ.
0
user = User.first
another_user = User.last
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
another_user = User.last
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
0
Извините — сорвалось.
0
user = User.first
another_user = User.last
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:live, :me_as=>:friend, :him_as=>:friend)
user.graph_to(another_user, :context=>:sport, :me_as=>:student, :him_as=>:trainer)
user.graph_to(another_user, :context=>:cafe, :me_as=>:barmen, :him_as=>:client)
как душе угодно.
another_user = User.last
user.graph_to(another_user, :context=>:job, :me_as=>:boss, :him_as=>:staff_member)
user.graph_to(another_user, :context=>:live, :me_as=>:friend, :him_as=>:friend)
user.graph_to(another_user, :context=>:sport, :me_as=>:student, :him_as=>:trainer)
user.graph_to(another_user, :context=>:cafe, :me_as=>:barmen, :him_as=>:client)
как душе угодно.
0
Под «визуализацией» я имел ввиду, как всё это будет выглядеть для обычного пользователя вашего сервиса?
Как ему определить контекст связи? Выбрать из вашего огромного списка, возможен мултиселект?
Как ему определить контекст связи? Выбрать из вашего огромного списка, возможен мултиселект?
0
Возможно это страница с заголовками: Мои Друзья, Мои учителя, Мои знакомые, Мои родители, Мои соседи, а под ними ссылки на пользователей или юзерпики.
Кнопку — это мой друг, это мой босс, это мой одноклассник, это моя школа — можно выводить в зависимости от контекста (статуса пользователя). По тысячи других условий.
Я не хочу рассуждать об этом — это больше дело дизайнера, наверное. Я не проектировщих интерфейсов.
Кнопку — это мой друг, это мой босс, это мой одноклассник, это моя школа — можно выводить в зависимости от контекста (статуса пользователя). По тысячи других условий.
Я не хочу рассуждать об этом — это больше дело дизайнера, наверное. Я не проектировщих интерфейсов.
0
Ваше решение больно смахивает на «Списки» (Lists) в друзьях Facebook. Недостаток Ваш, контекст определяется программистом и он статичен для всех пользователей.
0
«Зайка моя я твой зайчик
Ручка моя я твой пальчик
Рыбка моя я твой глазик
Банька моя я твой тазик»
:)
Ручка моя я твой пальчик
Рыбка моя я твой глазик
Банька моя я твой тазик»
:)
+1
Как минимум — RDBMS для этого подходит слабо.
Посмотрите на графовые базы данных, я на все сто уверен, что к наиболее популярным есть привязка из Ruby.
wiki.neo4j.org/content/Ruby
Посмотрите на графовые базы данных, я на все сто уверен, что к наиболее популярным есть привязка из Ruby.
wiki.neo4j.org/content/Ruby
+1
Вконтакте есть «категории», одного человека можно распихнуть сразу в несколько
доступ к альбомам можно ограничивать категориями
не кажется ли надстройка категорий поверх друзей более функциональной?
+в инстант_мессанджере у них еще отдельные категории, никак не связанные с вконтактными
доступ к альбомам можно ограничивать категориями
не кажется ли надстройка категорий поверх друзей более функциональной?
+в инстант_мессанджере у них еще отдельные категории, никак не связанные с вконтактными
+1
Я, например, хотел бы устанавливать связь пользователя со школьным сайтом.
Представьте, кнопка — Я здесь учился!
Выше я приводил пример: u.graph_to(User.find(20), :context=>:school, :me_as=>:student, :him_as=>:school)
где u — текущий пользователь, а User.find(20) — учетная запись администратора школьного сайта.
В социальной сети, по моему мнению под пользователем вполне можно понимать и организацию.
А к какой категории друзей тогда отнести свою школу/компанию?
В посте я попробовал создавать неравнозначные связи в различных контекстах. Как это получилось — это уже другое дело.
Представьте, кнопка — Я здесь учился!
Выше я приводил пример: u.graph_to(User.find(20), :context=>:school, :me_as=>:student, :him_as=>:school)
где u — текущий пользователь, а User.find(20) — учетная запись администратора школьного сайта.
В социальной сети, по моему мнению под пользователем вполне можно понимать и организацию.
А к какой категории друзей тогда отнести свою школу/компанию?
В посте я попробовал создавать неравнозначные связи в различных контекстах. Как это получилось — это уже другое дело.
+1
Сомневаюсь, что предложенный вами вариант лучше хранения в базе инвертированной записи.
0
НЛО прилетело и опубликовало эту надпись здесь
Делается не так, что вот так:
WHERE user_id=1 AND friend_id=5 OR user_id=5 AND friend_id=1
То есть когда вы ищите всех своих друзей, вам нужно пройти по всей таблице дружб и найти все записи, которые accepted и в которых вы находитесь либо в user_id, либо в friend_id. Если есть инвертированная запись, то нужно выбирать только те записи, где я (user_id) равен какому-то значению.
WHERE user_id=1 AND friend_id=5 OR user_id=5 AND friend_id=1
То есть когда вы ищите всех своих друзей, вам нужно пройти по всей таблице дружб и найти все записи, которые accepted и в которых вы находитесь либо в user_id, либо в friend_id. Если есть инвертированная запись, то нужно выбирать только те записи, где я (user_id) равен какому-то значению.
+1
Вот и я сомневаюсь :)
0
Спасибо за пост, интересно почитать.
Немного по руби коду:
1) в times не нужно вручную менять i, а когда i не нужно, можно ее не писать:
2) при объявлении method_missing лучше так же объявлять respond_to? с той же регуляркой (чтобы проверки на наличие метода срабатывали, если кто-то захочет проверить, подробнее www.dcmanges.com/blog/30; понятно, что код в статье на поиграться, но все же):
Немного по руби коду:
1) в times не нужно вручную менять i, а когда i не нужно, можно ее не писать:
10.times { |i| puts i }; 5.times { puts :hello; }
2) при объявлении method_missing лучше так же объявлять respond_to? с той же регуляркой (чтобы проверки на наличие метода срабатывали, если кто-то захочет проверить, подробнее www.dcmanges.com/blog/30; понятно, что код в статье на поиграться, но все же):
def respond_to?(method_name)
/^(.*)_(.*)_from_(.*)$/.match(method_name.to_s) || super
end
+2
но вернувшись из школы (я работаю учителем)
Даёшь больше таких учителей в школы!
+4
Если method_missing(method_name, *args) не находит какой-то метод, то он попытается его распарсить по регулярке.
Если подобный подход применять регулярно и в большом проекте, то глюкабитили проекта будет на высоте. Особенно обрадуются вновь пришедшие в проект разработчики.
0
Увы я не могу определить границы применимости данного метода. Если сможете что-то пояснить на этот счет — то это будет очень хорошо. Но в данном случае альтернатив не вижу.
0
Основные проблемы будут с трудно уловимыми опечатками.
Добавьте сюда написание одних и тех же слов на разных языках, транслитерациях.
Для перцу можно добавить опечатки в разных раскладках, например, job и jоb — для ЭВМ это два разных слова.
Если в команде есть один программист с Пунто-свитчером, то это уже потенциальная бомба замедленного действия.
А на живом запуске проекта ошибки будут накапливаться долго и постепенно, что потом откат бэкапа базы данных не спасет (бэкап тоже будет с глючными записями).
В общем, метод удобный, но работать надо с ним аккуратно.
Добавьте сюда написание одних и тех же слов на разных языках, транслитерациях.
Для перцу можно добавить опечатки в разных раскладках, например, job и jоb — для ЭВМ это два разных слова.
Если в команде есть один программист с Пунто-свитчером, то это уже потенциальная бомба замедленного действия.
А на живом запуске проекта ошибки будут накапливаться долго и постепенно, что потом откат бэкапа базы данных не спасет (бэкап тоже будет с глючными записями).
В общем, метод удобный, но работать надо с ним аккуратно.
0
Мне бы, блин, такого учителя в школе в свое время…
0
вам нужно в калифорнию!
0
у меня тут после 6 уроков и так калифорния.
0
Помимо удвоенного количества селектов, такой метод привносит один очень важный нюанс: эту базу очень сложно разбить на несколько. Например, если одного сервера БД уже не будет хватать.
Существуют, конечно, нереляционные решения, которые позволяют это сделать (MongoDB, например). Но во-первых это прощай джойны, а во-вторых, всё равно один из двух селектов будет глобальным (отправляться на все шарды).
Вывод: инвертированные записи и копирование изменений — необходимая оптимизация в реальном мире.
Существуют, конечно, нереляционные решения, которые позволяют это сделать (MongoDB, например). Но во-первых это прощай джойны, а во-вторых, всё равно один из двух селектов будет глобальным (отправляться на все шарды).
Вывод: инвертированные записи и копирование изменений — необходимая оптимизация в реальном мире.
0
Посмотрите на Диаспору, там реализована подобная «дружба». Добавляется контекст, а потом в него добавляются контакты.
0
вы описали приблизительно то же самое, что делает FlockDB.
вообще мы решали проблему несколькими путями. во-первых, если граф направленый (twitter model), то все немного по-другому. говорим что A => B, а взаимная связь будет уже B => A. Соответственно, чтобы понять, кто с кем «дружит» — нужно сделать запрос с join на себя.
на самом деле, очень удобно для таких вещей использовать Graph DB, такие как, например, neo4j. Там на каждую связь (т.к. база по сути schema-less), можно вешать различиные аттрибуты, и по ним производить поиск и фильтрацию. делать одно и двунаправленные графы. делать traversals, каскадом, рекурсивно — как пожелаете.
посмотрите на github.com/maxdemarzi/neography гем для упрощения работы с neo4j. мы решили его написать в силу того, что через Rest c neo4j общаться не очень удобно, а ближайший аналог — требует jruby.
вообще мы решали проблему несколькими путями. во-первых, если граф направленый (twitter model), то все немного по-другому. говорим что A => B, а взаимная связь будет уже B => A. Соответственно, чтобы понять, кто с кем «дружит» — нужно сделать запрос с join на себя.
на самом деле, очень удобно для таких вещей использовать Graph DB, такие как, например, neo4j. Там на каждую связь (т.к. база по сути schema-less), можно вешать различиные аттрибуты, и по ним производить поиск и фильтрацию. делать одно и двунаправленные графы. делать traversals, каскадом, рекурсивно — как пожелаете.
посмотрите на github.com/maxdemarzi/neography гем для упрощения работы с neo4j. мы решили его написать в силу того, что через Rest c neo4j общаться не очень удобно, а ближайший аналог — требует jruby.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Размышления о реализации социального графа