Skip to content

AACSearch/strapi-plugin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1 Commit
ย 
ย 

Repository files navigation

AACSearch Strapi Plugin

Strapi plugin for AACSearch - Integrate search capabilities into your headless CMS.

๐Ÿš€ Features

  • Real-Time Indexing: Automatic content synchronization with AACSearch
  • Multi-Language Support: Index content in multiple locales
  • Custom Content Types: Support for any Strapi content type
  • Advanced Relations: Index relational data and populate fields
  • Webhook Integration: Real-time updates via Strapi lifecycle hooks
  • Admin Panel: Manage search settings and view analytics
  • Draft Preview: Index and search draft content (optional)

๐Ÿ“ฆ Installation

# npm
npm install @aacsearch/strapi-plugin

# yarn
yarn add @aacsearch/strapi-plugin

# pnpm
pnpm add @aacsearch/strapi-plugin

๐Ÿ›  Configuration

Plugin Registration

// config/plugins.js
module.exports = {
  'aacsearch': {
    enabled: true,
    config: {
      apiKey: env('AACSEARCH_API_KEY'),
      organizationId: env('AACSEARCH_ORG_ID'),
      baseUrl: env('AACSEARCH_BASE_URL', 'https://api.aacsearch.com'),
      indexName: env('AACSEARCH_INDEX_NAME', 'strapi-content'),
      
      // Content types to index
      contentTypes: {
        'api::article.article': {
          enabled: true,
          fields: ['title', 'content', 'excerpt'],
          populate: ['author', 'category'],
          publicationState: 'published' // 'published' | 'preview' | 'both'
        },
        'api::page.page': {
          enabled: true,
          fields: ['title', 'body', 'seo.metaDescription'],
          populate: ['sections.image']
        }
      },
      
      // Indexing options
      indexing: {
        batchSize: 100,
        delayBetweenBatches: 1000,
        includeUnpublished: false,
        includeDrafts: false
      },
      
      // Webhook settings
      webhooks: {
        enabled: true,
        events: ['create', 'update', 'delete', 'publish', 'unpublish']
      }
    }
  }
};

Environment Variables

# .env
AACSEARCH_API_KEY=your_api_key_here
AACSEARCH_ORG_ID=your_organization_id
AACSEARCH_INDEX_NAME=my-strapi-site

๐Ÿ“‹ Content Type Configuration

Basic Configuration

// Content type configuration
contentTypes: {
  'api::product.product': {
    enabled: true,
    fields: [
      'name',
      'description', 
      'price',
      'sku'
    ],
    populate: [
      'category',
      'images',
      'variants.color'
    ],
    transformData: (entry) => {
      // Custom data transformation
      return {
        ...entry,
        searchableText: `${entry.name} ${entry.description}`,
        priceRange: getPriceRange(entry.price)
      };
    }
  }
}

Advanced Field Mapping

contentTypes: {
  'api::article.article': {
    enabled: true,
    fieldMapping: {
      // Map Strapi fields to search index fields
      'title': 'title',
      'content': 'body',
      'publishedAt': 'published_date',
      'author.name': 'author_name',
      'category.name': 'category'
    },
    filters: {
      // Only index published articles
      publishedAt: { $notNull: true },
      // Exclude private articles
      private: { $eq: false }
    }
  }
}

Multilingual Content

contentTypes: {
  'api::article.article': {
    enabled: true,
    localized: true,
    localeStrategy: 'separate', // 'separate' | 'combined'
    fields: ['title', 'content'],
    populate: ['localizations']
  }
}

๐Ÿ”ง API Usage

Manual Indexing

// services/search.js
module.exports = ({ strapi }) => ({
  async indexContent(contentType, id) {
    const entry = await strapi.entityService.findOne(contentType, id, {
      populate: '*'
    });
    
    return await strapi.plugin('aacsearch').service('indexing').indexEntry(
      contentType, 
      entry
    );
  },
  
  async searchContent(query, options = {}) {
    return await strapi.plugin('aacsearch').service('search').search(
      query, 
      options
    );
  }
});

Custom Controllers

// api/search/controllers/search.js
module.exports = {
  async search(ctx) {
    const { query, filters, limit = 20, offset = 0 } = ctx.query;
    
    try {
      const results = await strapi.plugin('aacsearch').service('search').search({
        query,
        filters,
        limit: parseInt(limit),
        offset: parseInt(offset)
      });
      
      ctx.body = results;
    } catch (error) {
      ctx.throw(500, 'Search failed', { error });
    }
  },
  
  async reindex(ctx) {
    const { contentType } = ctx.params;
    
    try {
      await strapi.plugin('aacsearch').service('indexing').reindexContentType(contentType);
      ctx.body = { message: 'Reindexing started' };
    } catch (error) {
      ctx.throw(500, 'Reindexing failed', { error });
    }
  }
};

