import { Settings } from '@algolia/client-search'; import { RequestOptions } from '@algolia/transporter'; import algoliasearch, { SearchIndex } from 'algoliasearch'; export enum AlgoliaIndexKind { PAGES = 'spoken-pages', PAGES_SAVING_DESC = 'spoken-pages-saving-desc', PAGES_PRICE_ASC = 'spoken-pages-price-asc', PAGES_PRICE_DESC = 'spoken-pages-price-desc', PAGES_NAME_ASC = 'spoken-pages-name-asc', PRODUCTS = 'spoken-products', PRODUCTS_SAVING_DESC = 'spoken-products-saving-desc', PRODUCTS_PRICE_ASC = 'spoken-products-price-asc', PRODUCTS_PRICE_DESC = 'spoken-products-price-desc', PRODUCTS_NAME_ASC = 'spoken-products-name-asc', } const cachedIndexes = new Map(); /** * Provides access to the algolia API. This class should be instantiated from * the `fromIndex` method, as it supports caching so new objects aren't * created each time. * @example * ```ts * const searchIndex = AlgoliaSearchIndex.fromIndex(AlgoliaIndex.users); * await searchIndex.createOrUpdateRecord('0', { name: 'A U Thor' }); * ``` */ export default class AlgoliaSearchIndex { private index: SearchIndex; /** * Creates a new AlgoliaSearchIndex object if one does not exist for the * index `indexName`. If one already exists, returns that same obejct. */ static fromIndex(indexName: AlgoliaIndexKind): AlgoliaSearchIndex { const cachedObject = cachedIndexes.get(indexName); if (cachedObject) { return cachedObject; } return new AlgoliaSearchIndex(indexName); } private constructor(indexName: AlgoliaIndexKind) { if (!process.env.ALGOLIA_PROJECT_ID) { throw new Error( 'Cannot connect to algolia without process.env.ALGOLIA_PROJECT_ID' ); } if (!process.env.ALGOLIA_ADMIN_API_KEY) { throw new Error( 'Cannot connect to algolia without process.env.ALGOLIA_ADMIN_API_KEY' ); } const client = algoliasearch( process.env.ALGOLIA_PROJECT_ID, process.env.ALGOLIA_ADMIN_API_KEY ); this.index = client.initIndex(indexName); cachedIndexes.set(indexName, this); } /** * Change settings on index */ setSettings( settings: Settings, options?: RequestOptions ): ReturnType { return this.index.setSettings(settings, options); } /** * Create or updates an object with record at `id`. Will format in the way * that Algolia expects */ async createOrUpdateRecord( id: string, record: { objectID: string } & Record ): Promise { // this strips the id if passed in to data, since algolia expects an // objectId to be consistent, so in case it was passed in, we'd rather // not index this key twice await this.index.partialUpdateObject( { ...record, objectID: id, }, { createIfNotExists: true, } ); } async removeRecord(id: string): Promise { await this.index.deleteObject(id); } async createOrUpdateRecords( records: ({ objectID: string } & Record)[] ): Promise { await this.index.partialUpdateObjects(records, { createIfNotExists: true, }); } }