Skip to content

Commit f8df14e

Browse files
committed
Merge pull request #62 from amplitude/revert-61-revert-60-set_group
Set Group
2 parents 6ba8ca2 + c861efe commit f8df14e

19 files changed

+864
-63
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## Unreleased
22

3+
* Add support for setting groups for users and events. See the [Readme](https://github.com/amplitude/Amplitude-Javascript#setting-groups) for more information.
34
* Add `logRevenueV2` and new `Revenue` class to support logging revenue events with properties, and revenue type. See [Readme](https://github.com/amplitude/Amplitude-Javascript#tracking-revenue) for more info.
45
* Add helper method to regenerate a new random deviceId. This can be used to anonymize a user after they log out. Note this is not recommended unless you know what are you doing. See [Readme](https://github.com/amplitude/Amplitude-Javascript#logging-out-and-anonymous-users) for more information.
56

README.md

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ This Readme will guide you through using Amplitude's Javascript SDK to track use
1818
return this}}var o=function(){this._q=[];return this};var a=["add","append","clearAll","prepend","set","setOnce","unset"];
1919
for(var u=0;u<a.length;u++){i(o,a[u])}n.Identify=o;var c=function(){this._q=[];return this;
2020
};var p=["setProductId","setQuantity","setPrice","setRevenueType","setEventProperties"];
21-
for(var l=0;l<p.length;l++){i(c,p[l])}n.Revenue=c;var d=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","logRevenueV2","regenerateDeviceId"];
21+
for(var l=0;l<p.length;l++){i(c,p[l])}n.Revenue=c;var d=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","setGroup","logRevenueV2","regenerateDeviceId"];
2222
function v(e){function t(t){e[t]=function(){e._q.push([t].concat(Array.prototype.slice.call(arguments,0)));
2323
}}for(var n=0;n<d.length;n++){t(d[n])}}v(n);e.amplitude=n})(window,document);
2424
@@ -266,6 +266,29 @@ amplitude.init('YOUR_API_KEY_HERE', null, {
266266
# Advanced #
267267
This SDK automatically grabs useful data about the browser, including browser type and operating system version.
268268
269+
### Setting Groups ###
270+
271+
Amplitude supports assigning users to groups, and performing queries such as Count by Distinct on those groups. An example would be if you want to group your users based on what organization they are in by using an orgId. You can designate Joe to be in orgId 10, while Sue is in orgId 15. When performing an event segmentation query, you can then select Count by Distinct orgIds to query the number of different orgIds that have performed a specific event. As long as at least one member of that group has performed the specific event, that group will be included in the count. See our help article on [Count By Distinct]() for more information.
272+
273+
When setting groups you need to define a `groupType` and `groupName`(s). In the above example, 'orgId' is a `groupType`, and the value 10 or 15 is the `groupName`. Another example of a `groupType` could be 'sport' with `groupNames` like 'tennis', 'baseball', etc.
274+
275+
You can use `setGroup(groupType, groupName)` to designate which groups a user belongs to. Note: this will also set the `groupType`: `groupName` as a user property. **This will overwrite any existing groupName value set for that user's groupType, as well as the corresponding user property value.** `groupType` is a string, and `groupName` can be either a string or an array of strings to indicate a user being in multiple groups (for example Joe is in orgId 10 and 16, so the `groupName` would be [10, 16]).
276+
277+
```javascript
278+
amplitude.setGroup('orgId', '15');
279+
amplitude.setGroup('sport', ['soccer', 'tennis']);
280+
```
281+
282+
You can also use `logEventWithGroups` to set event-level groups, meaning the group designation only applies for the specific event being logged and does not persist on the user (unless you explicitly set it with `setGroup`).
283+
284+
```javascript
285+
var eventProperties = {
286+
'key': 'value'
287+
}
288+
289+
amplitude.logEventWithGroups('initialize_game', eventProperties, {'sport': 'soccer'});
290+
```
291+
269292
### Setting Version Name ###
270293
By default, no version name is set. You can specify a version name to distinguish between different versions of your site by calling `setVersionName`:
271294

amplitude-segment-snippet.min.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
(function(e,t){var r=e.amplitude||{_q:[]};function n(e,t){e.prototype[t]=function(){
2-
this._q.push([t].concat(Array.prototype.slice.call(arguments,0)));return this}}var i=function(){
3-
this._q=[];return this};var s=["add","append","clearAll","prepend","set","setOnce","unset"];
4-
for(var o=0;o<s.length;o++){n(i,s[o])}r.Identify=i;var a=function(){this._q=[];return this;
5-
};var c=["setProductId","setQuantity","setPrice","setRevenueType","setEventProperties"];
6-
for(var u=0;u<c.length;u++){n(a,c[u])}r.Revenue=a;var l=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","logRevenueV2","regenerateDeviceId"];
7-
function p(e){function t(t){e[t]=function(){e._q.push([t].concat(Array.prototype.slice.call(arguments,0)));
8-
}}for(var r=0;r<l.length;r++){t(l[r])}}p(r);e.amplitude=r})(window,document);
2+
this._q.push([t].concat(Array.prototype.slice.call(arguments,0)));return this}}var s=function(){
3+
this._q=[];return this};var i=["add","append","clearAll","prepend","set","setOnce","unset"];
4+
for(var o=0;o<i.length;o++){n(s,i[o])}r.Identify=s;var a=function(){this._q=[];return this;
5+
};var u=["setProductId","setQuantity","setPrice","setRevenueType","setEventProperties"];
6+
for(var c=0;c<u.length;c++){n(a,u[c])}r.Revenue=a;var p=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","setGroup","logRevenueV2","regenerateDeviceId"];
7+
function l(e){function t(t){e[t]=function(){e._q.push([t].concat(Array.prototype.slice.call(arguments,0)));
8+
}}for(var r=0;r<p.length;r++){t(p[r])}}l(r);e.amplitude=r})(window,document);

