För närvarande samlar nästan alla företag i världen in statistik om användaråtgärder på en webbresurs. Motivationen är tydlig – företag vill veta hur deras produkt/webbplats används och bättre förstå sina användare. Naturligtvis finns det ett stort antal verktyg på marknaden för att lösa detta problem - från analyssystem som tillhandahåller data i form av instrumentpaneler och grafer (till exempel,
Men vi hittade ett problem som inte är löst än. Så född
Varför ska vi utveckla vår egen tjänst?
Det var nittiotalet, vi överlevde så gott vi kunde. 2019 utvecklade vi First Customer Data Platform API kSense, som gjorde det möjligt att samla data från olika källor (Facebook-annonser, Stripe, Salesforce, Google play, Google Analytics, etc.) för mer bekväm dataanalys, identifiera beroenden, etc. Vi har märkt att många användare använder vår dataanalysplattform, specifikt Google Analytics (nedan kallat GA). Vi pratade med några användare och fick reda på att de behöver deras produktanalysdata, som de får med hjälp av GA, men
De installerade en Segment javascript-pixel på sin webbresurs och deras användarbeteendedata laddades in i en specificerad databas (t.ex. Postgres). Men Segment har också sitt minus – priset. Till exempel, om en webbresurs har 90,000 1,000 MTU (månadsspårade användare), måste du betala ~ $ XNUMX XNUMX per månad till kassan. Det fanns också ett tredje problem - vissa webbläsartillägg (som AdBlock) blockerade insamlingen av analyser. http-förfrågningar från webbläsaren skickades till GA- och segmentdomänerna. Utifrån våra kunders önskemål har vi skapat en analystjänst som samlar in en komplett uppsättning data (utan provtagning), kostnadsfritt och kan köras på vår egen infrastruktur.
Hur tjänsten fungerar
Tjänsten består av tre delar: en javascript-pixel (som vi senare skrev om till typescript), en serverdel implementerad i GO-språket, och det var planerat att använda Redshift och BigQuery som en intern databas (senare lade de till stöd för Postgres , ClickHouse och Snowflake).
Händelsestrukturen GA och Segment beslutade att lämna oförändrade. Allt som behövdes var att duplicera alla händelser från webbresursen där pixeln är installerad till vår backend. Som det visar sig är detta lätt att göra. Javascript-pixeln åsidosatte den ursprungliga GA-biblioteksmetoden med en ny som duplicerade händelsen i vårt system.
//'ga' - стандартное название переменной Google Analytics
if (window.ga) {
ga(tracker => {
var originalSendHitTask = tracker.get('sendHitTask');
tracker.set('sendHitTask', (model) => {
var payLoad = model.get('hitPayload');
//отправка оригинального события в GA
originalSendHitTask(model);
let jsonPayload = this.parseQuery(payLoad);
//отправка события в наш сервис
this.send3p('ga', jsonPayload);
});
});
}
Med Segment-pixeln är allt enklare, det har middleware-metoder, och vi använde en av dem.
//'analytics' - стандартное название переменной Segment
if (window.analytics) {
if (window.analytics.addSourceMiddleware) {
window.analytics.addSourceMiddleware(chain => {
try {
//дублирование события в наш сервис
this.send3p('ajs', chain.payload);
} catch (e) {
LOG.warn('Failed to send an event', e)
}
//отправка оригинального события в Segment
chain.next(chain.payload);
});
} else {
LOG.warn("Invalid interceptor state. Analytics js initialized, but not completely");
}
} else {
LOG.warn('Analytics.js listener is not set.');
}
Förutom att kopiera händelser har vi lagt till möjligheten att skicka godtyckliga json:
//Отправка событий с произвольным json объектом
eventN.track('product_page_view', {
product_id: '1e48fb70-ef12-4ea9-ab10-fd0b910c49ce',
product_price: 399.99,
price_currency: 'USD'
product_release_start: '2020-09-25T12:38:27.763000Z'
});
Låt oss sedan prata om serversidan. Backend bör acceptera http-förfrågningar, fyll dem med ytterligare information, till exempel geodata (tack
//входящий json
{
"field_1": {
"sub_field_1": "text1",
"sub_field_2": 100
},
"field_2": "text2",
"field_3": {
"sub_field_1": {
"sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
}
}
}
//результат
{
"field_1_sub_field_1": "text1",
"field_1_sub_field_2": 100,
"field_2": "text2",
"field_3_sub_field_1_sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
}
Men arrayer konverteras för närvarande helt enkelt till strängar. inte alla relationsdatabaser stöder upprepade fält. Det är också möjligt att ändra fältnamn eller ta bort dem med valfria mappningsregler. De låter dig ändra dataschemat, om det behövs, eller casta en datatyp till en annan. Till exempel, om json-fältet innehåller en sträng med en tidsstämpel (field_3_sub_field_1_sub_sub_field_1 från exemplet ovan), för att skapa ett fält i databasen med tidsstämpeltypen måste du skriva en mappningsregel i konfigurationen. Med andra ord bestäms datatypen för fältet först av json-värdet och sedan tillämpas regeln för typcasting (om den är konfigurerad). Vi har identifierat fyra huvuddatatyper: STRING, FLOAT4, INT64 och TIMESTAMP. Mappnings- och castingreglerna ser ut så här:
rules:
- "/field_1/subfield_1 -> " #правило удаления поля
- "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
- "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа
Algoritm för att bestämma datatypen:
- konvertera json-strukturen till platt struktur
- bestämma datatypen för fält genom värden
- tillämpa kartläggning och typgjutningsregler
Sedan från den inkommande json-strukturen:
{
"product_id": "1e48fb70-ef12-4ea9-ab10-fd0b910c49ce",
"product_price": 399.99,
"price_currency": "USD",
"product_type": "supplies",
"product_release_start": "2020-09-25T12:38:27.763000Z",
"images": {
"main": "picture1",
"sub": "picture2"
}
}
dataschema kommer att erhållas:
"product_id" character varying,
"product_price" numeric (38,18),
"price_currency" character varying,
"product_type" character varying,
"product_release_start" timestamp,
"images_main" character varying,
"images_sub" character varying
Vi tänkte också att användaren skulle kunna ställa in partitionering eller dela data i databasen enligt andra kriterier och implementerade möjligheten att ställa in tabellnamnet som en konstant eller
tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'
Strukturen för inkommande händelser kan dock ändras under körning. Vi har implementerat en algoritm för att kontrollera skillnaden mellan strukturen för en befintlig tabell och strukturen för en inkommande händelse. Om en skillnad hittas kommer tabellen att uppdateras med nya fält. För att göra detta, använd patch SQL-frågan:
#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying
Arkitektur
Varför behöver du skriva händelser till filsystemet, och inte bara skriva dem direkt till databasen? Databaser visar inte alltid hög prestanda med ett stort antal inlägg (
Öppen källkod och framtidsplaner
Vid något tillfälle blev tjänsten som en fullfjädrad produkt och vi bestämde oss för att lägga den i Open Source. För tillfället har integrationer med Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake implementerats. Alla integrationer stöder både batch- och strömmande dataladdningslägen. Lade till stöd för förfrågningar via API.
Det nuvarande integrationsschemat ser ut så här:
Även om tjänsten kan användas oberoende (till exempel med Docker), har vi också
→
→
→
Vi blir glada om EventNative hjälper dig att lösa dina problem!
Endast registrerade användare kan delta i undersökningen.
Vilket statistikinsamlingssystem som används i ditt företag
-
48,0%Google Analytics12
-
4,0%Segment1
-
16,0%Annat (skriv i kommentarerna) 4
-
32,0%Implementerade din tjänst8
25 användare röstade. 6 användare avstod från att rösta.
Källa: will.com