Breathe Data
into Your Website

Documentation and Examples

Simple serverless service for building data-driven websites with JavaScript.

Introduction

DataDriven is like a database populated with information about the activity that happens on your website that you can access using a simple JavaScript API with no backend required on your end.

The DD.js library allows you to:

  • track data from you website in a similar way that many web analytics do,
  • write queries to expose some data as publicly accessible Data Feeds (authorisation in the DevMode is required)
  • Read the data from the created Data Feeds and use it on your website.

If you haven't yet, visit our homepage for an example and workflow diagrams.

Examples and Tutorials

There are multiple examples you can find at the bottom of this page. For tutorials, follow us on Medium.

Script Setup and Settings

Overview

The DD.js script can be loaded either from our servers or you can download it and serve it from your own servers. The script can be loaded synchronously or asynchronously. If you decide to use asynchronous method, you will need to check if the script is loaded and wait for it if necessary.

In order for DD.js to work you must pass the project settings - you can specify them as params in the script tag src attribute:

<script type="text/javascript" src="./DD.js?projectID=XYZ_123&publicKey=abcdefghijklmnoprstuwx0123456789"></script>

or in a global object DDDataLayer:

window.DDDataLayer = {
    "settings" : {
        "project" : {
            "projectID" : "XYZ_123",
            "publicKey" : "abcdefghijklmnoprstuwx0123456789"
        }
    }
}

or pass the settings object to DD.init method:

DD.init({
    "settings" : {
        "project" : {
            "projectID" : "XYZ_123",
            "publicKey" : "abcdefghijklmnoprstuwx0123456789"
        }
    }
});

Script Settings

Using the DDDataLayer.settings object, you can override the default settings.

  • autostart DDDataLayer.settings.tracker.autostart = true

    The default value is true, change to false if you want don't want to automatically start tracking a new visit when a visitor opens a page. You can then control when a visit starts using DD.tracker.start()

Using npm

The DD.js is available as a package on npm. Add @drvn/dd as a dependency in your package.json file, or run the below command in the project directory:

npm install @drvn/dd
                        

Tracking

Overview

Each visit consists of properties and events. Every visit can only have one value per each assigned property, but it can have multiple values per each event type.

A single visit could be represented as an object with the following structure:

{
    properties : {
        startDate : "2018-08-01 12:12",
        visitorID : "ABC123",
        referrerURL : "https://www.producthunt.com",
        selectedProductPlan : "BASIC"
    },
    events : [
        { key:"URL",value:"https://mywebsite.com/?ref=producthunt"},
        { key:"pathname",value:"/"},
        { key:"URL",value:"https://mywebsite.com/sign-up"},
        { key:"pathname",value:"/sign-up"},
        { key:"selectedProductPlan",value:"PRO"},
        { key:"selectedProductPlan",value:"BASIC"},
        { key:"URL",value:"https://mywebsite.com/thank-you"},
        { key:"pathname",value:"/thank-you"}
    ]
}

In the example above, we tracked both, a property and an event, every time a visitor selected a plan. Notice, that the visit events contain information that the visitor first selected the PRO plan and then switched to BASIC. The visit properties contain only the last selected plan.

Starting a Visit

By default, a visit is automatically started when a visitor opens a website with added DD.js script. A new visit is also started if a previously active visit expires due to lack of activity.

If you want to customise when a new visit is started, use:

  • start DD.tracker.start()

    starts a new visit. If a visit is active, calling this method has no effect.

  • stop DD.tracker.stop()

    stops tracking of a currently active visit.

  • restart DD.tracker.restart()

    terminates an active visit and starts a new one. If there is no active visit, a new visit is started.

  • isConnected DD.tracker.isConnected()

    returns true if a visit is active, false otherwise.

Custom Tracking

Both methods for tracking properties and events require key and value parameters. The key must be a string and value can be a string or number. If you want to track an object as a value, use JSON.

The max length of a key is 900 bytes and value is 1000 bytes. Please remember that some characters are multi-byte.

  • trackProperty DD.tracker.trackProperty(key, value)

    tracks a property, if two properties with the same key are tracked during a single visit, only the last property value will be preserved.

  • trackMetaEvent DD.tracker.trackMetaEvent(key, value)

    tracks an event

Creating Data Feeds

Overview

Data Feeds are subsets of all your data that authorised visitors can create by selecting and aggregating some properties or events from visits where some properties or events meet specified criteria into a table.

The Data Feeds can be accessed without authorisation to read the entire content of the feed, or further queried to select and aggregate values from specific columns and rows.

Creating a Data Feed

The first step is to create a dataFeed which provides necessary methods to select the data.

  • feed DD.data.feed( name )

    returns DataFeed object

The methods provided by dataFeed are similar to those you may know from SQL. All the methods support chaining.

  • select DataFeed.select( ...datapoints )

    selects properties and metaevents, as specified in the supplied arguments, into data feed columns; returns DataFeed

  • from DataFeed.from( visits )

    limits the selection to data from a segment of visits created using DD.data.segment( segmentName ) method; returns DataFeed

  • having DataFeed.having( ...columns )

    limits the selection to rows with specific column values, i.e. greater than 10. columns arguments must be DataFeedColumn objects created using DD.data.feedColumn( columnName ) where columnName matches the names in the DD.select definitions; returns DataFeed

  • orderBy DataFeed.orderBy( ...columns )

    sorts the selected rows by one or more columns; returns DataFeed

  • limit DataFeed.limit( int )

    limits the selection to the maximum number of rows; returns DataFeed

The select method requires that you provide datapoints you want to select. Depending on type of data you want to select use:

  • property DD.data.datapoint.property( 'key' )

    Returns Datapoint object with methods to filter and aggregate datapoint values

  • metaevent DD.data.datapoint.metaevent( 'key' )

    Returns Datapoint object with methods to filter and aggregate datapoint values

The Datapoint methods allow you to filter which values to select (these methods are described in the Segmentation chapter). The Datapoint methods also allow you to aggregate the selected values.

  • avg Datapoint.avg ( )

    sets the average value of matched datapoint values for each grouped row; returns Datapoint

  • count Datapoint.count ( )

    sets the total count of matched datapoint values for each grouped row; returns Datapoint

  • countDistinct Datapoint.countDistinct ( )

    sets the total count of distinct matched datapoint values for each grouped row; returns Datapoint

  • max Datapoint.max ( )

    sets the maximum matched datapoint value for each grouped row; returns Datapoint

  • min Datapoint.min ( )

    sets the minimum matched datapoint value for each grouped row; returns Datapoint

  • groupConcat Datapoint.groupConcat ( )

    joins matched datapoint values for each grouped row; returns Datapoint

  • groupConcatDistinct Datapoint.groupConcatDistinct ( )

    joins unique matched datapoint values for each grouped row; returns Datapoint

  • sum Datapoint.sum ( )

    sets the sum matched datapoint values for each grouped row; returns Datapoint

The Datapoint has two additional methods that you can use:

  • as Datapoint.as ( columnName )

    sets the column name in the feed for a selected datapoint which is useful especially with the aggregator methods; returns Datapoint

  • protect Datapoint.protect( )

    values from a protected column can only be read from a feed or queried if dataFeedQuery specify their exact values; returns Datapoint

Reading Data Feeds

Overview

There are two options to read the data from feeds. You can either read its entire content or, if you have feeds with multiple rows, you can create a query to read and/or aggregate only selected rows.

Reading an Entire Data Feed

Once you create a data feed definition you can use DD.reader.read to load its data:

Note, that if the feed does not exist, the read request will not return any results. If you want to create a feed, you need to be signed in to the Console, open your website in the DevMode and then run your code. Only after a data feed is created, it can be accessed without authorisation.

  • read DD.reader.read ( feed, feedQuery, callback )

    the feed parameter is either a dataFeed object, or, if a feed was previously created, a publicKey string which you can find in the Console; DataFeedQuery is either an empty object or a dataFeedQuery; callback is a function executed once the read request is completed to which a response param is passed.

