Build your first Trending Information Dashboard with the AlphaSense SDK
This how-to guide will lead developers through building a standard Trending Information Dashboard with 3 charts built in.
Quick Start & Starting From Scratch
Before diving into the quick start guide or starting from scratch, ensure you meet the following requirements:
Requirements
- Node.js: Version 14.0.0 or higher. You can check your current version by running
node -v
in your terminal. - Programming Language: Ensure you have the latest version of your preferred programming language installed. For JavaScript/TypeScript projects, the Node.js version requirement applies.
You can create a new project with the help of the project template. To create a new project, run the following command:
npx @alphasense/sdk-create-app my-trends
This command will create a new project with the following structure:
my-trends
├── node_modules
├── alphasense.config.ts
├── index.ts
├── package.json
└── tsconfig.json
After updating the configuration (alphasense.config.ts file), you can generate the typings for the queries by running the following command:
npm run generate
After generating the typings, you can start using the SDK in your project! Update the index.ts file however you want, and you can run the code with the following command:
npm run dev
You can see the example result in the console if you have a valid configuration.
Configuration
The SDK checks for the configuration for the following inputs to make sure the SDK is propertly authenticated:
// Typescript (alphasense.config.ts)
import {AlphaSenseConfig} from '@alphasense/sdk-client'
const config: AlphaSenseConfig = {
username: '<your-username>',
password: '<your-password>',
apiKey: '<your-api-key>',
clientId: '<your-client-id>',
clientSecret: '<your-client-secret>',
}
export default config
Importing Required Libraries
In order to use the SDK, you will need to import the following libraries:
import {createClient, AlphaSenseConfig} from '@alphasense/sdk-client'
import config from './alphasense.config.ts'
import {
DocumentCountGroupBy,
enumDocumentCountGroupBy,
} from '@alphasense/sdk-client/dist/generated/schema.ts'
Building the Search Queries
To start, you will need to build the search queries that you want to use. We will create a new file
called api.ts
that will contain everything in relation to extracting information from AlphaSense.
Here is an example of how to build the search queries with various different filters specific to the
trends API:
const searchQueries = [
{
// NEGATIVE RISK
filter: {
keyword: {query: 'risk negative'},
date: {customRange: {from: '2020-01-01', to: '2024-01-01'}},
types: {ids: ['3']},
},
groupBy: enumDocumentCountGroupBy.DOCUMENT_TYPES_MONTHLY as DocumentCountGroupBy,
limit: 250,
},
{
// GENERATIVE AI
filter: {
keyword: {query: 'generative ai'},
date: {preset: 'LAST_6_MONTHS'},
types: {ids: ['17000', '18000', '19000']},
},
groupBy: enumDocumentCountGroupBy.DOCUMENT_TYPES_DAILY as DocumentCountGroupBy,
limit: 250,
},
{
// ELECTIONS
filter: {
keyword: {query: 'election'},
date: {customRange: {from: '2020-01-01', to: '2024-01-01'}},
types: {ids: ['31019']},
},
groupBy: enumDocumentCountGroupBy.DOCUMENT_TYPES_WEEKLY as DocumentCountGroupBy,
limit: 250,
},
]
Searching the Documents
After you have your search queries ready, you can start searching the documents by building out the client and fetching each of the results to pull back trending information:
// Function to search documents
const documentResults = async (
filter: Record<string, any>,
groupBy?: DocumentCountGroupBy,
limit?: number,
) => {
const client = await createClient(config)
try {
const resp = await client.query({
documentCountsInSearchResults: {
items: {
groupKeys: {
on_DocumentTypeAndDateGroup: {
date: true,
name: true,
},
},
value: true,
},
__args: {
limit,
filter,
groupBy:
groupBy || (enumDocumentCountGroupBy.DOCUMENT_TYPES_QUARTERLY as DocumentCountGroupBy),
},
},
})
return resp?.documentCountsInSearchResults || []
} catch (error) {
console.error('Error fetching document results:', error)
return []
}
}
Lastly, we will create a function to fetch all the results from the API and load them right away to be added into the initialization of the dashboard.
// Function to fetch all results
async function fetchAllResults() {
const allResults = []
for (const query of searchQueries) {
console.log(query)
const results = await documentResults(query.filter, query.groupBy, query.limit)
allResults.push(results)
}
return allResults
}
export {fetchAllResults}
Building the Dashboard
In a separate file index.ts
, we will start building a trends dashboard to get the latest document
trends on the searches we had discussed. We will be using the d3 package to create the charts and
the lodash package to group the data by month:
To start you will need to import the necessary libraries and instantiate the server and the DOM
import express from 'express'
import {fetchAllResults} from './api.ts'
import D3Node from 'd3-node'
import * as d3 from 'd3'
import {parseISO, format} from 'date-fns'
import {groupBy} from 'lodash'
const app = express()
const port = 3000
After importing the required package we can put together the functions needed to create the page. We will start with the createCharts function that will pull the information from all the API calls and create multipl charts to display it. createCharts will also call createTimeSeriesChart and createErrorChart to create the charts and handle any errors that may occur:
function createCharts(data: any[]): string[] {
return data.map((dataset, index) => {
const d3n = new D3Node()
const svg = d3n.createSVG(800, 400)
if (!dataset?.items) {
createErrorChart(svg, index)
} else {
const groupedData = groupBy(
dataset.items.filter(
(item: {groupKeys: {name: string}[]}) => item.groupKeys[0].name !== 'ALL',
),
(item: {groupKeys: {name: string}[]}) => item.groupKeys[0].name,
)
const categories = Object.keys(groupedData)
const timeSeriesData = processTimeSeriesData(groupedData)
timeSeriesData.length > 0
? createTimeSeriesChart(svg, timeSeriesData, categories, index)
: createErrorChart(svg, index, 'No time series data available')
}
return d3n.svgString()
})
}
Next we can create a function that will create the various charts needed for the dashboard to display the information chosen in the first analysis. This will include the createErrorChart function in case there is no data available and the createTimeSeriesChart function that will create a time series chart for the data available:
function createErrorChart(svg: any, index: number, message = 'Error in Dataset') {
svg.append('text').attr('x', 400).attr('y', 200).attr('text-anchor', 'middle').text(message)
}
function createTimeSeriesChart(
svg: any,
timeSeriesData: any[],
categories: string[],
index: number,
) {
const margin = {top: 20, right: 30, bottom: 40, left: 50}
const width = 750 - margin.left - margin.right
const height = 350 - margin.top - margin.bottom
const x = d3
.scaleTime()
.domain(d3.extent(timeSeriesData, (d: {date: Date}) => d.date) as [Date, Date])
.range([0, width])
const y = d3
.scaleLinear()
.domain([0, d3.max(timeSeriesData, (d: {value: number}) => d.value)])
.range([height, 0])
const color = d3.scaleOrdinal().domain(categories).range(d3.schemeCategory10)
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`)
addAxes(g, x, y, height)
drawBars(g, timeSeriesData, categories, x, y, width, height, color)
addLegend(svg, categories, color, width, margin)
addTitle(svg, ['Risk Negative', 'Generative AI', 'Election'][index])
}
For each of the 3 charts we are looking to build the createTimeSeriesChart will also need to add and draw the various parts of the chart. This includes the addAxes, drawBars, addLegend, and addTitle functions. Let's take a look at the addAxes function:
function addAxes(g: any, x: any, y: any, height: number) {
g.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(x))
g.append('g').call(d3.axisLeft(y))
}
Next, we can create the function to draw the bars of the chart:
function drawBars(
g: any,
data: any[],
categories: string[],
x: any,
y: any,
width: number,
height: number,
color: any,
) {
const barWidth = (width / data.length) * 0.8 // 80% of available space
const categoryWidth = barWidth / categories.length
const stack = d3
.stack()
.keys(categories)
.value((d: any, key: string) => d.values[key] || 0)
const groupedData = d3.group(data, (d: {date: Date}) => d.date)
const stackedData = stack(
Array.from(groupedData, ([date, values]) => ({
date,
values: Object.fromEntries(
values.map((v: {category: string; value: number}) => [v.category, v.value]),
),
})),
)
g.selectAll('.bar-group')
.data(stackedData)
.enter()
.append('g')
.attr('fill', d => color(d.key))
.selectAll('rect')
.data(d => d)
.enter()
.append('rect')
.attr(
'x',
(d: d3.SeriesPoint<{date: Date; values: Record<string, number>}>) =>
x(d.data.date) + barWidth * 0.1,
)
.attr('y', d => y(d[1]))
.attr('height', d => y(d[0]) - y(d[1]))
.attr('width', barWidth * 0.8)
}
Then we can add the legend and table to the chart:
function addLegend(svg: any, categories: string[], color: any, width: number, margin: any) {
const legend = svg
.append('g')
.attr('transform', `translate(${width + margin.left + 10},${margin.top})`)
categories.forEach((category, i) => {
const legendRow = legend.append('g').attr('transform', `translate(0, ${i * 20})`)
legendRow.append('rect').attr('width', 10).attr('height', 10).attr('fill', color(category))
legendRow.append('text').attr('x', 15).attr('y', 10).text(category)
})
}
function addTitle(svg: any, title: string) {
svg
.append('text')
.attr('x', 400)
.attr('y', 30)
.attr('text-anchor', 'middle')
.style('font-size', '16px')
.style('font-weight', 'bold')
.text(title)
}
To make sure we are producing complete sets of data we will need to process the time series data and group it by month:
function processTimeSeriesData(groupedData: Record<string, any[]>) {
return Object.entries(groupedData)
.flatMap(([category, items]) =>
items.map(item => {
let date: Date
let value: number
try {
// Parse date string to Date object
date = parseISO(item.groupKeys[0].date)
value = item.value
} catch (error) {
console.log('Error processing item:', error)
date = new Date() // Fallback to current date
value = 0 // Fallback value
}
return {
category,
date,
value,
}
}),
)
.sort((a, b) => a.date.getTime() - b.date.getTime())
}
Lastly, we will need to set up the server and the DOM to be able to serve the dashboard:
app.get('/', async (req, res) => {
try {
const allResults = await fetchAllResults()
const chartSvgs = createCharts(allResults)
const html = `
<!DOCTYPE html>
<html>
<head>
<title>Document Publication Trends</title>
<style>
body { font-family: Arial, sans-serif; }
h1 { text-align: center; }
.chart { display: block; margin: 20px auto; max-width: 800px; }
</style>
</head>
<body>
<h1>Document Publication Trends</h1>
${chartSvgs.map(svg => `<div class="chart">${svg}</div>`).join('')}
</body>
</html>
`
res.send(html)
} catch (error) {
console.error('Error:', error)
res.status(500).send('An error occurred')
}
})
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`)
})
Finally, run the app using the following command:
npm run dev
Then, open your browser and navigate to the following URL to see the result: http://localhost:3000
Once all the steps are completed, you can start viewing data directly from AlphaSense in your dashboard relating to the trends we discussed!