Sleek

Build Status Code Climate

Sleek is a gem for doing analytics. It allows you to easily collect and analyze events that happen in your app.

Sleek is a work-in-progress development. Use with caution.

Installation

The easiest way to install Sleek is to add it to your Gemfile:

gem "sleek"

Or, if you want the latest hotness:

gem "sleek", github: "goshakkk/sleek"

Then, install it:

$ bundle install

Sleek requires MongoDB to work and assumes that you have Mongoid configured already.

Finally, create needed indexes:

$ rake db:mongoid:create_indexes

Getting started

Namespacing

Namespaces are a great way to organize entirely different buckets of data inside a single application. In Sleek, everything is namespaced.

Creating a namespaced instance of Sleek is easy:

sleek = Sleek[:my_namespace]

You then would just call everything on this instance.

Sending an Event

The heart of analytics is in recording events. Events are things that happen in your app that you want to track. Events are stored in event buckets.

In order to send an event, you would simply need to call sleek.record, passing the event bucket name and the event payload.

sleek.record(:purchases, {
  customer: { id: 1, name: "First Last", email: "first@last.com" },
  items: [{ sku: "TSTITM1", name: "Test Item 1", price: 1999 }],
  total: 1999
})

Analyzing Events

Simple count

There are a few methods of analyzing your data. The simplest one is counting. It, you guessed it, would count how many times the event has occurred.

sleek.queries.count(:purchases)
# => 42

Average

In order to calculate average value, it's needed to additionally specify what property should the average be calculated based on:

sleek.queries.average(:purchases, target_property: :total)
# => 1999

Query with timeframe

You can limit the scope of events that analysis is run on by adding the :timeframe option to any query call.

sleek.queries.count(:purchases, timeframe: :this_day)
# => 10

Query with interval

Some kinds of applications may need to analyze trends in the data. Using intervals, you can break a timeframe into minutes, hours, days, weeks, or months. One can do so by passing the :interval option to any query call. Using :interval also requires that you specify :timeframe.

sleek.queries.count(:purchases, timeframe: :this_2_days, interval: :daily)
# => [
#      {:timeframe=>2013-01-01 00:00:00 UTC..2013-01-02 00:00:00 UTC, :value=>10},
#      {:timeframe=>2013-01-02 00:00:00 UTC..2013-01-03 00:00:00 UTC, :value=>24}
#    ]

Data analysis in more detail

Metrics

The word "metrics" is used to describe analysis queries which return a single numeric value.

Count

Count just counts the number of events recorded.

sleek.queries.count(:bucket)
# => 42

Count unique

It counts how many events have an unique value for a given property.

sleek.queries.count_unique(:bucket, params)

You must pass the target property name in params like this:

sleek.queries.count_unique(:purchases, target_property: "customer.id")
# => 30

Minimum

It finds the minimum numeric value for a given property. All non-numeric values are ignored. If none of property values are numeric, nil will be returned.

sleek.queries.minimum(:bucket, params)

You must pass the target property name in params like this:

sleek.queries.minimum(:purchases, target_property: "total")
# => 10_99

Maximum

It finds the maximum numeric value for a given property. All non-numeric values are ignored. If none of property values are numeric, nill will be returned.

sleek.queries.maximum(:bucket, params)

You must pass the target property name in params like this:

sleek.queries.maximum(:purchases, target_property: "total")
# => 199_99

Average

The average query finds the average value for a given property. All non-numeric values are ignored. If none of property values are numeric, nil will be returned.

sleek.queries.average(:bucket, params)

You must pass the target property name in params like this:

sleek.queries.average(:purchases, target_property: "total")
# => 49_35

Sum

The sum query sums all the numeric values for a given property. All non-numeric values are ignored. If none of property values are numeric, nil will be returned.

sleek.queries.sum(:bucket, params)

You must pass the target property name in params like this:

sleek.queries.sum(:purchases, target_property: "total")
# => 2_072_70

Series

Series allow you to analyze trends in metrics over time. They break a timeframe into intervals and compute the metric for those intervals.

Calculating series is simply done by adding the :timeframe and :interval options to the metric query.

Valid intervals are:

  • :hourly
  • :daily
  • :weekly
  • :monthly

Group by

In addition to using metrics and series, it is sometimes desired to group their outputs by a specific property value.

For example, you might be wondering, "How much have me made from each of our customers?" Group by will help you answer questions like this.

To group metrics or series result by value of some property, all you need to do is to pass the :group_by option to the query.

sleek.queries.sum(:purchases, target_property: "total", group_by: "customer.email")
# => {"first@another.com"=>214998, "first@last.com"=>64999}

Or, you may wonder how much did you make from each of your customers for every day of this week.

sleek.queries.sum(:purchases, target_property: "total", timeframe: :this_week,
  interval: :daily, group_by: "customer.email")

You can even combine it with filters. For example, how much did you make from each of your customers for evey day of this weeks on orders greater than $1000?

sleek.queries.sum(:purchases, target_property: "total", filter: ["total", :gte, 1000_00],
  timeframe: :this_week, interval: :daily, group_by: "customer.email")

Filters

To limit the scope of events used in analysis you can use a filter. To do so, you just pass the :filter option to the query.

A single filter is a 3-element array, consisting of:

  • property_name - the property name to filter.
  • operator - the name of the operator to apply.
  • value - the value used in operator to compare to property value.

Operators: eq, ne, lt, lte, gt, gte, in.

You can pass either a single filter or an array of filters.

sleek.queries.count(:purchases, filters: [:total, :gt, 1599])
# => 20

Timeframe & timezone

You can pass the :timeframe with or without :timezone to any query.

Timeframe is used to limit your query by some window of time. You can use a range of TimeWithRange objects to specify absolute timeframe, or you can use a string that describes relative timeframe.

Relative timeframe string (or a symbol) consists of these parts: category, optional number, and interval specification. Possible categories are this and previous, possible intervals are minute, hour, day, week, month.

Examples: this_day, previous_3_weeks.

By default, relative times are transformed into ranges of time objects in UTC timezone. You can, however, pass the :timezone option to tell Sleek to construct the window of time in the given timezone.

Refer to ActiveSupport::TimeZone docs for more details on possible timezone identifiers.

Other

Deleting namespace

sleek.delete!

Deleting buckets

sleek.delete_bucket(:purchases)

Deleting property from all events in the bucket

sleek.delete_property(:purchases, :some_property)

License

MIT.