Quickstart
SIDE Engine is an AI-native retrieval engine for agents, search and feeds. In this guide you will upload some data and create a searchable index of semantic text and image features.
Using the console? Start with the Launchpad quickstart (MovieLens).
Using AI? Start with our Agent Quickstart.
1. Install the SIDE Engine SDK
- TypeScript
- Python
- CLI
npm install @nextgres/side-client
pip install nextgres-side
pip install nextgres-side
2. Initialize the client with your API key
Add your API key to authenticate into your account. If you don't have an API key yet, see Get your API key.
- TypeScript
- Python
const client = new Client('YOUR_API_KEY');
client = Client(api_key="YOUR_KEY_HERE")
3. Create table and insert data
SIDE Engine stores data in tables. Data can be inserted manually or synced from an external source. Start by declaring a table of movie data and inserting a few rows of sample data.
- Python
- TypeScript
table_config = {
"schema_type": "CUSTOM",
"name": "pixar_movies",
"column_schema": {
"item_id": "Int64",
"movie_title": "String",
"image_url": "String",
"description": "String",
"release_date": "String",
"cast": "Array(String)",
},
}
client.create_table(table_config)
const tableConfig = Table.Custom({
name: 'pixar_movies',
column_schema: {
item_id: 'Int64',
title: 'String',
image_url: 'String',
description: 'String',
release_date: 'String',
cast: 'Array(String)',
},
});
await client.createTable(tableConfig);
Once the table schema is created, you can upload your data directly. You'll declare the rows as an object and then use the table insert method to write to the table.
- Python
- TypeScript
records = [
{"item_id": 187541, "movie_title": "Incredibles 2 (2018)", "image_url": "https://m.media-amazon.com/images/M/MV5BMTEzNzY0OTg0NTdeQTJeQWpwZ15BbWU4MDU3OTg3MjUz._V1_QL75_UX380_CR0,0,380,562_.jpg", "description": "The Incredibles family takes on a new mission which involves a change in family roles: Bob Parr (Mr. Incredible) must manage the house while his wife Helen (Elastigirl) goes out to save the world.", "release_date": "2018-06-15", "cast": ["Craig T. Nelson", "Holly Hunter", "Sarah Vowell", "Huck Milner", "Catherine Keener", "Eli Fucile", "Bob Odenkirk", "Samuel L. Jackson", "Michael Bird", "Sophia Bush", "Brad Bird", "Brad Bird", "Nicole Paradis Grindle", "John Walker", "Michael Giacchino", "Stephen Schaffer", "Natalie Lyon", "Kevin Reher", "Ralph Eggleston"]},
{"item_id": 177765, "movie_title": "Coco (2017)", "image_url": "https://m.media-amazon.com/images/M/MV5BMDIyM2E2NTAtMzlhNy00ZGUxLWI1NjgtZDY5MzhiMDc5NGU3XkEyXkFqcGc@._V1_QL75_UY562_CR7,0,380,562_.jpg", "description": "Aspiring musician Miguel, confronted with his family's ancestral ban on music, enters the Land of the Dead to find his great-great-grandfather, a legendary singer.", "release_date": "2017-11-22", "cast": ["Anthony Gonzalez", "Gael García Bernal", "Benjamin Bratt", "Alanna Ubach", "Renee Victor", "Jaime Camil", "Alfonso Arau", "Herbert Siguenza", "Gabriel Iglesias", "Lombardo Boyar", "Lee Unkrich", "Lee Unkrich", "Jason Katz", "Matthew Aldrich", "Adrian Molina", "Darla K. Anderson", "Michael Giacchino", "Steve Bloom", "Lee Unkrich", "Carla Hool", "Natalie Lyon", "Kevin Reher", "Harley Jessup"]},
... expand 16 lines
const records: Array<Record<string, any>> = [
{"item_id": 187541, "movie_title": "Incredibles 2 (2018)", "image_url": "https://m.media-amazon.com/images/M/MV5BMTEzNzY0OTg0NTdeQTJeQWpwZ15BbWU4MDU3OTg3MjUz._V1_QL75_UX380_CR0,0,380,562_.jpg", "description": "The Incredibles family takes on a new mission which involves a change in family roles: Bob Parr (Mr. Incredible) must manage the house while his wife Helen (Elastigirl) goes out to save the world.", "release_date": "2018-06-15", "cast": ["Craig T. Nelson", "Holly Hunter", "Sarah Vowell", "Huck Milner", "Catherine Keener", "Eli Fucile", "Bob Odenkirk", "Samuel L. Jackson", "Michael Bird", "Sophia Bush", "Brad Bird", "Brad Bird", "Nicole Paradis Grindle", "John Walker", "Michael Giacchino", "Stephen Schaffer", "Natalie Lyon", "Kevin Reher", "Ralph Eggleston"]},
{"item_id": 177765, "movie_title": "Coco (2017)", "image_url": "https://m.media-amazon.com/images/M/MV5BMDIyM2E2NTAtMzlhNy00ZGUxLWI1NjgtZDY5MzhiMDc5NGU3XkEyXkFqcGc@._V1_QL75_UY562_CR7,0,380,562_.jpg", "description": "Aspiring musician Miguel, confronted with his family's ancestral ban on music, enters the Land of the Dead to find his great-great-grandfather, a legendary singer.", "release_date": "2017-11-22", "cast": ["Anthony Gonzalez", "Gael García Bernal", "Benjamin Bratt", "Alanna Ubach", "Renee Victor", "Jaime Camil", "Alfonso Arau", "Herbert Siguenza", "Gabriel Iglesias", "Lombardo Boyar", "Lee Unkrich", "Lee Unkrich", "Jason Katz", "Matthew Aldrich", "Adrian Molina", "Darla K. Anderson", "Michael Giacchino", "Steve Bloom", "Lee Unkrich", "Carla Hool", "Natalie Lyon", "Kevin Reher", "Harley Jessup"]},
... expand 16 lines
Use the insert table rows method to add your data to the table you created:
- Python
- TypeScript
client.insert_table_rows("pixar_movies", records)
await client.insertTableRows('pixar_movies', records);
4. Create a view to enrich your data
Create an AI Enrichment View that uses description and image_url to generate a keyword_tags column for better search and filtering.
- Python
- TypeScript
from side import View
view_config = View.AI(
name="pixar_movie_keyword_tags",
source_dataset="pixar_movies",
source_columns=["description", "image_url", "movie_title", "item_id"],
output_columns=["keyword_tags"],
prompt="Given information about movies such as title, description and a poster image, create a new feature keyword_tags which is a set of descriptive tags that capture the movie's setting and subject matter. Return a comma-separated list of tags with [ and ].",
)
client.create_view(view_config)
import { View } from "@nextgres/side-client";
const viewConfig = View.AI({
name: "pixar_movie_keyword_tags",
source_dataset: "pixar_movies",
source_columns: ["description", "image_url", "title", "item_id"],
source_columns_in_output: ["description", "image_url", "title", "item_id"],
enriched_output_columns: ["keyword_tags"],
prompt: "Given information about movies such as title, description and a poster image, create a new feature keyword_tags which is a set of descriptive tags that capture the movie's setting and subject matter. Return a comma-separated list of tags with [ and ].",
});
await client.createView(viewConfig);
You can create engines at different levels of complexity. Each section below builds an engine and calls create_engine() so you can run it as soon as you finish the step.
5. Create a basic retrieval engine
Create a simple engine that contains your pixar_movies table and no embeddings yet. Call create_engine() to deploy it.
- Python
- TypeScript
from side.autogen.models.engine_config_v2 import EngineConfigV2
from side.autogen.models.data_config import DataConfig
from side.autogen.models.data_config_interaction_table import DataConfigInteractionTable
from side.autogen.models.reference_table_config import ReferenceTableConfig
basic_engine = EngineConfigV2(
name="basic_engine",
data=DataConfig(
item_table=DataConfigInteractionTable(
ReferenceTableConfig(name="pixar_movies")
)
),
)
client.create_engine(engine_config=basic_engine)
const basicEngine = new Engine('basic_engine').items('pixar_movies');
await client.createEngine(basicEngine);
6. Create an engine with text embeddings
SIDE Engine supports any Hugging Face embedding model. We also have multiple in-house embedding models. Add an embedding to enable semantic text search on your engine. This example uses sentence-transformers/all-MiniLM-L6-v2 and encodes the title, description, and cast columns.
- Python
- TypeScript
from side.autogen.models.engine_config_v2 import EngineConfigV2
from side.autogen.models.data_config import DataConfig
from side.autogen.models.data_config_interaction_table import DataConfigInteractionTable
from side.autogen.models.reference_table_config import ReferenceTableConfig
from side.autogen.models.index_config import IndexConfig
from side.autogen.models.embedding_config import EmbeddingConfig
from side.autogen.models.encoder import Encoder
from side.autogen.models.hugging_face_encoder import HuggingFaceEncoder
pixar_engine = EngineConfigV2(
name="pixar_text_search_engine",
data=DataConfig(
item_table=DataConfigInteractionTable(
ReferenceTableConfig(name="pixar_movies")
)
),
index=IndexConfig(
embeddings=[
EmbeddingConfig(
name="movie_text_embedding",
encoder=Encoder(
HuggingFaceEncoder(
model_name="sentence-transformers/all-MiniLM-L6-v2",
item_fields=["movie_title", "description", "cast"],
)
),
)
]
),
)
client.create_engine(engine_config=pixar_engine)
const pixarEngine = new Engine('pixar_text_search_engine')
.items('pixar_movies')
.withEmbedding({
name: 'movie_text_embedding',
encoder: {
type: 'hugging_face',
model_name: 'sentence-transformers/all-MiniLM-L6-v2',
item_fields: ['title', 'description', 'cast'],
},
});
await client.createEngine(pixarEngine);
7. Create an engine with image embeddings
In this example, we add a CLIP image embedding (e.g. jinaai/jina-clip-v2 or openai/clip-vit-base-patch32) to enable text-to-image search. CLIP encodes both images and text into the same vector space.
- Python
- TypeScript
from side.autogen.models.engine_config_v2 import EngineConfigV2
from side.autogen.models.data_config import DataConfig
from side.autogen.models.data_config_interaction_table import DataConfigInteractionTable
from side.autogen.models.reference_table_config import ReferenceTableConfig
from side.autogen.models.index_config import IndexConfig
from side.autogen.models.embedding_config import EmbeddingConfig
from side.autogen.models.encoder import Encoder
from side.autogen.models.hugging_face_encoder import HuggingFaceEncoder
image_engine = EngineConfigV2(
name="image_search_engine",
data=DataConfig(
item_table=DataConfigInteractionTable(
ReferenceTableConfig(name="pixar_movies")
)
),
index=IndexConfig(
embeddings=[
EmbeddingConfig(
name="image_embedding",
encoder=Encoder(
HuggingFaceEncoder(
model_name="openai/clip-vit-base-patch32",
item_fields=["image_url"],
)
),
)
]
),
)
client.create_engine(engine_config=image_engine)
const imageEngine = new Engine('image_search_engine')
.items('pixar_movies')
.withEmbedding({
name: 'image_embedding',
encoder: {
type: 'hugging_face',
model_name: 'openai/clip-vit-base-patch32',
item_fields: ['image_url'],
},
});
await client.createEngine(imageEngine);
8. Create an engine that learns from user behavior
This is where SIDE Engine really shines. Unlike a vector DB or plain semantic search, SIDE Engine lets you train behavioral models on your own data. Turn clicks, likes, and purchases into collaborative embeddings and scoring models that predict engagement. This is how you get truly personalized results that beat generic vector or keyword search.
Add interaction data
Create an interactions table with user_id, item_id, label, created_at, and optional event_value columns. The sample data uses a 5-star rating scale: rated_1_stars through rated_5_stars with matching labels 1–5 so the model learns from explicit feedback strength. Insert the sample data to train your engine.
- Python
- TypeScript
interaction_table_config = {
"schema_type": "CUSTOM",
"name": "pixar_interactions",
"column_schema": {
"user_id": "String",
"item_id": "Int64",
"label": "Int64",
"created_at": "String",
"event_value": "String",
},
}
client.create_table(interaction_table_config)
interactions = [
{"user_id": "user_1", "item_id": 1, "label": 5, "created_at": "2024-01-15", "event_value": "rated_5_stars"},
{"user_id": "user_1", "item_id": 3114, "label": 4, "created_at": "2024-01-16", "event_value": "rated_4_stars"},
{"user_id": "user_1", "item_id": 4886, "label": 3, "created_at": "2024-01-17", "event_value": "rated_3_stars"},
{"user_id": "user_1", "item_id": 6377, "label": 2, "created_at": "2024-01-18", "event_value": "rated_2_stars"},
{"user_id": "user_1", "item_id": 8961, "label": 1, "created_at": "2024-01-19", "event_value": "rated_1_stars"},
{"user_id": "user_2", "item_id": 45517, "label": 5, "created_at": "2024-01-20", "event_value": "rated_5_stars"},
{"user_id": "user_2", "item_id": 50872, "label": 4, "created_at": "2024-01-21", "event_value": "rated_4_stars"},
{"user_id": "user_2", "item_id": 68954, "label": 3, "created_at": "2024-01-22", "event_value": "rated_3_stars"},
{"user_id": "user_2", "item_id": 87876, "label": 2, "created_at": "2024-01-23", "event_value": "rated_2_stars"},
{"user_id": "user_2", "item_id": 95167, "label": 5, "created_at": "2024-01-24", "event_value": "rated_5_stars"},
{"user_id": "user_3", "item_id": 103141, "label": 4, "created_at": "2024-01-25", "event_value": "rated_4_stars"},
{"user_id": "user_3", "item_id": 134853, "label": 5, "created_at": "2024-01-26", "event_value": "rated_5_stars"},
{"user_id": "user_3", "item_id": 136016, "label": 1, "created_at": "2024-01-27", "event_value": "rated_1_stars"},
{"user_id": "user_3", "item_id": 157296, "label": 3, "created_at": "2024-01-28", "event_value": "rated_3_stars"},
{"user_id": "user_3", "item_id": 170957, "label": 2, "created_at": "2024-01-29", "event_value": "rated_2_stars"},
]
client.insert_table_rows("pixar_interactions", interactions)
const interactionTableConfig = Table.Custom({
name: 'pixar_interactions',
column_schema: {
user_id: 'String',
item_id: 'Int64',
label: 'Int64',
created_at: 'String',
event_value: 'String',
},
});
await client.createTable(interactionTableConfig);
const interactions = [
{ user_id: 'user_1', item_id: 1, label: 5, created_at: '2024-01-15', event_value: 'rated_5_stars' },
{ user_id: 'user_1', item_id: 3114, label: 4, created_at: '2024-01-16', event_value: 'rated_4_stars' },
{ user_id: 'user_1', item_id: 4886, label: 3, created_at: '2024-01-17', event_value: 'rated_3_stars' },
{ user_id: 'user_1', item_id: 6377, label: 2, created_at: '2024-01-18', event_value: 'rated_2_stars' },
{ user_id: 'user_1', item_id: 8961, label: 1, created_at: '2024-01-19', event_value: 'rated_1_stars' },
{ user_id: 'user_2', item_id: 45517, label: 5, created_at: '2024-01-20', event_value: 'rated_5_stars' },
{ user_id: 'user_2', item_id: 50872, label: 4, created_at: '2024-01-21', event_value: 'rated_4_stars' },
{ user_id: 'user_2', item_id: 68954, label: 3, created_at: '2024-01-22', event_value: 'rated_3_stars' },
{ user_id: 'user_2', item_id: 87876, label: 2, created_at: '2024-01-23', event_value: 'rated_2_stars' },
{ user_id: 'user_2', item_id: 95167, label: 5, created_at: '2024-01-24', event_value: 'rated_5_stars' },
{ user_id: 'user_3', item_id: 103141, label: 4, created_at: '2024-01-25', event_value: 'rated_4_stars' },
{ user_id: 'user_3', item_id: 134853, label: 5, created_at: '2024-01-26', event_value: 'rated_5_stars' },
{ user_id: 'user_3', item_id: 136016, label: 1, created_at: '2024-01-27', event_value: 'rated_1_stars' },
{ user_id: 'user_3', item_id: 157296, label: 3, created_at: '2024-01-28', event_value: 'rated_3_stars' },
{ user_id: 'user_3', item_id: 170957, label: 2, created_at: '2024-01-29', event_value: 'rated_2_stars' },
];
await client.insertTableRows('pixar_interactions', interactions);
Create the scoring engine
Point the engine at your item table and interaction table, add a GBDT scoring model, and create the engine.
- Python
- TypeScript
from side.autogen.models.engine_config_v2 import EngineConfigV2
from side.autogen.models.data_config import DataConfig
from side.autogen.models.data_config_interaction_table import DataConfigInteractionTable
from side.autogen.models.reference_table_config import ReferenceTableConfig
from side.autogen.models.training_config import TrainingConfig
from side.autogen.models.models_inner import ModelsInner
from side.autogen.models.shaped_internal_recsys_policies_gbdt_gbdt_policy_config import (
ShapedInternalRecsysPoliciesGbdtGBDTPolicyConfig,
)
pixar_scoring_engine = EngineConfigV2(
name="pixar_scoring_engine",
data=DataConfig(
item_table=DataConfigInteractionTable(
ReferenceTableConfig(name="pixar_movies")
),
interaction_table=DataConfigInteractionTable(
ReferenceTableConfig(name="pixar_interactions")
),
),
training=TrainingConfig(
models=[
ModelsInner(
ShapedInternalRecsysPoliciesGbdtGBDTPolicyConfig(
policy_type="gbdt",
name="click_through_rate",
)
)
],
),
)
client.create_engine(engine_config=pixar_scoring_engine)
const pixarScoringEngine = new Engine('pixar_scoring_engine')
.items('pixar_movies')
.interactions('pixar_interactions')
.withTraining({
models: [
{
policy_type: 'gbdt',
name: 'click_through_rate',
},
],
});
await client.createEngine(pixarScoringEngine);
9. Next steps
Once your engine is up and running, you’re ready to integrate it into your application. Read the Queries guide to learn how to get search results using your engine.