amplitude-snippet.min.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ s.parentNode.insertBefore(r,s);function i(e,t){e.prototype[t]=function(){this._q
55
return this}}var o=function(){this._q=[];return this};var a=["add","append","clearAll","prepend","set","setOnce","unset"];
66
for(var u=0;u<a.length;u++){i(o,a[u])}n.Identify=o;var c=function(){this._q=[];return this;
77
};var p=["setProductId","setQuantity","setPrice","setRevenueType","setEventProperties"];
8-
for(var l=0;l<p.length;l++){i(c,p[l])}n.Revenue=c;var d=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","logRevenueV2","regenerateDeviceId"];
8+
for(var l=0;l<p.length;l++){i(c,p[l])}n.Revenue=c;var d=["init","logEvent","logRevenue","setUserId","setUserProperties","setOptOut","setVersionName","setDomain","setDeviceId","setGlobalUserProperties","identify","clearUserProperties","setGroup","logRevenueV2","regenerateDeviceId"];
99
function v(e){function t(t){e[t]=function(){e._q.push([t].concat(Array.prototype.slice.call(arguments,0)));
1010
}}for(var n=0;n<d.length;n++){t(d[n])}}v(n);e.amplitude=n})(window,document);

amplitude.js

Lines changed: 131 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,17 @@ Amplitude.prototype.init = function init(apiKey, opt_userId, opt_config, opt_cal
199199
// validate event properties for unsent events
200200
for (var i = 0; i < this._unsentEvents.length; i++) {
201201
var eventProperties = this._unsentEvents[i].event_properties;
202+
var groups = this._unsentEvents[i].groups;
202203
this._unsentEvents[i].event_properties = utils.validateProperties(eventProperties);
204+
this._unsentEvents[i].groups = utils.validateGroups(groups);
203205
}
204206

205207
// validate user properties for unsent identifys
206208
for (var j = 0; j < this._unsentIdentifys.length; j++) {
207209
var userProperties = this._unsentIdentifys[j].user_properties;
210+
var identifyGroups = this._unsentIdentifys[j].groups;
208211
this._unsentIdentifys[j].user_properties = utils.validateProperties(userProperties);
212+
this._unsentIdentifys[j].groups = utils.validateGroups(identifyGroups);
209213
}
210214

211215
this._sendEventsIfReady(); // try sending unsent events
@@ -658,6 +662,31 @@ Amplitude.prototype.setUserId = function setUserId(userId) {
658662
}
659663
};
660664

