README
About
Build elastic search resolvers
Install
Install by referencing the github location and the release number:
npm install --save social-native/snpkg-snapi-elasticsearch-resolver-builder#v0.1.0
Run locally
- Run the migrations
NODE_ENV=development npm run migrate:latest
- Seed the database
NODE_ENV=development npm run seed:run
- Run the dev server
npm run dev
- Visit the GraphQL playground http://localhost:4000/graphql
- Run some queries!
query {
campaign(id: 2143) {
aggregation {
percentageHistogram {
audienceCity {
valueFormat
nameFormat
data {
name
value
}
}
audienceState {
data {
name
value
}
}
audienceCountry {
data {
name
value
}
}
}
sum {
comments {
total
}
reach {
total
}
likes {
total
}
engagements {
total
}
engagementRate {
total
}
contents {
total
}
creators {
total
}
}
distributed {
likes(input: { interval: 10000 }) {
data {
x
y
}
}
engagements(input: { interval: 10000 }) {
data {
x
y
}
}
engagementRate(input: { interval: 100, since: "0", until: "20000000000" }) {
data {
x
y
}
}
reach(input: { interval: 10000 }) {
data {
x
y
}
}
comments(input: { interval: 10000 }) {
data {
x
y
}
}
}
cumulative {
likes(input: { interval: 10000 }) {
data {
x
y
}
}
engagements(input: { interval: 10000 }) {
data {
x
y
}
}
engagementRate(input: { interval: 10000 }) {
data {
x
y
}
}
reach(input: { interval: 10000 }) {
data {
x
y
}
}
comments(input: { interval: 10000 }) {
data {
x
y
}
}
}
}
}
}
Defining a resolver builder template:
- Extend the ResolverBuilderBase
import {Base} from 'snpkg-snapi-elasticsearch-resolver-builder';
class SomeBuilder extends Base{}
- Define
setState
,queryBuilder
, andnodeBuilder
in the template
class SomeBuilder extends ResolverBase {
protected setState({args}: IResolverInputObj): IState {
return {
range: this.calcRange(args.input || {}),
// 60 minutes is the default bucket size
interval: args && args.input && args.input.interval ? args.input.interval : 60
};
}
protected buildQuery(
{parent: {id}}: IResolverInputObj,
config: ICumulativePercentageConfig,
state: IState
) {
return {
body: {
size: 0,
query: {
bool: {
filter: [
{term: {campaign_id: id}},
{range: {metric_since_timestamp: state.range}}
]
}
},
sort: [{indexed_timestamp: 'desc'}],
aggs: {
per_day: {
date_histogram: {
field: 'indexed_timestamp',
interval: `${state.interval}m`,
format: 'yyyy-MM-dd',
min_doc_count: 0
},
...etc
}
}
}
};
}
protected buildNode(result: IQueryResult, state: IState) {
const parsedData = (get(result, 'body.aggregations.per_day.buckets') ||
[]) as ICumulativePercentBucket[];
...etc
return <GQLNodeType>;
}
}
How to use
- Build a query executor
import {DefaultQueryExecutor} from 'snpkg-snapi-elasticsearch-resolver-builder';
const queryExecutor = new DefaultQueryExecutor(ELASTIC_SEARCH_INDEX, elasticSearchClient);
- Initialize one or more of the reusable elastic search resolver builders or use one of the builders you defined in a template
import {builders} from 'snpkg-snapi-elasticsearch-resolver-builder';
const sumESResolver = new builders.Sum({queryExecutor});
const uniqueCountESResolver = new builders.UniqueCount({queryExecutor});
const distributedESResolver = new builders.Distributed({queryExecutor});
const cumulativeESResolver = new builders.Cumulative({queryExecutor});
const cumulativePercentESResolver = new builders.CumulativePercent({queryExecutor});
const distributedPercentESResolver = new builders.DistributedPercent({queryExecutor});
const sumPercentESResolver = new builders.SumPercent({queryExecutor});
const percentHistogramESResolver = new builders.PercentHistogram({queryExecutor});
- Build one or more resolvers
const resolvers = {
sum: {
comments: sumESResolver.buildResolver({fieldToSum: 'comments'}),
engagementRate: sumPercentESResolver.buildResolver({
numeratorFieldToAggregateOver: 'engagements',
denominatorFieldToAggregateOver: 'reach'
}),
engagements: sumESResolver.buildResolver({fieldToSum: 'engagements'}),
likes: sumESResolver.buildResolver({fieldToSum: 'likes'}),
reach: sumESResolver.buildResolver({fieldToSum: 'reach'}),
creators: uniqueCountESResolver.buildResolver({fieldToUniquelyCount: 'creator_id'}),
contents: uniqueCountESResolver.buildResolver({fieldToUniquelyCount: 'content_post_id'})
},
distributed: {
comments: distributedESResolver.buildResolver({fieldToAggregateOver: 'comments'}),
engagementRate: distributedPercentESResolver.buildResolver({
numeratorFieldToAggregateOver: 'engagements',
denominatorFieldToAggregateOver: 'reach'
}),
engagements: distributedESResolver.buildResolver({fieldToAggregateOver: 'engagements'}),
likes: distributedESResolver.buildResolver({fieldToAggregateOver: 'likes'}),
reach: distributedESResolver.buildResolver({fieldToAggregateOver: 'reach'})
},
cumulative: {
comments: cumulativeESResolver.buildResolver({fieldToAggregateOver: 'comments'}),
engagementRate: cumulativePercentESResolver.buildResolver({
numeratorFieldToAggregateOver: 'engagements',
denominatorFieldToAggregateOver: 'reach'
}),
engagements: cumulativeESResolver.buildResolver({fieldToAggregateOver: 'engagements'}),
likes: cumulativeESResolver.buildResolver({fieldToAggregateOver: 'likes'}),
reach: cumulativeESResolver.buildResolver({fieldToAggregateOver: 'reach'})
},
percentageHistogram: {
audienceCountry: percentHistogramESResolver.buildResolver({
numeratorFieldToBucketBy: 'audience_geography_countries.name',
numeratorFieldToCount: 'audience_geography_countries.count',
denominatorFieldToAggregateOver: 'audience_age.count',
nameFormat: percentHistogramESResolver.nameFormats.ISO_COUNTRY_CODE
}),
audienceCity: percentHistogramESResolver.buildResolver({
numeratorFieldToBucketBy: 'audience_geography_cities.name',
numeratorFieldToCount: 'audience_geography_cities.count',
denominatorFieldToAggregateOver: 'audience_age.count',
nameFormat: percentHistogramESResolver.nameFormats.RAW
}),
audienceState: percentHistogramESResolver.buildResolver({
numeratorFieldToBucketBy: 'audience_geography_states.name',
numeratorFieldToCount: 'audience_geography_states.count',
denominatorFieldToAggregateOver: 'audience_age.count',
nameFormat: percentHistogramESResolver.nameFormats.RAW
})
}
};