Skip to content

Instantly share code, notes, and snippets.

@mcky
Last active February 14, 2021 01:30
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mcky/d931ab1488a95742b056b466bfeed1ff to your computer and use it in GitHub Desktop.
Save mcky/d931ab1488a95742b056b466bfeed1ff to your computer and use it in GitHub Desktop.
Sanity.io + Serverless + Algolia
const algoliasearch = require('algoliasearch');
const request = require('request');
const ndjson = require('ndjson');
const {bindNodeCallback} = require('rxjs');
const {streamToRx} = require('rxjs-stream');
const {bufferCount, map, mergeMap, toArray, tap} = require('rxjs/operators');
// Algolia configuration
const algoliaApp = process.env.ALGOLIA_APP_ID;
const algoliaIndex = process.env.ALGOLIA_INDEX;
// Sanity configuration
const projectId = process.env.SANITY_PROJECT_ID;
const dataset = process.env.SANITY_DATASET;
const sanityExportURL = `https://${projectId}.api.sanity.io/v1/data/export/${dataset}`;
module.exports.indexContent = function indexContent(event, context, cb) {
// Initiate an Algolia client
const client = algoliasearch(algoliaApp, process.env.ALGOLIA_TOKEN);
// Initiate the Algolia index
const index = client.initIndex(algoliaIndex);
// bind the update function to use it as an observable
const partialUpdateObjects = bindNodeCallback((...args) => index.saveObjects(...args));
streamToRx(
request(sanityExportURL).pipe(ndjson())
).pipe(
/*
* Pick and prepare fields you want to index,
* here we reduce structured text to plain text
*/
map(function sanityToAlgolia(doc) {
return {
objectID: doc._id,
body: blocksToText(doc.body || []),
blurb: blocksToText(doc.blurb || []),
title: doc.title,
name: doc.name,
slug: doc.slug,
};
}),
// buffer batches in chunks of 100
bufferCount(100),
// 👇uncomment to console.log objects for debugging
// tap(console.log),
// submit actions, one batch at a time
mergeMap(docs => partialUpdateObjects(docs), 1),
// collect all batches and emit when the stream is complete
toArray()
)
.subscribe(batchResults => {
const totalLength = batchResults.reduce((count, batchResult) => count + batchResult.objectIDs.length, 0);
cb(null, `Updated ${totalLength} documents in ${batchResults.length} batches`);
}, cb);
};
const defaults = {nonTextBehavior: 'remove'};
function blocksToText(blocks, opts = {}) {
const options = Object.assign({}, defaults, opts)
return blocks
.map(block => {
if (block._type !== 'block' || !block.children) {
return options.nonTextBehavior === 'remove' ? '' : `[${block._type} block]`;
}
return block.children.map(child => child.text).join('');
})
.join('\n\n');
}
service: algolia-sync
provider:
name: aws
runtime: nodejs8.10
plugins:
- serverless-offline
- serverless-offline-scheduler
functions:
indexContent:
handler: handler.indexContent
events:
- schedule: rate(1 day)
environment:
ALGOLIA_APP_ID: 'your_app_id_here'
ALGOLIA_TOKEN: 'your_token_here'
ALGOLIA_INDEX: 'your_index_name_here'
SANITY_PROJECT_ID: 'your_sanity_project_id_here'
SANITY_DATASET: 'your_sanity_dataset_here'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment