@huloglobal/vendure-plugin-visitor-analytics 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -15
- package/dist/abandoned-cart.controller.d.ts +78 -0
- package/dist/abandoned-cart.controller.d.ts.map +1 -0
- package/dist/abandoned-cart.controller.js +251 -0
- package/dist/abandoned-cart.controller.js.map +1 -0
- package/dist/abandoned-cart.entity.d.ts +61 -0
- package/dist/abandoned-cart.entity.d.ts.map +1 -0
- package/dist/abandoned-cart.entity.js +143 -0
- package/dist/abandoned-cart.entity.js.map +1 -0
- package/dist/abandoned-cart.scanner.d.ts +34 -0
- package/dist/abandoned-cart.scanner.d.ts.map +1 -0
- package/dist/abandoned-cart.scanner.js +85 -0
- package/dist/abandoned-cart.scanner.js.map +1 -0
- package/dist/abandoned-cart.service.d.ts +92 -0
- package/dist/abandoned-cart.service.d.ts.map +1 -0
- package/dist/abandoned-cart.service.js +276 -0
- package/dist/abandoned-cart.service.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -1
- package/dist/index.js.map +1 -1
- package/dist/journey-buffs.controller.d.ts +43 -0
- package/dist/journey-buffs.controller.d.ts.map +1 -0
- package/dist/journey-buffs.controller.js +79 -0
- package/dist/journey-buffs.controller.js.map +1 -0
- package/dist/journey-buffs.service.d.ts +53 -0
- package/dist/journey-buffs.service.d.ts.map +1 -0
- package/dist/journey-buffs.service.js +141 -0
- package/dist/journey-buffs.service.js.map +1 -0
- package/dist/plugin.d.ts +5 -0
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +28 -3
- package/dist/plugin.js.map +1 -1
- package/dist/product-co-view.entity.d.ts +19 -0
- package/dist/product-co-view.entity.d.ts.map +1 -0
- package/dist/product-co-view.entity.js +52 -0
- package/dist/product-co-view.entity.js.map +1 -0
- package/dist/recommendations.controller.d.ts +72 -0
- package/dist/recommendations.controller.d.ts.map +1 -0
- package/dist/recommendations.controller.js +123 -0
- package/dist/recommendations.controller.js.map +1 -0
- package/dist/recommendations.service.d.ts +62 -0
- package/dist/recommendations.service.d.ts.map +1 -0
- package/dist/recommendations.service.js +167 -0
- package/dist/recommendations.service.js.map +1 -0
- package/dist/search-analytics.controller.d.ts +36 -0
- package/dist/search-analytics.controller.d.ts.map +1 -0
- package/dist/search-analytics.controller.js +86 -0
- package/dist/search-analytics.controller.js.map +1 -0
- package/dist/search-analytics.service.d.ts +36 -0
- package/dist/search-analytics.service.d.ts.map +1 -0
- package/dist/search-analytics.service.js +105 -0
- package/dist/search-analytics.service.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.SearchAnalyticsService = void 0;
|
|
13
|
+
const common_1 = require("@nestjs/common");
|
|
14
|
+
const core_1 = require("@vendure/core");
|
|
15
|
+
/**
|
|
16
|
+
* Zero-schema-cost search analytics: reads back over the existing
|
|
17
|
+
* `visitor_event` table where the storefront has posted `type='event'`,
|
|
18
|
+
* `eventType='search'` custom events, and aggregates them per query
|
|
19
|
+
* string.
|
|
20
|
+
*
|
|
21
|
+
* The storefront just needs to fire:
|
|
22
|
+
* hulo.search('rgb keyboard', 42) // (query, resultsCount)
|
|
23
|
+
*
|
|
24
|
+
* Everything else lives in the read side.
|
|
25
|
+
*/
|
|
26
|
+
let SearchAnalyticsService = class SearchAnalyticsService {
|
|
27
|
+
constructor(connection) {
|
|
28
|
+
this.connection = connection;
|
|
29
|
+
}
|
|
30
|
+
/** Top search queries in the window, by volume. */
|
|
31
|
+
async topQueries(sinceDays = 7, channelId = 1, limit = 50) {
|
|
32
|
+
const since = new Date(Date.now() - sinceDays * 86400000);
|
|
33
|
+
const rows = await this.connection.rawConnection.query(`SELECT
|
|
34
|
+
LOWER(TRIM(BOTH '"' FROM SUBSTRING_INDEX(SUBSTRING_INDEX(meta, '"query":"', -1), '"', 1))) AS query,
|
|
35
|
+
COUNT(*) AS searches,
|
|
36
|
+
AVG(CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(meta, '"resultsCount":', -1), ',', 1) AS UNSIGNED)) AS avgResults
|
|
37
|
+
FROM visitor_event
|
|
38
|
+
WHERE type = 'event'
|
|
39
|
+
AND meta LIKE '%"eventType":"search"%'
|
|
40
|
+
AND channelId = ?
|
|
41
|
+
AND createdAt >= ?
|
|
42
|
+
GROUP BY query
|
|
43
|
+
HAVING query <> ''
|
|
44
|
+
ORDER BY searches DESC
|
|
45
|
+
LIMIT ?`, [channelId, since, Math.min(Math.max(1, limit), 500)]);
|
|
46
|
+
return rows.map(r => ({
|
|
47
|
+
query: String(r.query || ''),
|
|
48
|
+
searches: Number(r.searches),
|
|
49
|
+
avgResults: Math.round(Number(r.avgResults || 0) * 10) / 10,
|
|
50
|
+
}));
|
|
51
|
+
}
|
|
52
|
+
/** Zero-result queries — catalogue-gap intel. */
|
|
53
|
+
async zeroResultQueries(sinceDays = 7, channelId = 1, limit = 50) {
|
|
54
|
+
const since = new Date(Date.now() - sinceDays * 86400000);
|
|
55
|
+
const rows = await this.connection.rawConnection.query(`SELECT
|
|
56
|
+
LOWER(TRIM(BOTH '"' FROM SUBSTRING_INDEX(SUBSTRING_INDEX(meta, '"query":"', -1), '"', 1))) AS query,
|
|
57
|
+
COUNT(*) AS searches
|
|
58
|
+
FROM visitor_event
|
|
59
|
+
WHERE type = 'event'
|
|
60
|
+
AND meta LIKE '%"eventType":"search"%'
|
|
61
|
+
AND meta LIKE '%"resultsCount":0%'
|
|
62
|
+
AND channelId = ?
|
|
63
|
+
AND createdAt >= ?
|
|
64
|
+
GROUP BY query
|
|
65
|
+
HAVING query <> ''
|
|
66
|
+
ORDER BY searches DESC
|
|
67
|
+
LIMIT ?`, [channelId, since, Math.min(Math.max(1, limit), 500)]);
|
|
68
|
+
return rows.map(r => ({ query: String(r.query || ''), searches: Number(r.searches) }));
|
|
69
|
+
}
|
|
70
|
+
/** Search-to-cart-add conversion — of sessions that searched, what
|
|
71
|
+
* fraction went on to fire an add_to_cart. Coarse, but the direction
|
|
72
|
+
* of movement is what matters for catalogue tuning. */
|
|
73
|
+
async searchToAddRate(sinceDays = 7, channelId = 1) {
|
|
74
|
+
const since = new Date(Date.now() - sinceDays * 86400000);
|
|
75
|
+
const rows = await this.connection.rawConnection.query(`SELECT
|
|
76
|
+
COUNT(DISTINCT ve.sessionId) AS sessionsSearched,
|
|
77
|
+
COUNT(DISTINCT CASE WHEN adds.sessionId IS NOT NULL THEN ve.sessionId END) AS sessionsAdded
|
|
78
|
+
FROM visitor_event ve
|
|
79
|
+
LEFT JOIN (
|
|
80
|
+
SELECT DISTINCT sessionId FROM visitor_event
|
|
81
|
+
WHERE type = 'event'
|
|
82
|
+
AND meta LIKE '%"eventType":"add_to_cart"%'
|
|
83
|
+
AND channelId = ?
|
|
84
|
+
AND createdAt >= ?
|
|
85
|
+
) adds ON adds.sessionId = ve.sessionId
|
|
86
|
+
WHERE ve.type = 'event'
|
|
87
|
+
AND ve.meta LIKE '%"eventType":"search"%'
|
|
88
|
+
AND ve.channelId = ?
|
|
89
|
+
AND ve.createdAt >= ?`, [channelId, since, channelId, since]);
|
|
90
|
+
const r = (rows === null || rows === void 0 ? void 0 : rows[0]) || {};
|
|
91
|
+
const searched = Number(r.sessionsSearched || 0);
|
|
92
|
+
const added = Number(r.sessionsAdded || 0);
|
|
93
|
+
return {
|
|
94
|
+
sessionsSearched: searched,
|
|
95
|
+
sessionsAdded: added,
|
|
96
|
+
conversion: searched ? Math.round((added / searched) * 1000) / 10 : 0,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
exports.SearchAnalyticsService = SearchAnalyticsService;
|
|
101
|
+
exports.SearchAnalyticsService = SearchAnalyticsService = __decorate([
|
|
102
|
+
(0, common_1.Injectable)(),
|
|
103
|
+
__metadata("design:paramtypes", [core_1.TransactionalConnection])
|
|
104
|
+
], SearchAnalyticsService);
|
|
105
|
+
//# sourceMappingURL=search-analytics.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-analytics.service.js","sourceRoot":"","sources":["../src/search-analytics.service.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huloglobal/vendure-plugin-visitor-analytics",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Full-funnel visitor analytics for Vendure storefronts \u2014 pageviews, time-on-page, session journey, exit pages, funnel drop-off, and per-visitor profile drawer with parsed user-agent + MaxMind GeoLite2 enrichment. Auto-issues visitor + session cookies on first request; logs guest and signed-in events against the same visitor id so the journey survives login.",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"author": "Wayne Garrison <wayne@garrison.me.uk>",
|