Существует широко используемая база данных 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
}

Как видите, наша работа сделала две очень важные вещи:

  1. Во-первых, наш код упрощен и намного чище.
  2. Мы создали многоразовую абстракцию

Вывод

Как видите, мы создали удобную вещь, которая представляет собой многоразовую разбивку на страницы для драйвера MongoDB, а также вы узнали, что такое параметры поиска и как их использовать.
Для ясности, мы можем даже создать многоразовые FindOptionsBuilder, который можно использовать для создания более сложного кода многократного использования, если он нам понадобится. Об этом я и напишу в следующей статье.