Frontend Integration

// Next.js example
export async function getStaticProps() {
  const searchResults = await fetch(`${process.env.STRAPI_URL}/api/search?query=react`);
  const results = await searchResults.json();
  
  return {
    props: { results }
  };
}

// React component
function SearchComponent() {
  const [query, setQuery] = useState('');
  const [results, setResults] = useState([]);
  
  const handleSearch = async () => {
    const response = await fetch(`/api/search?query=${encodeURIComponent(query)}`);
    const data = await response.json();
    setResults(data.documents || []);
  };
  
  return (
    <div>
      <input 
        value={query} 
        onChange={(e) => setQuery(e.target.value)}
        onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
      />
      <button onClick={handleSearch}>Search</button>
      
      {results.map(result => (
        <div key={result.id}>
          <h3>{result.title}</h3>
          <p>{result.excerpt}</p>
        </div>
      ))}
    </div>
  );
}

๐Ÿ”„ Lifecycle Hooks

Automatic Indexing

The plugin automatically handles content changes through Strapi lifecycle hooks:

// Automatically handled by the plugin
// - afterCreate: Index new entries
// - afterUpdate: Update search index
// - afterDelete: Remove from index
// - afterPublish: Index published content
// - afterUnpublish: Remove unpublished content

Custom Hooks

// config/functions/bootstrap.js
module.exports = async () => {
  // Custom indexing logic
  strapi.db.lifecycles.subscribe({
    models: ['api::article.article'],
    
    async afterUpdate(event) {
      const { model, result } = event;
      
      // Custom logic before indexing
      if (result.featured) {
        await strapi.plugin('aacsearch').service('indexing').prioritizeEntry(
          model.uid,
          result.id
        );
      }
    }
  });
};

๐ŸŽ› Admin Panel

Search Settings

Navigate to Settings โ†’ AACSearch:

  • Configure API credentials
  • Enable/disable content types
  • Set field mappings
  • View indexing status
  • Trigger manual re-indexing

Search Analytics

View search performance in AACSearch โ†’ Analytics:

  • Popular search queries
  • Search volume trends
  • Zero-result searches
  • Click-through rates

Content Manager Integration

The plugin adds search capabilities to the Content Manager:

  • Quick search across all content types
  • Filter by content type
  • Search within specific fields
  • Advanced search with multiple criteria

๐Ÿ›  CLI Commands

# Strapi CLI commands
npm run strapi aacsearch:reindex          # Reindex all content
npm run strapi aacsearch:reindex --type=api::article.article  # Reindex specific type
npm run strapi aacsearch:clear            # Clear search index
npm run strapi aacsearch:status           # Show indexing status
npm run strapi aacsearch:sync             # Sync configuration

๐Ÿ“Š Advanced Features

Custom Search Endpoints

// api/search/routes/search.js
module.exports = {
  routes: [
    {
      method: 'GET',
      path: '/search',
      handler: 'search.search',
      config: {
        auth: false, // or configure authentication
        rateLimit: {
          max: 100,
          duration: 60000
        }
      }
    },
    {
      method: 'POST',
      path: '/search/reindex/:contentType',
      handler: 'search.reindex',
      config: {
        policies: ['admin::isAuthenticatedAdmin']
      }
    }
  ]
};

Search Result Transformation

// Custom result transformation
contentTypes: {
  'api::product.product': {
    enabled: true,
    fields: ['name', 'description'],
    transformResult: (hit, originalEntry) => {
      return {
        ...hit,
        url: `/products/${originalEntry.slug}`,
        thumbnail: originalEntry.images?.[0]?.url,
        inStock: originalEntry.inventory > 0
      };
    }
  }
}

Faceted Search

// Configure facets for filtering
contentTypes: {
  'api::article.article': {
    enabled: true,
    facets: [
      'category.name',
      'author.name',
      'tags.name'
    ],
    numericFacets: [
      'publishedAt',
      'readTime'
    ]
  }
}

๐Ÿ”— Links

๐Ÿ“„ License

MIT ยฉ AACSearch

About

Strapi plugin for AACSearch - Integrate search capabilities into your headless CMS

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors