Существует широко используемая база данных NoSql под названием MongoDB, и я собираюсь показать вам, как легко вы можете создать многоразовую разбивку на страницы, которая сделает ваш код намного чище.
На прошлой неделе я писал о создании многоразовой разбивки на страницы в Golang и Gorm, но есть еще одна база данных, которая используется очень часто, под названием MongoDB. Ясно, написать эту многоразовую разбивку на страницы легко, как в Gorm.
Допустим, у нас есть эта функция репозитория:
func (o offerRepo) GetPagedAndFilteredAuthorized(limit, page int, authorId uint) ([]Offer, error) { result := make([]Offer, 0) coll := o.db.Database(mongoDatabaseName).Collection(offerCollectionName) ctx, _ := context.WithTimeout(context.Background(), contextTimeout) l := int64(limit) skip := int64(page * limit - limit) fOpt := options.FindOptions{Limit: &l, Skip: &skip} curr, err := coll.Find(ctx, bson.D{{"author_id", authorId}}, &fOpt) if err != nil { return result, err } for curr.Next(ctx) { var el Offer if err := curr.Decode(&el); err != nil { log.Println(err) } result = append(result, el) } return result, nil }
Конечно, эта функция не так чиста, как использование ORM (например, GORM), но не так уж и плоха. По сути, мы создаем параметры, которые будут использоваться при запросе данных.
- «L» - это предел (сколько записей необходимо получить)
- «Skip» представляет, сколько записей должно быть пропущено, поэтому мы всегда будем пропускать столько записей, сколько страниц умножено на количество записей минус ограничение (поэтому для page = 1 мы пропускаем 0 записей, для page = 2 мы пропускаем например 2 * 10–10 записей)
Хорошо, эта функция работает, но есть код, который можно повторно использовать во многих местах, когда мы запрашиваем данные из MongoDB. Как программисты, мы должны упростить и повторно использовать большой объем кода, это наша работа! 😄
По сути, функция coll.find () может принимать бесконечное количество параметров поиска , чтобы вы могли догадаться, куда мы идем!
Прежде всего, нам нужно создать структуру (я знаю, это можно сделать с помощью единственной функции, но создание структуры как абстракции удобно, если нам не нужна очень хорошая производительность)
type mongoPaginate struct { limit int64 page int64 }
Мы хотим избежать утечки пакетов, поэтому делаем эту структуру частной, доступной только внутри текущего пакета. Затем мы собираемся создать конструктивную функцию (называемую конструктором! 😆):
func newMongoPaginate(limit, page int) *mongoPaginate { return &mongoPaginate{ limit: int64(limit), page: int64(page), } }
Мы используем int64, потому что MongoDB findOptions принимает только тип int64 (я предполагаю, что причина этого в том, что в нашей коллекции может быть много документов, а запросы могут быть намного больше). < br /> Последняя функция:
func (mp *mongoPaginate) getPaginatedOpts() *options.FindOptions { l := mp.limit skip := mp.page*mp.limit - mp.limit fOpt := options.FindOptions{Limit: &l, Skip: &skip} return &fOpt }
В этом мы создаем findOptions с ограничением и пропускаем то же самое, что и в начале, и возвращаем ссылку на этот объект.
Итак, использование этой абстракции выглядит так:
New version: func (o offerRepo) GetPagedAndFilteredAuthorized(limit, page int, authorId uint) ([]Offer, error) { result := make([]Offer, 0) coll := o.db.Database(mongoDatabaseName).Collection(offerCollectionName) ctx, _ := context.WithTimeout(context.Background(), contextTimeout) curr, err := coll.Find(ctx, bson.D{{"author_id", authorId}}, newMongoPaginate(limit,page).getPaginatedOpts()) if err != nil { return result, err } for curr.Next(ctx) { var el Offer if err := curr.Decode(&el); err != nil { log.Println(err) } result = append(result, el) } return result, nil } Old version: func (o offerRepo) GetPagedAndFilteredAuthorized(limit, page int, authorId uint) ([]Offer, error) { result := make([]Offer, 0) coll := o.db.Database(mongoDatabaseName).Collection(offerCollectionName) ctx, _ := context.WithTimeout(context.Background(), contextTimeout) l := int64(limit) skip := int64(page * limit - limit) fOpt := options.FindOptions{Limit: &l, Skip: &skip} curr, err := coll.Find(ctx, bson.D{{"author_id", authorId}}, &fOpt) if err != nil { return result, err } for curr.Next(ctx) { var el Offer if err := curr.Decode(&el); err != nil { log.Println(err) } result = append(result, el) } return result, nil }
Как видите, наша работа сделала две очень важные вещи:
- Во-первых, наш код упрощен и намного чище.
- Мы создали многоразовую абстракцию
Вывод
Как видите, мы создали удобную вещь, которая представляет собой многоразовую разбивку на страницы для драйвера MongoDB, а также вы узнали, что такое параметры поиска и как их использовать.
Для ясности, мы можем даже создать многоразовые FindOptionsBuilder, который можно использовать для создания более сложного кода многократного использования, если он нам понадобится. Об этом я и напишу в следующей статье.