216 lines
5.8 KiB
Go
216 lines
5.8 KiB
Go
// File Path: monorepo/cloud/maplepress-backend/pkg/search/index.go
|
|
package search
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/meilisearch/meilisearch-go"
|
|
)
|
|
|
|
// PageDocument represents a document in the search index
|
|
type PageDocument struct {
|
|
ID string `json:"id"` // page_id
|
|
SiteID string `json:"site_id"` // for filtering (though each site has its own index)
|
|
TenantID string `json:"tenant_id"` // for additional isolation
|
|
Title string `json:"title"`
|
|
Content string `json:"content"` // HTML stripped
|
|
Excerpt string `json:"excerpt"`
|
|
URL string `json:"url"`
|
|
Status string `json:"status"` // publish, draft, trash
|
|
PostType string `json:"post_type"` // page, post
|
|
Author string `json:"author"`
|
|
PublishedAt int64 `json:"published_at"` // Unix timestamp for sorting
|
|
ModifiedAt int64 `json:"modified_at"` // Unix timestamp for sorting
|
|
}
|
|
|
|
// CreateIndex creates a new index for a site
|
|
func (c *Client) CreateIndex(siteID string) error {
|
|
indexName := c.GetIndexName(siteID)
|
|
|
|
// Create index with site_id as primary key
|
|
_, err := c.client.CreateIndex(&meilisearch.IndexConfig{
|
|
Uid: indexName,
|
|
PrimaryKey: "id", // page_id is the primary key
|
|
})
|
|
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create index %s: %w", indexName, err)
|
|
}
|
|
|
|
// Configure index settings
|
|
return c.ConfigureIndex(siteID)
|
|
}
|
|
|
|
// ConfigureIndex configures the index settings
|
|
func (c *Client) ConfigureIndex(siteID string) error {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
// Set searchable attributes (in order of priority)
|
|
searchableAttributes := []string{
|
|
"title",
|
|
"excerpt",
|
|
"content",
|
|
}
|
|
|
|
_, err := index.UpdateSearchableAttributes(&searchableAttributes)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set searchable attributes: %w", err)
|
|
}
|
|
|
|
// Set filterable attributes
|
|
filterableAttributes := []interface{}{
|
|
"status",
|
|
"post_type",
|
|
"author",
|
|
"published_at",
|
|
}
|
|
|
|
_, err = index.UpdateFilterableAttributes(&filterableAttributes)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set filterable attributes: %w", err)
|
|
}
|
|
|
|
// Set ranking rules
|
|
rankingRules := []string{
|
|
"words",
|
|
"typo",
|
|
"proximity",
|
|
"attribute",
|
|
"sort",
|
|
"exactness",
|
|
}
|
|
|
|
_, err = index.UpdateRankingRules(&rankingRules)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set ranking rules: %w", err)
|
|
}
|
|
|
|
// Set displayed attributes (don't return full content in search results)
|
|
displayedAttributes := []string{
|
|
"id",
|
|
"title",
|
|
"excerpt",
|
|
"url",
|
|
"status",
|
|
"post_type",
|
|
"author",
|
|
"published_at",
|
|
"modified_at",
|
|
}
|
|
|
|
_, err = index.UpdateDisplayedAttributes(&displayedAttributes)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to set displayed attributes: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// IndexExists checks if an index exists
|
|
func (c *Client) IndexExists(siteID string) (bool, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
|
|
_, err := c.client.GetIndex(indexName)
|
|
if err != nil {
|
|
// Check if error is "index not found" (status code 404)
|
|
if meiliErr, ok := err.(*meilisearch.Error); ok {
|
|
if meiliErr.StatusCode == 404 {
|
|
return false, nil
|
|
}
|
|
}
|
|
return false, fmt.Errorf("failed to check index existence: %w", err)
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
// DeleteIndex deletes an index for a site
|
|
func (c *Client) DeleteIndex(siteID string) error {
|
|
indexName := c.GetIndexName(siteID)
|
|
|
|
_, err := c.client.DeleteIndex(indexName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to delete index %s: %w", indexName, err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// AddDocuments adds or updates documents in the index
|
|
func (c *Client) AddDocuments(siteID string, documents []PageDocument) (*meilisearch.TaskInfo, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
taskInfo, err := index.AddDocuments(documents, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to add documents to index %s: %w", indexName, err)
|
|
}
|
|
|
|
return taskInfo, nil
|
|
}
|
|
|
|
// UpdateDocuments updates documents in the index
|
|
func (c *Client) UpdateDocuments(siteID string, documents []PageDocument) (*meilisearch.TaskInfo, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
taskInfo, err := index.UpdateDocuments(documents, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to update documents in index %s: %w", indexName, err)
|
|
}
|
|
|
|
return taskInfo, nil
|
|
}
|
|
|
|
// DeleteDocument deletes a single document from the index
|
|
func (c *Client) DeleteDocument(siteID string, documentID string) (*meilisearch.TaskInfo, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
taskInfo, err := index.DeleteDocument(documentID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to delete document %s from index %s: %w", documentID, indexName, err)
|
|
}
|
|
|
|
return taskInfo, nil
|
|
}
|
|
|
|
// DeleteDocuments deletes multiple documents from the index
|
|
func (c *Client) DeleteDocuments(siteID string, documentIDs []string) (*meilisearch.TaskInfo, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
taskInfo, err := index.DeleteDocuments(documentIDs)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to delete documents from index %s: %w", indexName, err)
|
|
}
|
|
|
|
return taskInfo, nil
|
|
}
|
|
|
|
// DeleteAllDocuments deletes all documents from the index
|
|
func (c *Client) DeleteAllDocuments(siteID string) (*meilisearch.TaskInfo, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
taskInfo, err := index.DeleteAllDocuments()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to delete all documents from index %s: %w", indexName, err)
|
|
}
|
|
|
|
return taskInfo, nil
|
|
}
|
|
|
|
// GetStats returns statistics about an index
|
|
func (c *Client) GetStats(siteID string) (*meilisearch.StatsIndex, error) {
|
|
indexName := c.GetIndexName(siteID)
|
|
index := c.client.Index(indexName)
|
|
|
|
stats, err := index.GetStats()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get stats for index %s: %w", indexName, err)
|
|
}
|
|
|
|
return stats, nil
|
|
}
|