// prepare a feed

var feed = DD.data.feed('All-Time Signups Count');

feed.select(
    DD.data.datapoints.metaevent('URL').startsWith("https://mywebsite.com/thank-you").as('signups')
);
       
DD.reader.read (feed, {}, function(response){
    // callback function
});
                                

Reading a Data Feed Using a Query

In the example above, we requested to read the entire data feed. However, if a data feed contains multiple rows you may want to select just some of the rows and/or aggregate them.

Let's say we have a data feed with number of sign-ups per product plan:

selectedProductPlansignups
BASIC1
PRO1

Using a dataFeedQuery we could select the number of sign-ups for a specific plan:

// prepare query
var query = DD.data.feedQuery();

query.select(
    DD.data.feedColumn('signups')
).where(
    DD.data.feedColumn('selectedProductPlan').equals('BASIC')
);

DD.reader.read (feed, query, function(response){
    // callback function
});
                                    

or, we could sum up the signups from all the rows:

// prepare query
var query = DD.data.feedQuery();

query.select(
    DD.data.feedColumn('signups').sum()
);

DD.reader.read (feed, query, function(response){
    // callback function
});
                                    

If it's possible, try to create data feeds that can be read without using DataFeedQuery because such requests are slower.

  • feedQuery DD.data.feedQuery ( )

    returns DataFeedQuery object with the methods to select, filter, aggregate and sort results.

The DataFeedQuery methods:

  • select DataFeedQuery.select ( ...dataFeedColumns )

    selects and aggregates specified columns of a feed. Each of the columns must be a feedColumn object; returns DataFeedQuery

  • where DataFeedQuery.where ( ...dataFeedColumns )

    filters the selected rows. Each of the columns must be a feedColumn object; returns DataFeedQuery

  • groupBy DataFeedQuery.groupBy ( )

    returns DataFeedQuery

  • limit DataFeedQuery.limit ( )

    limits the number of the returned rows; returns DataFeedQuery

  • order DataFeedQuery.order ( )

    returns DataFeedQuery

  • sortBy DataFeedQuery.sortBy ( )

    returns DataFeedQuery

The select and where methods of DataFeedQuery require an array of DataFeedColumn objects which you can create using:

  • feedColumn DD.data.feedColumn ( columnName );

    returns DataFeedColumn object with methods select and aggregate values from a specified columns.

When you pass feed columns to DataFeedQuery.select you can use the following aggregators. If you use DataFeedQuery.where, only the values from the matching rows will be used.

  • as DataFeedColumn.as ( name )

    sets the name of the selected value; returns DataFeedColumn

  • avg DataFeedColumn.avg ( )

    selects the average of numerical values in the column; returns DataFeedColumn

  • count DataFeedColumn.count ( )

    selects the count of values in the column; returns DataFeedColumn

  • countDistinct DataFeedColumn.countDistinct ( )

    selects the count of the distinct values in the column; returns DataFeedColumn

  • max DataFeedColumn.max ( )

    selects the highest numerical value in the column; returns DataFeedColumn

  • min DataFeedColumn.min ( )

    selects the lowest numerical value in the column; returns DataFeedColumn

  • groupConcat DataFeedColumn.groupConcat ( )

    selects a single string with the column values; returns DataFeedColumn

  • groupConcatDistinct DataFeedColumn.groupConcatDistinct ( )

    selects a single string with the distinct column values; returns DataFeedColumn

  • sum DataFeedColumn.sum ( )

    selects the sum of numerical values in the column; returns DataFeedColumn

