|
2 | 2 | * Description: Deletes obsolete custom events (not received for >90 days) from Countly and Drill DBs for an app. |
3 | 3 | * Usage: node delete_x_days_inactive_events.js |
4 | 4 | */ |
5 | | - const { ObjectId } = require('mongodb'); |
6 | | - const pluginManager = require('../../../plugins/pluginManager.js'); |
7 | | - const common = require('../../../api/utils/common.js'); |
8 | | - const drillCommon = require('../../../plugins/drill/api/common.js'); |
9 | | -//double-check below details before running the script |
10 | | - const DRY_RUN = true; // Set to false to actually delete |
11 | | - const APP_ID = ""; // Replace with your app's ObjectID string |
12 | | - const DAYS = 90; // Number of days for active events |
13 | | - function daysAgo(days) { |
14 | | - return Math.floor(Date.now() / 1000) - days * 24 * 60 * 60; // eventTimes uses Unix timestamps (seconds) |
15 | | - } |
16 | | - Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("countly_drill")]).then(async function([countlyDb, drillDb]) { |
17 | | - console.log("Connected to databases..."); |
18 | | - common.db = countlyDb; |
19 | | - common.drillDb = drillDb; |
20 | | - try { |
21 | | - const app = await countlyDb.collection("apps").findOne({_id: new ObjectId(APP_ID)}, {_id: 1, name: 1}); |
22 | | - if (!app) { |
23 | | - console.log("App not found"); |
24 | | - return close(); |
25 | | - } |
26 | | - console.log("App:", app.name); |
27 | | - // Get eventTimes data |
28 | | - const eventTimesColl = "eventTimes" + APP_ID; |
29 | | - const allDocs = await countlyDb.collection(eventTimesColl).find({}).toArray(); |
30 | | - |
31 | | - let obsoleteEvents = []; |
32 | | - if (allDocs.length) { |
33 | | - for (const doc of allDocs) { |
34 | | - if (doc.e && Array.isArray(doc.e)) { |
35 | | - for (const eventObj of doc.e) { |
36 | | - // eventObj.e is event key, eventObj.ts is last timestamp (Unix seconds) |
37 | | - if (eventObj.t && Math.floor(eventObj.t / 1000) < daysAgo(DAYS)) { |
38 | | - obsoleteEvents.push(eventObj.e); |
39 | | - } |
40 | | - } |
41 | | - } |
42 | | - } |
43 | | - } |
44 | | - obsoleteEvents = [...new Set(obsoleteEvents)]; |
45 | | - console.log("Obsolete events to delete:", obsoleteEvents); |
46 | | - if (DRY_RUN) { |
47 | | - console.log("DRY_RUN enabled. No changes will be made."); |
48 | | - return close(); |
49 | | - } |
50 | | - if (!obsoleteEvents.length) { |
51 | | - return close("No obsolete events to delete"); |
52 | | - } |
53 | | - // Delete obsolete events |
54 | | - await deleteDrillEvents(app._id, obsoleteEvents); |
55 | | - await deleteCountlyEvents(app._id, obsoleteEvents); |
56 | | - await deleteEventTimes(app._id, obsoleteEvents); |
57 | | - await deleteEventGroups(app._id, obsoleteEvents); |
58 | | - await deleteEventKeys(app._id, obsoleteEvents); |
59 | | - close(); |
60 | | - |
61 | | - } catch (err) { |
62 | | - console.log("App not found or error occurred"); |
63 | | - close(err); |
64 | | - } |
65 | | - async function deleteDrillEvents(appId, events) { |
66 | | - for (let i = 0; i < events.length; i++) { |
67 | | - var collectionName = drillCommon.getCollectionName(events[i], appId); |
68 | | - try { |
69 | | - await drillDb.collection(collectionName).drop(); |
70 | | - console.log("Dropped Drill collection:", collectionName); |
71 | | - } catch (ex) { |
72 | | - // Collection may not exist |
73 | | - console.log("Could not drop collection (may not exist):", collectionName); |
74 | | - } |
75 | | - } |
76 | | - //delete from aggregated drill event collection |
77 | | - await drillDb.collection('drill_events').deleteMany({'a': appId + "", 'e': {$in: events}}); |
78 | | - console.log("Cleared from drill_events"); |
79 | | - await drillDb.collection('drill_bookmarks').deleteMany({'app_id': appId, 'event_key': {$in: events}}); |
80 | | - console.log("Cleared drill_bookmarks"); |
81 | | - await drillDb.collection("drill_meta").deleteMany({'app_id': (appId + ""), "type": "e", "e": {$in: events}}); |
82 | | - console.log("Cleared drill_meta"); |
83 | | - } |
84 | | - async function deleteCountlyEvents(appId, events) { |
85 | | - for (let i = 0; i < events.length; i++) { |
86 | | - var collectionName = 'events' + drillCommon.getEventHash(events[i], appId); |
87 | | - try { |
88 | | - await countlyDb.collection(collectionName).drop(); |
89 | | - console.log("Dropped Countly collection:", collectionName); |
90 | | - } catch (ex) { |
91 | | - // Collection may not exist |
92 | | - console.log("Could not drop collection (may not exist):", collectionName); |
93 | | - } |
94 | | - //clear from merged collection |
95 | | - await countlyDb.collection("events_data").deleteMany({'_id': {"$regex": "^" + appId + "_" + drillCommon.getEventHash(events[i], appId) + "_.*"}}); |
96 | | - console.log("Cleared from aggregated events_data for:", events[i]); |
97 | | - } |
98 | | - } |
99 | | - async function deleteEventTimes(appId, events) { |
100 | | - await countlyDb.collection("eventTimes" + appId).updateMany({}, {"$pull": {"e": {"e": {$in: events}}}}); |
101 | | - console.log("Cleared eventTimes"); |
102 | | - await countlyDb.collection("timelineStatus").deleteMany({'app_id': (appId + ""), "event": {$in: events}}); |
103 | | - console.log("Cleared timelineStatus"); |
104 | | - } |
105 | | - async function deleteEventKeys(appId, events) { |
106 | | - const unsetQuery = {}; |
107 | | - for (let i = 0; i < events.length; i++) { |
108 | | - unsetQuery[`segments.${events[i]}`] = ""; |
109 | | - unsetQuery[`map.${events[i]}`] = ""; |
110 | | - unsetQuery[`omitted_segments.${events[i]}`] = ""; |
111 | | - } |
112 | | - // Remove from "list" and "overview" arrays, and unset event segments/maps |
113 | | - await countlyDb.collection("events").updateOne( |
114 | | - {_id: appId}, |
115 | | - { |
116 | | - $pull: { |
117 | | - list: {$in: events}, |
118 | | - overview: {eventKey: {$in: events}} |
119 | | - }, |
120 | | - $unset: unsetQuery |
121 | | - } |
122 | | - ); |
123 | | - console.log("Cleared events:", events); |
124 | | - } |
125 | | - async function deleteEventGroups(appId, events) { |
126 | | - await countlyDb.collection("event_groups").updateMany({'app_id': (appId + "")}, {"$pull": {"source_events": {$in: events}}}); |
127 | | - console.log("Cleared event_groups"); |
128 | | - } |
129 | | - function close(err) { |
130 | | - if (err) console.log("Finished with errors:", err); |
131 | | - else console.log("Finished successfully."); |
132 | | - countlyDb.close(); |
133 | | - drillDb.close(); |
134 | | - } |
135 | | - }); |
| 5 | +const { ObjectId } = require('mongodb'); |
| 6 | +const pluginManager = require('../../../plugins/pluginManager.js'); |
| 7 | +const common = require('../../../api/utils/common.js'); |
| 8 | +const drillCommon = require('../../../plugins/drill/api/common.js'); |
| 9 | +// double-check below details before running the script |
| 10 | +const DRY_RUN = true; // Set to false to actually delete |
| 11 | +const APP_ID = ""; // Replace with your app's ObjectID string |
| 12 | +const DAYS = 90; // Number of days for active events |
| 13 | +function daysAgo(days) { |
| 14 | + // eventTimes uses Unix timestamps (seconds) |
| 15 | + return Math.floor(Date.now() / 1000) - days * 24 * 60 * 60; |
| 16 | +} |
| 17 | +Promise.all([pluginManager.dbConnection("countly"), pluginManager.dbConnection("countly_drill")]).then(async function([countlyDb, drillDb]){ |
| 18 | + console.log("Connected to databases..."); |
| 19 | + common.db = countlyDb; |
| 20 | + common.drillDb = drillDb; |
| 21 | + try { |
| 22 | + const app = await countlyDb.collection("apps").findOne({ _id: new ObjectId(APP_ID) }, { _id: 1, name: 1 }); |
| 23 | + if (!app) { |
| 24 | + console.log("App not found"); |
| 25 | + return close(); |
| 26 | + } |
| 27 | + console.log("App:", app.name); |
| 28 | + // Get eventTimes data |
| 29 | + const eventTimesColl = "eventTimes" + APP_ID; |
| 30 | + const allDocs = await countlyDb.collection(eventTimesColl).find({}).toArray(); |
| 31 | + |
| 32 | + let obsoleteEvents = []; |
| 33 | + if (allDocs.length) { |
| 34 | + for (const doc of allDocs) { |
| 35 | + if (doc.e && Array.isArray(doc.e)) { |
| 36 | + for (const eventObj of doc.e) { |
| 37 | + // eventObj.e is event key, eventObj.ts is last timestamp (Unix seconds) |
| 38 | + if (eventObj.t && Math.floor(eventObj.t / 1000) < daysAgo(DAYS)) { |
| 39 | + obsoleteEvents.push(eventObj.e); |
| 40 | + } |
| 41 | + } |
| 42 | + } |
| 43 | + } |
| 44 | + } |
| 45 | + obsoleteEvents = [...new Set(obsoleteEvents)]; |
| 46 | + console.log("Obsolete events to delete:", obsoleteEvents); |
| 47 | + if (DRY_RUN) { |
| 48 | + console.log("DRY_RUN enabled. No changes will be made."); |
| 49 | + return close(); |
| 50 | + } |
| 51 | + if (!obsoleteEvents.length) { |
| 52 | + return close("No obsolete events to delete"); |
| 53 | + } |
| 54 | + // Delete obsolete events |
| 55 | + await deleteDrillEvents(app._id, obsoleteEvents); |
| 56 | + await deleteCountlyEvents(app._id, obsoleteEvents); |
| 57 | + await deleteEventTimes(app._id, obsoleteEvents); |
| 58 | + await deleteEventGroups(app._id, obsoleteEvents); |
| 59 | + await deleteEventKeys(app._id, obsoleteEvents); |
| 60 | + close(); |
| 61 | + } catch (err) { |
| 62 | + console.log("App not found or error occurred"); |
| 63 | + close(err); |
| 64 | + } |
| 65 | + async function deleteDrillEvents(appId, events) { |
| 66 | + for (let i = 0; i < events.length; i++) { |
| 67 | + var collectionName = drillCommon.getCollectionName(events[i], appId); |
| 68 | + try { |
| 69 | + await drillDb.collection(collectionName).drop(); |
| 70 | + console.log("Dropped Drill collection:", collectionName); |
| 71 | + } catch (ex) { |
| 72 | + // Collection may not exist |
| 73 | + console.log("Could not drop collection (may not exist):", collectionName); |
| 74 | + } |
| 75 | + } |
| 76 | + // delete from aggregated drill event collection |
| 77 | + await drillDb.collection('drill_events').deleteMany({ 'a': appId + "", 'e': { $in: events } }); |
| 78 | + console.log("Cleared from drill_events"); |
| 79 | + await drillDb.collection('drill_bookmarks').deleteMany({ 'app_id': appId, 'event_key': { $in: events } }); |
| 80 | + console.log("Cleared drill_bookmarks"); |
| 81 | + await drillDb.collection("drill_meta").deleteMany({ 'app_id': (appId + ""), "type": "e", "e": { $in: events } }); |
| 82 | + console.log("Cleared drill_meta"); |
| 83 | + } |
| 84 | + async function deleteCountlyEvents(appId, events) { |
| 85 | + for (let i = 0; i < events.length; i++) { |
| 86 | + var collectionName = 'events' + drillCommon.getEventHash(events[i], appId); |
| 87 | + try { |
| 88 | + await countlyDb.collection(collectionName).drop(); |
| 89 | + console.log("Dropped Countly collection:", collectionName); |
| 90 | + } catch (ex) { |
| 91 | + // Collection may not exist |
| 92 | + console.log("Could not drop collection (may not exist):", collectionName); |
| 93 | + } |
| 94 | + // clear from merged collection |
| 95 | + await countlyDb.collection("events_data").deleteMany({ '_id': { "$regex": "^" + appId + "_" + drillCommon.getEventHash(events[i], appId) + "_.*" } }); |
| 96 | + console.log("Cleared from aggregated events_data for:", events[i]); |
| 97 | + } |
| 98 | + } |
| 99 | + async function deleteEventTimes(appId, events) { |
| 100 | + await countlyDb.collection("eventTimes" + appId).updateMany({}, { "$pull": { "e": { "e": { $in: events } } } }); |
| 101 | + console.log("Cleared eventTimes"); |
| 102 | + await countlyDb.collection("timelineStatus").deleteMany({ 'app_id': (appId + ""), "event": { $in: events } }); |
| 103 | + console.log("Cleared timelineStatus"); |
| 104 | + } |
| 105 | + async function deleteEventKeys(appId, events) { |
| 106 | + const unsetQuery = {}; |
| 107 | + for (let i = 0; i < events.length; i++) { |
| 108 | + unsetQuery[`segments.${events[i]}`] = ""; |
| 109 | + unsetQuery[`map.${events[i]}`] = ""; |
| 110 | + unsetQuery[`omitted_segments.${events[i]}`] = ""; |
| 111 | + } |
| 112 | + // Remove from "list" and "overview" arrays, and unset event segments/maps |
| 113 | + await countlyDb.collection("events").updateOne( |
| 114 | + { _id: appId }, |
| 115 | + { |
| 116 | + $pull: { |
| 117 | + list: { $in: events }, |
| 118 | + overview: { eventKey: { $in: events } } |
| 119 | + }, |
| 120 | + $unset: unsetQuery |
| 121 | + } |
| 122 | + ); |
| 123 | + console.log("Cleared events:", events); |
| 124 | + } |
| 125 | + async function deleteEventGroups(appId, events) { |
| 126 | + await countlyDb.collection("event_groups").updateMany({ 'app_id': (appId + "") }, { "$pull": { "source_events": { $in: events } } }); |
| 127 | + console.log("Cleared event_groups"); |
| 128 | + } |
| 129 | + function close(err) { |
| 130 | + if (err) { |
| 131 | + console.log("Finished with errors:", err); |
| 132 | + } else { |
| 133 | + console.log("Finished successfully."); |
| 134 | + } |
| 135 | + countlyDb.close(); |
| 136 | + drillDb.close(); |
| 137 | + } |
| 138 | +}); |
0 commit comments