В настоящее время создание аналитической панели управления критически важно для любого бизнеса. Хотя подписка на Google Analytics является очевидным выбором, иногда нам может потребоваться отслеживать события на более детальном уровне или создавать собственную панель мониторинга. Cassandra отлично подходит для написания аналитического движка, но добавляет дополнительный уровень сложности работы. Это где Parse приходит.
Parse — это IaaS от Facebook. Вы можете использовать его как полнофункциональное хранилище данных, не тратя время на инфраструктуру. В этой статье я собираюсь объяснить, как использовать Parse для создания собственной аналитической панели мониторинга. Читай дальше.
Начиная
В качестве основы мы будем использовать пример приложения, которое я создал для одной из моих предыдущих статей. Вы можете скачать это здесь . Это приложение использует mongoid и HAML, но приведенные здесь примеры также должны работать с Active Record и ERB. С этим из нашего пути, давайте установим основы.
Сначала создайте бесплатную учетную запись с помощью Parse и настройте в ней приложение. Вам понадобится ключ приложения и ключ Javascript, которые вы можете найти на вкладке «Настройки».
Создайте новый файл javascript analytics.js
// app/assets/javascripts/analytics.js
var CustomAnalytics = {
init: function (type){
Parse.initialize(APP_ID, JS_KEY);
}
}
и включите это в свой макет верхнего уровня:
# app/views/application.html.haml
!!!
%html
%head
%title Build Custom analytics with Parse
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true
= javascript_include_tag 'application', 'data-turbolinks-track' => true
= javascript_include_tag 'vendor/parse', 'data-turbolinks-track' => true
= javascript_include_tag 'analytics', 'data-turbolinks-track' => true
= csrf_meta_tags
%body
= yield
:javascript
// Initialize the Analytics first
CustomAnalytics.init();
Мы создали пространство имен с именем CustomAnalytics
init
Это предпочтительнее, чем инициализация Parse внутри представления, поскольку при желании вы можете инициализировать несколько поставщиков аналитики, таких как Google или Mixpanel.
Теперь наше приложение готово к общению с серверами Parse.
ПРИМЕЧАНИЕ. Parse использует модель подписки на основе использования. Возможно, вы захотите проверить их тарифные планы до его реализации.
Отслеживание событий
Прежде чем показать, как создать собственный механизм аналитики, давайте сначала взглянем на встроенную библиотеку отслеживания событий Parse, которая похожа на события Google. Это может помочь отследить произвольные события в приложении до удержания пользователя без особой работы с нашей стороны.
В примере приложения есть 2 страницы: одна показывает список категорий, а другая — языки. Допустим, я хочу отслеживать, сколько пользователей нажимают на категории:
# app/views/category/index.html.haml
%h1
Category Listing
%ul#categories
- @categories.each do |cat|
%li
%a{:href=>"/category/#{cat['id']}", :class=>'js-category-click'}
%h3
= cat["name"]
%p
= cat["desc"]
и добавьте это в файл макетов:
# app/views/layouts/application.html.haml
//.......
:javascript
CustomAnalytics.init();
$( '.js-category' ).on('click', function(e){
e.preventDefault();
var url = e.currentTarget.href;
Parse.Analytics.track( 'CATEGORY_CLICK', {
'target': 'category',
}).then(function(){
window.location.href = url;
});
});
//.............
Здесь мы используем встроенный метод track
Требуется 2 параметра: имя события и размеры. Измерения — это пользовательские точки данных, которые мы можем передавать, которые могут быть использованы позже для фильтрации отчетов. Этот метод возвращает обещание. Мы можем выполнить успешный обратный вызов, как только это будет выполнено, в данном случае, перенаправив на исходную ссылку.
Мы должны сделать то же самое для отслеживания событий на языковой странице. Но это много повторяющегося кода. Давайте рефакторинг этого потока кода.
Добавьте это в свой файл analytics.js
// app/assets/javascripts/analytics.js
var CustomAnalytics = {
//...
track: function( name, dimensions, successCallback ){
Parse.Analytics.track( name, dimensions )
.then( successCallback );
}
//...
}
И измените код отслеживания в вашем файле category.js
# app/views/layouts/application.html.haml
//.......
:javascript
//.......
$( '.js-category' ).on('click', function(e){
e.preventDefault();
var url = e.currentTarget.href;
CustomAnalytics.track( 'CATEGORY_CLICK', {
'target': 'category',
}, function(){
window.location.href = url;
})
});
//.......
Мы передаем те же параметры методу отслеживания. Поначалу это может показаться не очень много, но это уменьшает объем стандартного кода, особенно когда на вашей странице много событий.
Чтобы просмотреть отслеживаемые события, перейдите в Analytics -> Events на панели инструментов Parse.
В качестве пользовательского хранилища данных
Мы можем использовать облачные данные Parse для хранения наших пользовательских данных. Он работает очень похоже на хранилище данных NoSQL и довольно гибкий.
Для начала создайте новый класс с именем CategoryClicks
Data
В вашем application.html.haml
//.........
function trackCloudData( name, id, type ){
var CategoryClicks = Parse.Object.extend('CategoryClicks'),
cloud_data = new CategoryClicks();
//Custom data
cloud_data.name = name
cloud_data.type = type
cloud_data.id = id
// This syncs the data with the server
cloud_data.save();
}
//..........
$( '.js-category' ).on('click', function(e){
e.preventDefault();
var $elem = $(e.currentTarget),
url = $elem.url,
name = $elem.data('name'),
id = $elem.data('id');
CustomAnalytics.track( 'CATEGORY_CLICK', {
'target': 'category',
}, function(){
window.location.href = url;
});
trackCloudData(name, id, type);
});
//.........
Parse.Object
Это простая модель Backbone, и мы можем установить для нее пользовательские атрибуты. Когда вы сохраняете эту модель, она синхронизирует данные с сервером.
Теперь все данные, которые вы отслеживали, доступны на панели инструментов Parse.
Хранить и получать
Если мы создаем приложение реального времени, мы можем использовать JS API Parse для извлечения данных с сервера. Но для построения панели временных рядов это не сработает. Нам нужно собрать эту информацию из Parse и преобразовать позже в соответствии с нашими потребностями.
Официального Ruby-клиента для Parse не существует, но замечательный gem parse-ruby-client
прекрасно подходит.
Добавьте этот драгоценный камень в Gemfile:
# Gemfile
gem 'parse-ruby-client'
После завершения bundle install
# app/models/category_analytics.rb
class CategoryAnalytics
include Mongoid::Document
include Mongoid::Timestamps
field :category_id, type: BSON::ObjectId
field :name, type: String
field :count, type: Integer
field :date, type: DateTime
end
Напишите простое задание, которое пройдет по всем категориям и получит запрос на чтение за указанную дату. Поскольку это происходит по сетевому вызову, было бы лучше, если бы мы обрабатывали это асинхронно через resque .
И создайте новую задачу Resque, щелкните по категории aggregator.rb :
# lib/tasks/category_click_aggregator.rb
class CategoryClickAggregator
@queue = :category_analytics
def self.perform()
Parse.init(:application_id => "APP_ID", :api_key => "API_KEY")
categories = Category.all
yesterday = Date.yesterday
start_date = Parse::Date.new(yesterday.to_time)
end_date = Parse::Date.new(Date.today.to_time)
# Convert the dates to Parse date to play nice with their API
categories.each do |cat|
count = Parse::Query.new("BookHistory").tap do |q|
q.eq("category_id", cat.id)
q.greater_eq("createdAt", start_date)
q.less_eq("createdAt", end_date)
end.get.count
# See if this exists already
category_analytics = CategoryAnalytics.find_by(:category_id => cat.id, :date => yesterday )
if category_analytics.nil?
category_analytics = CategoryAnalytics.new
category_analytics.name = cat.name
category_analytics.category_id = cat.id
category_analytics.date = yesterday
end
category_analytics.count = count
category_analytics.save
end
end
end
Модуль Parse::Query
POST
Затем мы получаем результаты, агрегируем их и сохраняем в локальной базе данных для поддержки генерации отчетов временных рядов.
Это просто простая демонстрация того, как извлечь данные из Parse. Однако в производстве я бы рекомендовал запускать отдельное задание, которое проходит по всем категориям и ставит задания в очередь индивидуально. Таким образом, задачи могут быть возобновлены, когда они терпят неудачу вместо всего банка. Кроме того, по мере роста данных мы можем порождать нескольких рабочих и выполнять их параллельно.
Ограничения
Все запросы к Parse разбиты на страницы, и по умолчанию ограничение на количество страниц составляет 100. Если у вас более 100 записей в наборе результатов, приведенный выше код вернет неверные результаты. Мы можем увеличить лимит вручную до 1000, но это все равно постигнет та же участь, что и ваши события.
Чтобы исправить это должным образом, нам придется прибегнуть к уродливому циклу do..while
total_count = 0
offset = 0
loop do
count = Parse::Query.new("BookHistory").tap do |q|
q.eq("category_id", cat.id)
q.greater_eq("createdAt", start_date)
q.less_eq("createdAt", end_date)
q.limit(1000)
q.offset(offset)
end.get.count
total_count+= count
offset++
break if count < 1000
end
Завершение
Parse — отличный инструмент, и мы едва поцарапали его поверхность. Например, мы можем использовать его для аутентификации и отслеживания пользователей, использования заданий для запуска пользовательских заданий в облаке и настройки веб-хуков. Надеюсь, я заинтересовался этой статьей. Код, используемый в этой статье, доступен здесь . Не стесняйтесь присоединиться к обсуждению в комментариях.