Запрос ниже:

  • сопоставить подборку документов с соответствием
  • группировать по дням месяца, а затем с помощью аккумулятора добавить к возвращаемому набору данных URL-адреса и счетчики

Проблема в том, что запрос довольно сложен, и по мере увеличения объема данных это действительно не кажется очень производительным. Есть ли более простой способ добиться того же в mongodb?

Форма вывода выглядит так:

{
  "results": [
    {
      "_id": {
        "month": 2,
        "day": 2,
        "year": 2021
      },
      "urls": [
        {
          "url": "https://shop.mydomain.com/product/golden-axe",
          "count": 20
        },
        {
          "url": "https://shop.mydomain.com/product/phantasy-star",
          "count": 218
        },
        {
          "url": "https://shop.mydomain.com/product/sega-arcades-retro",
          "count": 30
        }
      ],
      "count": 268
    },
    {
      "_id": {
        "month": 2,
        "day": 3,
        "year": 2021
      },
      "urls": [
        {
          "url": "https://shop.mydomain.com/product/golden-axe",
          "count": 109
        },
        {
          "url": "https://shop.mydomain.com/product/phantasy-star",
          "count": 416
        },
        {
          "url": "https://shop.mydomain.com/product/sega-arcades-retro",
          "count": 109
        }
      ],
      "count": 634
    },
        const aggregate = [
      {
        $match: {
          source: 'itemLineView',
          createdAt: {
            $gte: new Date(query.dateGT),
            $lte: new Date(query.dateLT)
          },
          url: { $regex: `^${query.url}` },
        }
      },
      {
        $group: {
          _id: {
            month: {
              $month: '$createdAt'
            },
            day: {
              $dayOfMonth: '$createdAt'
            },
            year: {
              $year: '$createdAt'
            }
          },
          urls: {
            // https://docs.mongodb.com/manual/reference/operator/aggregation/accumulator/#grp._S_accumulator
            $accumulator: {
              init: function (): AccumulatorSourceStats {
                return {
                  origins: []
                };
              },
              // initArgs: [], // Argument arr to pass to the init function
              accumulate: function (state: AccumulatorSourceStats, url: string) {
                const index = state.origins.findIndex(function (origin) {
                  return origin.url === url;
                });
                if (index === -1) {
                  state.origins.push({
                    url: url,
                    count: 1
                  });
                } else {
                  ++state.origins[index].count;
                }
                return state;
              },

              accumulateArgs: ['$url'], // Argument(s) passed to the accumulate function

              merge: function (state1: AccumulatorSourceStats, state2: AccumulatorSourceStats) {
                return {
                  origins: state1.origins.concat(state2.origins)
                };
              },

              finalize: function (state: AccumulatorSourceStats) { // Adjust the state to only return field we need
                const sortByUrl = function (a: AccumulatorSourceStatsOrigin, b: AccumulatorSourceStatsOrigin) {
                  if (a.url < b.url) {
                    return -1;
                  }
                  if (a.url > b.url) {
                    return 1;
                  }
                  return 0;
                };
                return state.origins.sort(sortByUrl);
              },

              lang: 'js'
            }
          },
          count: { $sum: 1 }
        }
      },
      { $sort: { _id: 1 } }
    ];
    return this.model.aggregate(aggregate);
2
John 17 Фев 2021 в 11:58

1 ответ

Лучший ответ

Из документов:

Выполнение JavaScript внутри оператора агрегирования может снизить производительность. Используйте оператор $ аккумулятор, только если предоставленные операторы конвейера не могут удовлетворить потребности вашего приложения.

Считается хорошей практикой избегать использования кода javascript в конвейере Mongo, это следует использовать только в крайнем случае. И в этом случае мы можем избежать его использования, просто введя $group дважды, один раз для каждого URL в день, а затем один раз в день. вот так:

db.collection.aggregate([
  {
    $match: {
      source: "itemLineView",
      createdAt: {
        $gte: new Date(query.dateGT),
        $lte: new Date(query.dateLT)
      },
      url: {
        $regex: `^${query.url}`
      },
      
    }
  },
  {
    $group: {
      _id: {
        month: {
          $month: "$createdAt"
        },
        day: {
          $dayOfMonth: "$createdAt"
        },
        year: {
          $year: "$createdAt"
        },
        url: "$url"
      },
      count: {
        $sum: 1
      }
    }
  },
  {
    $group: {
      _id: {
        month: "$_id.month",
        day: "$_id.day",
        year: "$_id.year",
        
      },
      urls: {
        $push: {
          url: "$_id.url",
          count: "$count"
        }
      },
      count: {
        $sum: "$count"
      }
    }
  }
])

Монго Детская площадка

2
Tom Slabbaert 17 Фев 2021 в 10:27