Skip to main content

Overview

Text search combines semantic text understanding with visual similarity to find relevant products. Use the visualWeight parameter to balance between text matching and visual similarity.

Basic Usage

import { Refine } from '@refine-ai/sdk';

const refine = new Refine({
  apiKey: process.env.REFINE_API_KEY,
  organizationId: 'org_abc123',
  catalogId: 'cat_xyz789'
});

const results = await refine.search.text({
  query: 'red running shoes',
  topK: 20
});

results.results.forEach(product => {
  console.log(`${product.title} - $${product.price}`);
});

Parameters

query
string
required
The search query. Natural language queries like “summer dress for wedding” work best.
topK
number
required
Number of results to return. Recommended: 12-48 for grid layouts.
visualWeight
number
default:"0.3"
Balance between text and visual search (0.0 to 1.0).
  • 0.0 = Pure text matching
  • 1.0 = Pure visual similarity
  • 0.3 = Text-primary with visual boost (default)
filters
Filter[]
Array of filters to apply. See Filters for operators.
sortBy
SortBy
Sorting configuration with field and order (‘ascending’ | ‘descending’).

With Filters and Sorting

const results = await refine.search.text({
  query: 'summer dress',
  topK: 24,
  visualWeight: 0.5,
  filters: [
    { field: 'price', operator: 'gte', value: 50 },
    { field: 'price', operator: 'lte', value: 200 },
    { field: 'metadata.color', operator: 'in', value: ['red', 'blue', 'green'] },
    { field: 'stock', operator: 'gt', value: 0 }
  ],
  sortBy: {
    field: 'price',
    order: 'ascending'
  }
});

Response

interface SearchResponse {
  results: SearchResultItem[];
  filterOptions?: FilterOption[];
  serveId: string;
  totalResults: number;
}

interface SearchResultItem {
  productId: string;
  title: string;
  price: number;
  imageUrl: string;
  metadata: Record<string, any>;
  score: number;
}
results
SearchResultItem[]
Array of matching products, ordered by relevance.
filterOptions
FilterOption[]
Available filter values based on the result set. Useful for building faceted navigation.
serveId
string
Unique identifier for this search serve. Used internally for event tracking.
totalResults
number
Total number of matching products (before topK limit). Use for pagination UI.

Tracking Search Results

Always track search results to power analytics and improve recommendations:
const results = await refine.search.text({ query: 'shoes', topK: 20 });

const searchContext = refine.events.trackSearch(
  'shoes',
  results.results,
  { 
    surface: 'search_results',
    totalResults: results.totalResults 
  }
);

// When user clicks a product
searchContext.trackClick(productId, position);

// When a product becomes visible
searchContext.trackView(productId, position);

// When user adds to cart
searchContext.trackAddToCart(productId);
See Event Tracking for more details.

Visual Weight Recommendations

The optimal visualWeight depends on your product category:
CategoryRecommended WeightReason
Fashion/Apparel0.5 - 0.7Visual appearance is primary
Electronics0.2 - 0.4Specs and text matter more
Home Goods0.4 - 0.6Balanced
Art/Decor0.7 - 0.9Almost purely visual
Books0.1 - 0.2Text is primary
Jewelry0.6 - 0.8Visual details matter
A/B test different visual weights to find the optimal balance for your specific catalog and user behavior.

Pagination

Use topK with offset for pagination:
const PAGE_SIZE = 24;
let currentPage = 0;

async function loadPage(page: number) {
  const results = await refine.search.text({
    query: 'shoes',
    topK: PAGE_SIZE,
    // Note: offset is handled server-side with pagination tokens
  });
  
  currentPage = page;
  return results;
}
For best performance, prefer “Load More” patterns over traditional pagination. Each new search request is independent and provides fresh relevance scoring.

Error Handling

import { 
  RefineValidationError, 
  RefineAuthError,
  RefineRateLimitError 
} from '@refine-ai/sdk';

try {
  const results = await refine.search.text({ query: 'shoes', topK: 20 });
} catch (error) {
  if (error instanceof RefineValidationError) {
    console.error('Invalid search parameters:', error.message);
  } else if (error instanceof RefineAuthError) {
    console.error('Check your API key');
  } else if (error instanceof RefineRateLimitError) {
    console.error('Rate limited, retry after:', error.retryAfter);
  }
}

Complete Example

import { Refine, RefineValidationError } from '@refine-ai/sdk';

const refine = new Refine({
  apiKey: process.env.REFINE_API_KEY,
  organizationId: 'org_abc123',
  catalogId: 'cat_xyz789'
});

async function performSearch(query: string, filters: Filter[] = []) {
  try {
    const results = await refine.search.text({
      query,
      topK: 24,
      visualWeight: 0.5,
      filters,
      sortBy: { field: 'relevance', order: 'descending' }
    });

    // Track the search
    const searchContext = refine.events.trackSearch(query, results.results, {
      surface: 'search_results',
      totalResults: results.totalResults
    });

    return {
      products: results.results,
      total: results.totalResults,
      filterOptions: results.filterOptions,
      trackClick: (productId: string, position: number) => {
        searchContext.trackClick(productId, position);
      }
    };
  } catch (error) {
    if (error instanceof RefineValidationError) {
      return { products: [], total: 0, error: 'Invalid search' };
    }
    throw error;
  }
}

Next Steps