MongoDB хранение пользовательских данных в общих объектах коллекции

#mongodb #schema #mongoid

#mongodb #схема #mongoid

Вопрос:

Я разрабатываю приложение, которое обрабатывает RSS-каналы с использованием MongoDB. В настоящее время мои коллекции следующие:

 Entry
fields: content, feed_id, title, publish_date, url

Feed
fields: description, title, url

User
fields: email_address
subscriptions (embedded collection; fields: feed_id, tags)
  

Пользователь может подписаться на каналы, которые связаны из встроенной коллекции подписки. Из подписок я могу получить список всех каналов, которые должен видеть пользователь, а также соответствующие записи.

Как я должен хранить информацию о статусе записи (isRead, isStarred и т.д.), Специфичную для пользователя? Когда пользователь просматривает запись, которую мне нужно записать, isRead = 1. Два распространенных запроса, которые мне нужно выполнить, это:

  • Найдите все записи для определенного канала, где isRead = 0 или статус не существует в данный момент
  • Для конкретного пользователя отметьте все записи до даты публикации с помощью isRead = 1 (это могут быть сотни или даже тысячи записей, поэтому это должно быть эффективно)

Ответ №1:

Хм, это сложная задача!

Для меня имеет смысл сохранять записи для непрочитанных записей и удалять их при чтении. Я основываю это на предположении, что для каждого отдельного пользователя будет больше прочитанных сообщений, чем непрочитанных, поэтому у вас также может не быть документов для всех этих уже прочитанных записей, которые вечно хранятся в вашей БД. Это также упрощает задачу — не беспокоиться об ограничении размера документа в 16 МБ, если вам не нужно повсюду таскать с собой многолетнюю историю.

Для выделенных записей я бы просто добавил массив идентификаторов объектов записи для пользователя. Нет необходимости привязывать их к подписке; будет намного проще получить список элементов, которые пользователь выделил таким образом.

Для непрочитанных записей это немного сложнее. Я бы все равно добавил его в виде массива, но чтобы удовлетворить ваше требование о возможности быстрой пометки записей как прочитанных до определенной даты, я бы денормализовал и сохранил дату публикации вместе с идентификатором объекта записи в новом документе ‘UnreadEntry’.

 User
fields: email_address, starred_entries[]
subscriptions (embedded collection; fields: feed_id, tags, unread_entries[])

UnreadEntry
fields: id is Entry ObjectId, publish_date
  

Вы должны помнить об ограничении документа, но 16 МБ — это чертовски много непрочитанных записей / каналов, поэтому будьте реалистичны в отношении того, действительно ли это ограничение, о котором вам нужно беспокоиться. (Если это так, должно быть довольно просто отключить User.subscriptions от своего собственного документа.)

Теперь оба ваших запроса становятся довольно простыми в написании:

Все непрочитанные записи для определенного канала: user.subscriptions.find(feedID).unread_entries

Отметьте все записи до даты публикации, прочитанные: user.subscriptions.find(feedID).unread_entries.where(publish_date.lte => my_date).delete_all

И, конечно, если вам просто нужно пометить все записи в ленте как прочитанные, это очень просто: user.subscriptions.find(feedID).unread_entries.delete_all