665+
/**
666+
* Add user to a group or groups. You need to specify a groupType and groupName(s).
667+
* For example you can group people by their organization.
668+
* In that case groupType is "orgId" and groupName would be the actual ID(s).
669+
* groupName can be a string or an array of strings to indicate a user in multiple gruups.
670+
* You can also call setGroup multiple times with different groupTypes to track multiple types of groups (up to 5 per app).
671+
* Note: this will also set groupType: groupName as a user property.
672+
* See the [SDK Readme]{@link https://github.com/amplitude/Amplitude-Javascript#setting-groups} for more information.
673+
* @public
674+
* @param {string} groupType - the group type (ex: orgId)
675+
* @param {string|list} groupName - the name of the group (ex: 15), or a list of names of the groups
676+
* @example amplitude.setGroup('orgId', 15); // this adds the current user to orgId 15.
677+
*/
678+
Amplitude.prototype.setGroup = function(groupType, groupName) {
679+
if (!this._apiKeySet('setGroup()') || !utils.validateInput(groupType, 'groupType', 'string') ||
680+
utils.isEmptyString(groupType)) {
681+
return;
682+
}
683+
684+
var groups = {};
685+
groups[groupType] = groupName;
686+
var identify = new Identify().set(groupType, groupName);
687+
this._logEvent(Constants.IDENTIFY_EVENT, null, null, identify.userPropertiesOperations, groups, null);
688+
};
689+
661690
/**
662691
* Sets whether to opt current user out of tracking.
663692
* @public
@@ -790,7 +819,9 @@ Amplitude.prototype.identify = function(identify_obj, opt_callback) {
790819
if (identify_obj instanceof Identify) {
791820
// only send if there are operations
792821
if (Object.keys(identify_obj.userPropertiesOperations).length > 0) {
793-
return this._logEvent(Constants.IDENTIFY_EVENT, null, null, identify_obj.userPropertiesOperations, opt_callback);
822+
return this._logEvent(
823+
Constants.IDENTIFY_EVENT, null, null, identify_obj.userPropertiesOperations, null, opt_callback
824+
);
794825
}
795826
} else {
796827
utils.log('Invalid identify input type. Expected Identify object but saw ' + type(identify_obj));
@@ -804,7 +835,7 @@ Amplitude.prototype.identify = function(identify_obj, opt_callback) {
804835
/**
805836
* Set a versionName for your application.
806837
* @public
807-
* @param {string} versionName
838+
* @param {string} versionName - The version to set for your application.
808839
* @example amplitude.setVersionName('1.12.3');
809840
*/
810841
Amplitude.prototype.setVersionName = function setVersionName(versionName) {
@@ -818,7 +849,7 @@ Amplitude.prototype.setVersionName = function setVersionName(versionName) {
818849
* Private logEvent method. Keeps apiProperties from being publicly exposed.
819850
* @private
820851
*/
821-
Amplitude.prototype._logEvent = function _logEvent(eventType, eventProperties, apiProperties, userProperties, callback) {
852+
Amplitude.prototype._logEvent = function _logEvent(eventType, eventProperties, apiProperties, userProperties, groups, callback) {
822853
_loadCookieData(this); // reload cookie before each log event to sync event meta-data between windows and tabs
823854
if (!eventType || this.options.optOut) {
824855
if (type(callback) === 'function') {
@@ -845,6 +876,7 @@ Amplitude.prototype._logEvent = function _logEvent(eventType, eventProperties, a
845876
userProperties = userProperties || {};
846877
apiProperties = apiProperties || {};
847878
eventProperties = eventProperties || {};
879+
groups = groups || {};
848880
var event = {
849881
device_id: this.options.deviceId,
850882
user_id: this.options.userId,
@@ -866,7 +898,8 @@ Amplitude.prototype._logEvent = function _logEvent(eventType, eventProperties, a
866898
name: 'amplitude-js',
867899
version: version
868900
},
869-
sequence_number: sequenceNumber // for ordering events and identifys
901+
sequence_number: sequenceNumber, // for ordering events and identifys
902+
groups: utils.truncate(utils.validateGroups(groups))
870903
// country: null
871904
};
872905

@@ -927,7 +960,33 @@ Amplitude.prototype.logEvent = function logEvent(eventType, eventProperties, opt
927960
}
928961
return -1;
929962
}
930-
return this._logEvent(eventType, eventProperties, null, null, opt_callback);
963+
return this._logEvent(eventType, eventProperties, null, null, null, opt_callback);
964+
};
965+
966+
/**
967+
* Log an event with eventType, eventProperties, and groups. Use this to set event-level groups.
968+
* Note: the group(s) set only apply for the specific event type being logged and does not persist on the user
969+
* (unless you explicitly set it with setGroup).
970+
* See the [SDK Readme]{@link https://github.com/amplitude/Amplitude-Javascript#setting-groups} for more information
971+
* about groups and Count by Distinct on the Amplitude platform.
972+
* @public
973+
* @param {string} eventType - name of event
974+
* @param {object} eventProperties - (optional) an object with string keys and values for the event properties.
975+
* @param {object} groups - (optional) an object with string groupType: groupName values for the event being logged.
976+
* groupName can be a string or an array of strings.
977+
* @param {Amplitude~eventCallback} opt_callback - (optional) a callback function to run after the event is logged.
978+
* Note: the server response code and response body from the event upload are passed to the callback function.
979+
* @example amplitude.logEventWithGroups('Clicked Button', null, {'orgId': 24});
980+
*/
981+
Amplitude.prototype.logEventWithGroups = function(eventType, eventProperties, groups, opt_callback) {
982+
if (!this._apiKeySet('logEventWithGroup()') ||
983+
!utils.validateInput(eventType, 'eventType', 'string')) {
984+
if (type(opt_callback) === 'function') {
985+
opt_callback(0, 'No request sent');
986+
}
987+
return -1;
988+
}
989+
return this._logEvent(eventType, eventProperties, null, null, groups, opt_callback);
931990
};
932991

933992
/**
@@ -989,7 +1048,7 @@ Amplitude.prototype.logRevenue = function logRevenue(price, quantity, product) {
9891048
special: 'revenue_amount',
9901049
quantity: quantity || 1,
9911050
price: price
992-
});
1051+
}, null, null, null);
9931052
};
9941053

9951054
/**
@@ -2298,8 +2357,8 @@ var validateProperties = function validateProperties(properties) {
22982357
var key = property;
22992358
var keyType = type(key);
23002359
if (keyType !== 'string') {
2301-
log('WARNING: Non-string property key, received type ' + keyType + ', coercing to string "' + key + '"');
23022360
key = String(key);
2361+
log('WARNING: Non-string property key, received type ' + keyType + ', coercing to string "' + key + '"');
23032362
}
23042363

23052364
// validate value
@@ -2343,11 +2402,76 @@ var validatePropertyValue = function validatePropertyValue(key, value) {
23432402
return value;
23442403
};
23452404

2405+
var validateGroups = function validateGroups(groups) {
2406+
var groupsType = type(groups);
2407+
if (groupsType !== 'object') {
2408+
log('Error: invalid groups format. Expecting Javascript object, received ' + groupsType + ', ignoring');
2409+
return {};
2410+
}
2411+
2412+
var copy = {}; // create a copy with all of the valid properties
2413+
for (var group in groups) {
2414+
if (!groups.hasOwnProperty(group)) {
2415+
continue;
2416+
}
2417+
2418+
// validate key
2419+
var key = group;
2420+
var keyType = type(key);
2421+
if (keyType !== 'string') {
2422+
key = String(key);
2423+
log('WARNING: Non-string groupType, received type ' + keyType + ', coercing to string "' + key + '"');
2424+
}
2425+
2426+
// validate value
2427+
var value = validateGroupName(key, groups[group]);
2428+
if (value === null) {
2429+
continue;
2430+
}
2431+
copy[key] = value;
2432+
}
2433+
return copy;
2434+
};
2435+
2436+
var validateGroupName = function validateGroupName(key, groupName) {
2437+
var groupNameType = type(groupName);
2438+
if (groupNameType === 'string') {
2439+
return groupName;
2440+
}
2441+
if (groupNameType === 'date' || groupNameType === 'number' || groupNameType === 'boolean') {
2442+
groupName = String(groupName);
2443+
log('WARNING: Non-string groupName, received type ' + groupNameType + ', coercing to string "' + groupName + '"');
2444+
return groupName;
2445+
}
2446+
if (groupNameType === 'array') {
2447+
// check for nested arrays or objects
2448+
var arrayCopy = [];
2449+
for (var i = 0; i < groupName.length; i++) {
2450+
var element = groupName[i];
2451+
var elemType = type(element);
2452+
if (elemType === 'array' || elemType === 'object') {
2453+
log('WARNING: Skipping nested ' + elemType + ' in array groupName');
2454+
continue;
2455+
} else if (elemType === 'string') {
2456+
arrayCopy.push(element);
2457+
} else if (elemType === 'date' || elemType === 'number' || elemType === 'boolean') {
2458+
element = String(element);
2459+
log('WARNING: Non-string groupName, received type ' + elemType + ', coercing to string "' + element + '"');
2460+
arrayCopy.push(element);
2461+
}
2462+
}
2463+
return arrayCopy;
2464+
}
2465+
log('WARNING: Non-string groupName, received type ' + groupNameType +
2466+
'. Please use strings or array of strings for groupName');
2467+
};
2468+
23462469
module.exports = {
23472470
log: log,
23482471
isEmptyString: isEmptyString,
23492472
sessionStorageEnabled: sessionStorageEnabled,
23502473
truncate: truncate,
2474+
validateGroups: validateGroups,
23512475
validateInput: validateInput,
23522476
validateProperties: validateProperties
23532477
};

amplitude.min.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)