HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Migrate best bets to pinned results

Migrate Optimizely Search & Navigation best bets to Optimizely Graph pinned results, covers configuration differences, data mapping, API usage, and search query updates.

👍

Early access preview

This content is an early access preview and may change before general availability. Use the thumbs up or down at the end of this article to share feedback and help shape the final release.

Best bets in Optimizely Search & Navigation and pinned results in Optimizely Graph let you promote specific content to the top of search results for defined search phrases. This ensures that users see important or authoritative content before other results.

This guide explains how to migrate existing best bets configurations from Optimizely Search & Navigation to Optimizely Graph pinned results, including API usage, data mapping, and query updates.

When to use pinned results

Use pinned results to:

  • Promote branded or navigational searches. For example, directing users to an About us page for company-related queries.
  • Highlight campaign or seasonal content for specific keywords
  • Ensure critical support or contact information appears first in search results
  • Guide users to official or compliant content for regulatory or policy-related searches

Key differences and migration impact

The following table summarizes the main differences between Search & Navigation best bets and Optimizely Graph pinned results and highlights required migration actions.

FeatureSearch & NavigationGraphMigration impact
ConfigurationAdmin UIREST API onlyBuild an API-based integration (HMAC or Basic authentication required)
ArchitecturePhrase-to-content mappingCollection-basedCreate collections, then add pinned items
Content referenceInteger IDGUIDQuery Graph to map ContentReference.ID to ContentReference.GuidValue
Supported contentInternal and external URLsInternal content onlyMigrate internal content; handle external URLs separately
Title and descriptionCustom or derived from contentNot supportedUse content fields in the application layer
Display stylingConfigurable in UINot supportedImplement styling in the application layer
Language handlingSingle item can apply to all languagesSeparate item per languageCreate one pinned item per language
Priority controlImplicit (insertion order)Explicit priority fieldAssign priorities sequentially (1 is highest)
Result limitUnlimitedMaximum of 5Select the five most important results
Query syntax.ApplyBestBets()pinned parameterUpdate all search queries
Locale handlingAutomaticExplicitAlways specify locale in queries
StatusDelete to removeActive or inactiveUse status for temporary or seasonal items
📘

Note

Both Optimizely Search & Navigation and Optimizely Graph support case-insensitive phrase matching.

Migration workflow

1. Retrieve best bets from Search & Navigation

Use the IBestBetRepository interface to retrieve existing best bets.

Prerequisites

  • EPiServer.Find.Framework
  • EPiServer.Find.Cms
  • Optimizely.ContentGraph.Core
using EPiServer.Find;
using EPiServer.Find.Framework.BestBets;
using EPiServer.Find.Cms;


var allBestBets = _bestBetRepository.List().ToList();


var internalBestBets = allBestBets
.Where(bb => bb.BestBetSelector is PageBestBetSelector)
.ToList();
📘

Note

Optimizely Graph supports internal CMS content only. Best bets that reference external URLs cannot be migrated and must be handled separately.

2. Map content ID to Graph GUID

Optimizely Graph uses content GUIDs instead of integer content Id. Query Graph to retrieve GUID values for the content you plan to pin.

query GetContentIds($ids: [Int!]) {
	Content(where: { ContentLink: { Id: { in: $ids } } }, limit: 100) {
		items {
			ContentLink {
				Id
				GuidValue
			}
		}
	}
}

Store the mapping between ContentReference.ID and ContentReference.GuidValue for later use.

3. Create a pinned results collection

Create a collection to group related pinned results.

Endpoint

POST https://cg.optimizely.com/api/pinned/collections
{
  "title": "Migrated Best Bets",
  "key": "migrated-best-bets",
  "isActive": true
}

Save the collection key for use in GraphQL queries.

4. Add pinned items to the collection

Add each migrated best bet as a pinned item.

Endpoint

POST https://cg.optimizely.com/api/pinned/collections/{id}/items
{
  "phrases": "support",
  "targetKey": "<content-guid>",
  "language": "en",
  "priority": 1
}

5. Update application search queries

Replace Search & Navigation-based search queries with Graph queries that use the pinned parameter.

query SearchWithPinnedResults(
  $term: String!
  $collection: String!
  $locale: [Locales!]
) {
  Content(
    where: { _fulltext: { contains: $term } }
    pinned: { phrase: $term, collections: [$collection] }
    locale: $locale
  ) {
    items {
      Name
      Url
    }
  }
}

Pinned results appear first in the response, followed by regular search results.

Verification and testing

Verify collections and pinned items using the REST API or Swagger UI, then validate search behavior using GraphiQL.

Remove the pinned parameter to compare results with and without pinned content.

Verify via REST API

Use the Swagger UI or direct API calls to verify your migration

Get all collections

GET https://cg.optimizely.com/api/pinned/collections
// Requires HMAC or Basic Auth

Example Response

[
  {
    "id": "collection-guid-here",
    "title": "Migrated Best Bets",
    "key": "migrated-best-bets",
    "isActive": true
  }
]

Get items in a collection

GET https://cg.optimizely.com/api/pinned/collections/{id}/items
// Requires HMAC or Basic Auth

Example Response

[
  {
    "id": "item-guid-here",
    "phrases": "support",
    "targetKey": "12345678-abcd-1234-abcd-1234567890ab",
    "language": "en",
    "priority": 1,
    "isActive": true
  }
]

Swagger UI: https://cg.optimizely.com/app/swagger (interactive API testing)

Test via GraphQL

GraphiQL: https://cg.optimizely.com/app/graphiql?auth=YOUR_SINGLE_KEY

Test Query

query {
  Content(
    where: { _fulltext: { contains: "support" } }
    pinned: {
      phrase: "support"
      collections: ["migrated-best-bets"]        # Use collection key, not ID
      # collections: ["water", "chemistry"]      # Multiple collections supported
    }
    locale: en
  ) {
    items { Name Url }
  }
}