Skip to content
This repository has been archived by the owner on Apr 11, 2024. It is now read-only.

Commit

Permalink
feat: Add query for table column aggregate count.
Browse files Browse the repository at this point in the history
  • Loading branch information
fourjuaneight committed Jun 21, 2022
1 parent 6cfee9d commit e465be2
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 4 deletions.
41 changes: 37 additions & 4 deletions src/handler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import {
addMediaItem,
queryMediaAggregateCount,
queryMediaItems,
queryTags,
searchMediaItems,
updateMediaItem,
} from './hasura';

import { RequestPayload, MediaItem } from './typings.d';
import {
RequestPayload,
MediaItem,
TableAggregate,
CountColumn,
} from './typings.d';

// default responses
const responseInit = {
Expand Down Expand Up @@ -97,7 +103,7 @@ const handleAction = async (payload: RequestPayload): Promise<Response> => {
responseInit
);
}
case payload.type === 'Insert':
case payload.type === 'Insert': {
const insertData = payload.data as MediaItem;
const saved = await addMediaItem(payload.table, insertData);

Expand All @@ -110,7 +116,8 @@ const handleAction = async (payload: RequestPayload): Promise<Response> => {
responseInit
);
break;
case payload.type === 'Update':
}
case payload.type === 'Update': {
const updateData = payload.data as MediaItem;
const updated = await updateMediaItem(
payload.table,
Expand All @@ -127,7 +134,8 @@ const handleAction = async (payload: RequestPayload): Promise<Response> => {
responseInit
);
break;
case payload.type === 'Search':
}
case payload.type === 'Search': {
const searchPattern = payload.query as string;
const searchItems = await searchMediaItems(
payload.table,
Expand All @@ -142,6 +150,21 @@ const handleAction = async (payload: RequestPayload): Promise<Response> => {
responseInit
);
break;
}
case payload.type === 'Count': {
const queryResults = await queryMediaAggregateCount(
payload.table as TableAggregate,
payload.countColumn as CountColumn
);

return new Response(
JSON.stringify({
count: queryResults,
table: payload.table,
}),
responseInit
);
}
default: {
const queryItems = await queryMediaItems(payload.table);

Expand Down Expand Up @@ -227,6 +250,16 @@ export const handleRequest = async (request: Request): Promise<Response> => {
JSON.stringify({ error: 'Missing Search query.' }),
badReqBody
);
case payload.type === 'Count' && !payload.countColumn:
return new Response(
JSON.stringify({ error: "Missing 'countColumn' parameter." }),
badReqBody
);
case payload.type === 'Count' && !payload.table:
return new Response(
JSON.stringify({ error: "Missing 'table' parameter." }),
badReqBody
);
case !key:
return new Response(
JSON.stringify({ error: "Missing 'key' header." }),
Expand Down
68 changes: 68 additions & 0 deletions src/hasura.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import {
HasuraErrors,
HasuraInsertResp,
HasuraQueryAggregateResp,
HasuraQueryResp,
HasuraQueryTagsResp,
HasuraUpdateResp,
MediaItem,
RecordColumnAggregateCount,
} from './typings.d';

const MEDIA_FIELDS = {
Expand All @@ -30,6 +32,22 @@ const objToQueryString = (obj: { [key: string]: any }) =>
return `${key}: ${fmtValue}`;
});

const countUnique = (iterable: string[]) =>
iterable.reduce((acc: RecordColumnAggregateCount, item) => {
acc[item] = (acc[item] || 0) + 1;
return acc;
}, {});

const countUniqueSorted = (iterable: string[]) =>
// sort descending by count
Object.entries(countUnique(iterable))
.sort((a, b) => b[1] - a[1])
.reduce(
(acc: RecordColumnAggregateCount, [key, val]) =>
({ ...acc, [key]: val } as RecordColumnAggregateCount),
{}
);

/**
* Get media tags from Hasura.
* @function
Expand Down Expand Up @@ -128,6 +146,56 @@ export const queryMediaItems = async (table: string): Promise<MediaItem[]> => {
}
};

/**
* Get aggregated count of media column from Hasura.
* @function
* @async
*
* @param {string} table
* @param {string} column
* @returns {Promise<RecordColumnAggregateCount>}
*/
export const queryMediaAggregateCount = async (
table: TableAggregate,
column: CountColumn
): Promise<RecordColumnAggregateCount> => {
const query = `
{
media_${table}(order_by: {${column}: asc}) {
${column}
}
}
`;

try {
const request = await fetch(`${HASURA_ENDPOINT}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Hasura-Admin-Secret': `${HASURA_ADMIN_SECRET}`,
},
body: JSON.stringify({ query }),
});
const response: any = await request.json();

if (response.errors) {
const { errors } = response as HasuraErrors;

throw `(queryMediaAggregateCount) - ${table}: \n ${errors
.map(err => `${err.extensions.path}: ${err.message}`)
.join('\n')} \n ${query}`;
}

const data = (response as HasuraQueryAggregateResp).data[`media_${table}`];
const collection = data.map(item => item[column]);

return countUniqueSorted(collection);
} catch (error) {
console.log(error);
throw error;
}
};

/**
* Search media entries from Hasura.
* @function
Expand Down
22 changes: 22 additions & 0 deletions src/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ export interface Video {

export type MediaItem = Book | Game | Video;

export type CountColumn =
| 'author'
| 'director'
| 'genre'
| 'platform'
| 'studio';

export type TableAggregate = 'books' | 'games' | 'movies' | 'shows';

export interface RecordColumnAggregateCount {
[key: string]: number;
}

export interface HasuraInsertResp {
data: {
[key: string]: {
Expand All @@ -45,6 +58,14 @@ export interface HasuraQueryResp {
};
}

export interface HasuraQueryAggregateResp {
data: {
[key: string]: {
[key: string]: string;
}[];
};
}

export interface HasuraQueryTagsResp {
data: {
[key: string]: { name: string }[];
Expand All @@ -67,4 +88,5 @@ export interface RequestPayload {
tagList?: string;
data?: MediaItem;
query?: string;
countColumn?: CountColumn;
}

0 comments on commit e465be2

Please sign in to comment.