When you pass feed columns to DataFeedQuery.where you can use the following filters:

  • contains DataFeedColumn.contains ( phrase )

    returns DataFeedColumn

  • endsWith DataFeedColumn.endsWith ( phrase )

    returns DataFeedColumn

  • equals DataFeedColumn.equals ( phrase )

    returns DataFeedColumn

  • in DataFeedColumn.in ( ...values )

    returns DataFeedColumn

  • isGreaterThan DataFeedColumn.isGreaterThan ( number )

    returns DataFeedColumn

  • isGreaterThanOrEqualTo DataFeedColumn.isGreaterThanOrEqualTo ( number )

    returns DataFeedColumn

  • isLessThan DataFeedColumn.isLessThan ( number )

    returns DataFeedColumn

  • isLessThanOrEqualTo DataFeedColumn.isLessThanOrEqualTo ( number )

    returns DataFeedColumn

  • negate DataFeedColumn.negate ( phrase )

    excludes rows matched by a filter; returns DataFeedColumn,

  • startsWith DataFeedColumn.startsWith ( phrase )

    returns DataFeedColumn

Protecting Data Feed Columns

From the same two visits (see above), you could create a data feed with the list of IDs of visitors who signed up and the dates on which they signed up:

_visitorIDselectedProductPlanstartDate
XYZ123PRO2018-08-01 13:13
ABC123BASIC2018-08-01 12:12

A visitorID is a sensitive information and you may want to restrict the access to that column using the protect method:

// prepare a feed

