npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@brunatali/pg-aggregates

v0.2.3

Published

Enhanced aggregates support for PostGraphile

Downloads

5

Readme

@brunatali/pg-aggregates

Same pluging from @graphile/pg-aggregates incuding:

  • Customizable time zone

  • Interval type corrections

  • Interval type new options (secondsInt, iso, isoShort and raw)
    Note. ISO representations in the 8601 standard.

  • Group by keys was changed from array to object (>v0.2).
    Warning. This feature is a breaking change, update with caution.

  • Pagination by parent configuration first, offset and orderBy
    Warning. Cursors has been implemented, but not extensively tested, use with caution.

@graphile/pg-aggregates

Click here to read original description from @graphile/pg-aggregates.

Usage

TIMEZONE

timezone is just applicable for groupBy item using column that represents time, date or both.
It's value must be entered as string, representing hour. Eg:

  • '03'
  • '11'
  • '-04'
  • '-10'

You can issue a GraphQL query such as:

query GameAggregates {
  allPlayers {
    groupedAggregates(groupBy: CREATED_AT_TRUNCATED_TO_DAY, timezone: "03") {
      distinctCount {
        goals
      }
    }
  }
}

INTERVAL

With interval corrections, now is perfectly possible to retrieve an average field by issue this GraphQL query:

query PlayersAggregates {
  allPlayers {
    groupedAggregates(groupBy: PLAYER_NAME) {
      average {
        gameTime {
          seconds
          secondsInt
          minutes
          hours
          days
          months
          years
          iso
          isoShort
          raw
        }
      }
    }
  }
}

Environment variable

Is accepted to set a default time zone by placing GROUP_BY_AGGREGATE_TIMEZONE to .env file.

GROUP_BY_AGGREGATE_TIMEZONE=-03

KEYS

By using this enhanced keys, you be able to list any kind of values:

  • Numbers
  • Strings
  • Arrays
  • Objects
  • Null

Considering we have 5 players distributed into male and female, with three kinds of ages, 25, 29 and 30 years old...

An query issued as:

query GameAggregates {
  allPlayers {
    groupedAggregates(groupBy: [PLAYER_GENDER, PLAYER_AGE]) {
      keys
    }
    totalCount
  }
}

Will return something like this:

{
  "data": {
    "allPlayers": {
      "groupedAggregates": [
        {
          "keys": {
            "playerGender": "female",
            "playerAge": 25
          }
        },
        {
          "keys": {
            "playerGender": "female",
            "playerAge": 29
          }
        },
        {
          "keys": {
            "playerGender": "male",
            "playerAge": 30
          }
        }
      ],
      "totalCount": 5
    }
  }
}

Defining your own grouping derivatives

You may add your own derivatives by adding a group by spec to build.pgAggregateGroupBySpecs via a plugin. Derivative specs are fairly straightforward, for example here's the spec for "truncated-to-hour":

const DATE_OID = "1082";
const TIMESTAMP_OID = "1114";
const TIMESTAMPTZ_OID = "1184";

const truncatedToHourSpec = {
  // [IMPORTANT] This new feature require that sql builder knows the column
  // type during the process. Passing this to query builder will makes the engine
  // to add correct time zone formatter.
  isTimestampLike: true,

  // A unique identifier for this spec, will be used to generate its name:
  id: "truncated-to-hour",

  // A filter to determine which column/function return types this derivative
  // is valid against:
  isSuitableType: (pgType) =>
    pgType.id === DATE_OID ||
    pgType.id === TIMESTAMP_OID ||
    pgType.id === TIMESTAMPTZ_OID,

  // The actual derivative - given the SQL fragment `sqlFrag` which represents
  // the column/function call, return a new SQL fragment that represents the
  // derived value, in this case a truncated timestamp:
  sqlWrap: (sqlFrag) => sql.fragment`date_trunc('hour', ${sqlFrag})`,
};

Building that up with a few more different intervals into a full PostGraphile plugin, you might write something like:

// Constants from PostgreSQL
const DATE_OID = "1082";
const TIMESTAMP_OID = "1114";
const TIMESTAMPTZ_OID = "1184";

// Produce an indexable list of date_trunc fields
// Other values: microseconds, milliseconds, second, minute, quarter,
// decade, century, millennium.
// See https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-TRUNC
const dateInterval = {
  year: {
    id: 1,
    name: "year",
  },
  month: {
    id: 2,
    name: "month",
  },
  week: {
    id: 3,
    name: "week",
  },
  day: {
    id: 4,
    name: "day",
  },
  hour: {
    id: 5,
    name: "hour",
  },
};

// Build a spec that truncates to the given interval
const tsTruncateSpec = (sql, interval) => ({
  id: `truncated-to-${interval}`,
  isSuitableType: (pgType) => {
    // Determine if a given type is a timestamp/timestamptz
    if (pgType.id === TIMESTAMP_OID || pgType.id === TIMESTAMPTZ_OID) {
      return true;
    }
    // Date type columns must not truncate to `day` or `hour`
    if (pgType.id === DATE_OID && interval.id <= dateInterval.week.id) {
      return true;
    }

    return false;
  },
  sqlWrap: (sqlFrag) =>
    sql.fragment`date_trunc(${sql.literal(interval)}, ${sqlFrag})`,
  isTimestampLike: true,
});

// This is the PostGraphile plugin; see:
// https://www.graphile.org/postgraphile/extending/
const DateTruncAggregateGroupSpecsPlugin = (builder) => {
  builder.hook("build", (build) => {
    const { pgSql: sql } = build;

    build.pgAggregateGroupBySpecs = [
      // Copy all existing specs, except the ones we're replacing
      ...build.pgAggregateGroupBySpecs.filter(
        (spec) => !["truncated-to-day", "truncated-to-hour"].includes(spec.id)
      ),

      // Add our timestamp specs
      ...Object.entries(dateInterval).map(([, dateField]) =>
        truncateBySpec(sql, dateField)
      ),
    ];

    return build;
  });
};