var feed = DD.data.feed('Trial Sign-Up Dates');
feed.select(
    DD.data.datapoints.property('visitorID').protect(),
    DD.data.datapoints.property('selectedProductPlan'),
    DD.data.datapoints.property('startDate'),
).having(DD.data.feedColumn('selectedProductPlan');
                                

If one or more columns in a feed are protected, it's no longer possible to read the entire feed at once. To read the data you need to create a dataFeedQuery and specify the exact value from the protected column for you which you want to select data. In this case, we can pull the visitorID from the current visit information:

// prepare query
var query = DD.data.feedQuery();

query.select(
    DD.data.feedColumn('startDate')
).where(
    DD.data.feedColumn('visitorID').equals(DD.visit.getvisitorID())
);

DD.reader.read (feed, query, function(response){
    // callback function
});
                                    

Segmentation

Segmentation allows you to select data only from the visits that meet your criteria. For example, you could prepare separate data feeds with the best-selling products on mobile and desktop devices:

// create a segment of visits and name it.

var visits = DD.data.segment('Mobile Visits');

// add a filters

visits
    .where(DD.data.datapoints.metaevent('Device').equals('Mobile'));
    
// select top 5 products sorted by visit count

var feed = DD.data.feed('Mobile Bestsellers');
feed.select(
    DD.data.datapoints.property('selectedProductPlan'),
    DD.data.datapoints.property('sessionID').count().as('signups'),
).from(visits).orderBy('`signups` DESC').limit(5);
                            

Creating a Segment of Visits

  • segment DD.data.segment( name )

    Returns Visits object with methods to filter visits by start date, properties and events. If no filtering criteria are applied, the segment returns all the visits.

Segmenting Visits by Properties and MetaEvents

  • where Visits.where( ...datapoints )

    Returns Visits, includes visits matching all of the supplied filters.

  • reject Visits.reject( ...datapoints )

    Returns Visits, excludes visits matching all of the supplied filters.

  • whereAny Visits.whereAny( ...datapoints )

    Returns Visits, includes visits matching at least one of the supplied filters.

  • rejectAny Visits.rejectAny( ...datapoints )

    Returns Visits, excludes visits matching at least one of the supplied filters.

To create a filter:

  • property DD.data.datapoints.property( 'key' )

    Returns Datapoint

  • metaevent DD.data.datapoints.metaevent( 'key' )

    Returns Datapoint, key may contain one or more * wildcards

Available string comparison filters:

  • contains Datapoint.contains( phrase )

    phrase may contain one or more * wildcards; returns Datapoint

  • endsWith Datapoint.endsWith( phrase )

    phrase may contain one or more * wildcards; returns Datapoint

  • equals Datapoint.equals( phrase )

    phrase may contain one or more * wildcards; returns Datapoint

  • startsWith Datapoint.startsWith( phrase )

    phrase may contain one or more * wildcards; returns Datapoint

Available numerical comparison filters:

  • isGreaterThan Datapoint.isGreaterThan( number )

    returns Datapoint

  • isGreaterThanOrEqualTo Datapoint.isGreaterThanOrEqualTo( number )

    returns Datapoint

  • isGreaterThanOrEqualTo Datapoint.isGreaterThanOrEqualTo( number )

    returns Datapoint

  • isLessThan Datapoint.isLessThan( number )

    returns Datapoint

  • isLessThanOrEqualTo Datapoint.isLessThanOrEqualTo( number )

    returns Datapoint

It is possible to invert the filter by using the negate method:

  • negate Datapoint.negate( )

    Returns Datapoint

Segmenting Visits by Date

  • startedAfter Visits.startedAfter ( date )

    applies filter to return visits started after the specified date. The supported input: Date object, YYYY-MM-DD HH:MM string, or a relative date string, for example ("Yesterday"); returns Visits

  • startedBefore Visits.startedBefore ( date )

    applies filter to return visits started before the specified date. The supported input: Date object, YYYY-MM-DD HH:MM string, or a relative date string, for example ("Yesterday"); returns Visits

  • startedBetween Visits.startedBetween ( date, date )

    applies filter to return visits started before the specified date. The supported input: Date object, YYYY-MM-DD HH:MM string, or a relative date string, for example ("Yesterday"); returns Visits

Relative date strings are resolved at the time of the execution of a query populating a feed and not at the time the feed is read.

  • This Month

    resolves to the 1st day of the current month

  • Last Month

    resolves to the 1st day of the previous month

  • This Week

    resolves to the 1st day of the current week (Sunday)

  • Last Week

    resolves to the 1st day of the previous week (Sunday)

  • Today

    resolves to 00:00 today

  • Yesterday

    resolves to 00:00 yesterday

  • Last Hour

    resolves to the beginning of the current hour

  • Last [X] [Period]s

    resolves to the beginning of the specified period

    var visits = DD.data.segment('Visits Yesterday and the Day Before');

    // add a filters

    visits
        .startedBetween('Last 2 Days','Today');
    

Note, that all ralative dates resolve to the beginning of a period. Hence, using Today as the closing date will exclude all visits started today. If you want to include the latest visits, either do not specify the closing date or use this hour.

Autorefeshing Data Feeds

When you create a feed using the DD.js API and you first run the query, you populate the feed with the data. The data feed is cached and distributed to servers in multiple regions to guarantee the shortest possible request times.

If a visitor would open the website, 10 minutes or 10 days later, the read requests would return the same data. If you want the feed's content to be refreshed, you need to schedule the times in the Console.

Examples

Total Visitors Count

Q: How many people visited this website? A:
// prepare a feed

var feed = DD.data.feed('Total Visitors Count');
feed.select(
        DD.data.datapoints.property('visitorID').countDistinct().as('visitorsCount')
    )

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        answer.innerHTML = 'A: '+response.results[0].visitorsCount+' to be exact';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Top URLs by Total Visitors Count

Q: What are the top 3 most visited URLs? A:
// prepare a feed

var feed = DD.data.feed('Top URLs by Total Visitors Count');
feed.select(
        DD.data.datapoints.metaevent('URL').as('Top URL'),
        DD.data.datapoints.property('visitorID').countDistinct().as('visitorsCount')
    ).orderBy('`visitorsCount` DESC', '`Top URL` ASC').limit(3);

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        var urls = [];
        for (var i = 0; i < response.results.length; i++){
            urls.push('<a href="'+response.results[i]['Top URL']+'" target="_blank">No.'+(i+1)+ ': '+response.results[i]['Top URL']+'</a>');
        }
        answer.innerHTML = 'A: '+'Here is the list:<br>'+urls.join('<br>');
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Top URLs by Total Visitors Count from: Exclude Localhost Visits

Q: What are the top 3 most visited URLs? A:
// create a segment of visits and name it.

var visits = DD.data.segment('Exclude Localhost Visits');

// add filters to exclude visits in which 
// one or more of the visited URLs contained phrase "localhost:"

visits
    .reject(
        DD.data.datapoints.metaevent('URL').contains('localhost:')
    );
    
// prepare a feed

var feed = DD.data.feed('Top URLs by Total Visitors Count');
feed.select(
        DD.data.datapoints.metaevent('URL').as('Top URL'),
        DD.data.datapoints.property('visitorID').countDistinct().as('visitorsCount')
    ).from(visits).orderBy('`visitorsCount` DESC', '`Top URL` ASC').limit(3);

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        var urls = [];
        for (var i = 0; i < response.results.length; i++){
            urls.push('<a href="'+response.results[i]['Top URL']+'" target="_blank">No.'+(i+1)+ ': '+response.results[i]['Top URL']+'</a>');
        }
        answer.innerHTML = 'A: '+'Here is the list:<br>'+urls.join('<br>');
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Total Visitors Count from: Visited Homepage

Q: How many people visited the homepage URL? A:
// create a segment of visits and name it.

var visits = DD.data.segment('Visited Homepage');

// add a filter to include only the visits in which 
// one or more of the visited pathnames were equal to "/"

visits
    .where(DD.data.datapoints.metaevent('pathname').equals('/'))
    
// prepare a feed, this time read data from the "visits" segment

var feed = DD.data.feed('Total Visitors Count');
feed.select(
        DD.data.datapoints.property('visitorID').countDistinct().as('visitorsCount')
    ).from(visits)

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        answer.innerHTML = 'A: '+response.results[0].visitorsCount+' to be exact';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Visits Count from: Visited Homepage This Month

Q: How many have visited this month? A:
// create a segment of visits and name it.
var visits = DD.data.segment('Visited Homepage This Month');

// add a filter to include only the visits in which 
// one or more of the visited pathnames were equal to "/"
// and visited since the first of the month until now;
visits
    .where(DD.data.datapoints.metaevent('pathname').equals('/'))
    .startedAfter('This Month');
    
// prepare a feed
var feed = DD.data.feed('Visits Count');
feed.select(
        DD.data.datapoints.property('visitorID').countDistinct().as('visitorsCount')
    ).from(visits)
    
// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created
DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        answer.innerHTML = 'A: '+response.results[0].visitorsCount+' to be exact';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Days of the Week Visits Count from: Visited Homepage This Month

Q: Surely, you don't know on which days the visitors came this month? A:
// create a segment of visits and name it.

var visits = DD.data.segment('Visited Homepage This Month');

// add a filter to include only the visits in which 
// one or more of the visited pathnames were equal to "/"
// and visited since the first of the month until now;

visits
    .where(DD.data.datapoints.metaevent('pathname').equals('/'))
    .startedAfter('This Month');
    
// prepare a feed
// group visitors count by session start time hour

var feed = DD.data.feed('Days of the Week Visits Count');
feed.select(
        DD.data.datapoints.property('visitID').count().as('visitsCount'),
        DD.data.datapoints.property('startTimeWeekDay'),
    ).from(visits)

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    if (response.results[0]){
        var days = [];
        var daysOfWeek = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
        var maxValue = 0;
        for (var i = 0; i<7; i++){
            days.push (0);
        }
        for (var i = 0; i<response.results.length; i++){
            days[response.results[i]['startTimeWeekDay']] = response.results[i].visitsCount*1;
            if (maxValue<days[response.results[i]['startTimeWeekDay']]){
                maxValue = days[response.results[i]['startTimeWeekDay']];
            }
        }
        for (var i = 0; i<7; i++){
            var val = days[i] > 0 ? days[i] : '';
            days[i] = '<span style="width:'+(100*days[i]/maxValue)+'%">'+'<span>'+val+'</span>'+'<span>'+(daysOfWeek[i])+'</span>'+'</span>';
        }
        var answer = document.querySelector('{{exampleId}} answer');
        answer.innerHTML = 'A: '+'<span class="barchart">'+days.join('')+'</span>';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Days of the Week Visits Count, Monday from: Visited Homepage This Month

Q: Surely, you could use the same data feed to get visits on Mondays? A:
// prepare feed query

var query = DD.data.feedQuery();

query.select(
        DD.data.feedColumn('visitsCount').sum()
    ).where(
        DD.data.feedColumn('startTimeWeekDay').equals('1'),
    );
    
// read the data from the Data Feed created before using its publicKey
// the publicKey can be found in the Console

DD.reader.read ("f775c449fde670ec40a77ba99193d0d2/Session_listGroupedDatapointsByFilters/b352a822b7198b38ce2f707ada4cf2d1", query, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        answer.innerHTML = 'A: Yes, anything for you: '+response.results[0].visitsCount+'.';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Days of the Week Visits Count, Weekdays from: Visited Homepage This Month

Q: How about all the weekdays, from Monday to Friday? A:
// prepare feed query

var query = DD.data.feedQuery();

// sum data from Monday till Friday 
query.select(
        DD.data.feedColumn('visitsCount').sum()
    ).where(
        DD.data.feedColumn('startTimeWeekDay').isGreaterThanOrEqualTo('1'),
        DD.data.feedColumn('startTimeWeekDay').isLessThanOrEqualTo('5')
    );
    
// read the data from the Data Feed created before using its publicKey

DD.reader.read ("f775c449fde670ec40a77ba99193d0d2/Session_listGroupedDatapointsByFilters/b352a822b7198b38ce2f707ada4cf2d1", query, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0]){
        answer.innerHTML = 'A: Easy! Here\'s the total: '+response.results[0].visitsCount+'.';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

First & Last Visit Date from: Visited Docs Page

Q: What are the dates of the first and last visit, huh? A:
// create a segment of visits and name it.

var visits = DD.data.segment('Visited Docs Page');

// add a filter to include only the visits in which 
// one or more of the visited pathnames were equal to "/"

visits
    .where(DD.data.datapoints.metaevent('pathname').contains('/docs.html'));
    
// prepare a feed
// get the earliest and the latest visit date

var feed = DD.data.feed('First & Last Visit Date');
feed.select(
        DD.data.datapoints.property('startTime').min().as('firstVisit'),
        DD.data.datapoints.property('startTime').max().as('lastVisit')
    ).from(visits)

// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, {}, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (response.results[0].firstVisit){
        answer.innerHTML = 'A: '+'The first visit was on '+new Date(response.results[0].firstVisit).toString()+ ' and the last one on '+new Date(response.results[0].lastVisit).toString();
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});
                            

Visitors' First Visit Dates, Mine from: Visited Docs Page

Q: Can you tell when I visited this page for the first time? A:
// create a segment of visits and name it.

var visits = DD.data.segment('Visited Docs Page');

// add a filter to include only the visits in which 
// one or more of the visited pathnames were equal to "/"

visits
    .where(DD.data.datapoints.metaevent('pathname').contains('/docs.html'));
    
// prepare a feed
// get the earliest visit date for each visitorID.
// protect the visitorID data feed column so its values can be searched for but not retrieved 

var feed = DD.data.feed('Visitors First Visit Dates');
feed.select(
        DD.data.datapoints.property('startTime').min().as('firstVisit'),
        DD.data.datapoints.property('visitorID').as('visitorID').protect()
    ).from(visits)


// prepare query
var query = DD.data.feedQuery();

query.select(
        DD.data.feedColumn('firstVisit')
    ).where(
        DD.data.feedColumn('visitorID').equals(DD.visit.getVisitorID())
    )
// read the data from the public data feed, if the feed doesn't exist and a user is authorised, the feed will be created

DD.reader.read (feed, query, function(response){
    // callback function
    console.log('Got my data, thank you DD!', response.results);
    var answer = document.querySelector('{{exampleId}} answer');
    if (!response.results[0]){
        answer.innerHTML = 'A: '+'This must be your visit in that browser here, is that right?';
    }else if (response.results[0].firstVisit){
        answer.innerHTML = 'A: '+'You came here on '+new Date(response.results[0].firstVisit).toString()+ ' and immediately fell in love with us, didn\'t you?';
    }else{
        answer.innerHTML = 'A: '+'I have no clue. Something went wrong I guess.';
    }
});