
{"id":9014,"date":"2026-10-27T16:17:41","date_gmt":"2026-10-27T15:17:41","guid":{"rendered":"https:\/\/dasini.net\/blog\/?p=9014"},"modified":"2026-10-27T16:17:45","modified_gmt":"2026-10-27T15:17:45","slug":"build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai","status":"publish","type":"post","link":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/","title":{"rendered":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\" style=\"font-size:15px\"><br><em><span style=\"text-decoration: underline;\">Note on model availability<\/span>: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the embedding model, regenerate the stored embeddings and use the same model for searches.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\" id=\"top\">In my previous series, we built a search engine for this blog using MySQL HeatWave GenAI. We started with title and excerpt embeddings, added article summaries and weighted ranking, then explored searching the full content through chunks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This time, let&rsquo;s keep MySQL HeatWave for storing and searching vectors, and call OCI Generative AI directly from Python to generate summaries, keywords, and embeddings.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The goal remains the same: help readers find relevant articles. This alternative gives the application responsibility for the model calls and the indexing workflow. It is another option to consider alongside the integrated approach described in the earlier articles.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For the background, see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/dasini.net\/blog\/2025\/03\/13\/build-an-ai-powered-search-engine-with-heatwave-genai-part-1\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 1)\">Part 1 \u2014 Embeddings and vector similarity search<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/dasini.net\/blog\/2025\/04\/08\/build-an-ai-powered-search-engine-with-heatwave-genai-part-2\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 2)\">Part 2 \u2014 Summaries and weighted ranking<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\">Part 3 \u2014 Searching <\/a><a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\" title=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\">full<\/a><a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 3)\"> articles with chunks<\/a><\/li>\n<\/ul>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">What are we going to build<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The Python demo reads published WordPress posts, creates a summary and keywords for each article, and generates separate embeddings for its title, excerpt, and summary. A Flask interface offers semantic, keyword, and hybrid search.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This follows the article-level representation used in part 2. The full article contributes through its generated summary; the demo does not embed every passage as part 3 did. That distinction matters for queries about details that a summary may omit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It is also a retrieval application. The LLM enriches the index, but it does not write an answer to each user&rsquo;s question. The interface returns article links and excerpts. A subsequent answer-generation step could turn this into a RAG application, but that step is outside this demo.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Who does what in this architecture<\/h2>\n\n\n\n<div class=\"wp-block-group is-layout-grid wp-container-core-group-is-layout-1e2e5166 wp-block-group-is-layout-grid\">\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Responsibility<\/strong><\/td><td><strong>Earlier examples<\/strong><\/td><td><strong>This article<\/strong><\/td><\/tr><\/thead><tbody><tr><td>Generate embeddings<\/td><td>HeatWave GenAI routines using MiniLM<\/td><td>Python calls OCI Generative AI<\/td><\/tr><tr><td>Summarize articles<\/td><td>HeatWave JavaScript API and an in-database LLM in <a href=\"https:\/\/dasini.net\/blog\/2025\/04\/08\/build-an-ai-powered-search-engine-with-heatwave-genai-part-2\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 2)\">part 2<\/a><\/td><td>Python calls a Cohere chat model on OCI<\/td><\/tr><tr><td>Prepare text<\/td><td>JavaScript in <a href=\"https:\/\/dasini.net\/blog\/2025\/04\/08\/build-an-ai-powered-search-engine-with-heatwave-genai-part-2\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 2)\">part 2<\/a> and Python in <a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 3)\">part 3<\/a><\/td><td>Python HTML parsing<\/td><\/tr><tr><td>Store searchable data<\/td><td>MySQL HeatWave tables and vectors<\/td><td>MySQL HeatWave tables and vectors<\/td><\/tr><tr><td>Compute vector distances<\/td><td>SQL DISTANCE<\/td><td>SQL DISTANCE<\/td><\/tr><tr><td>Coordinate processing<\/td><td>SQL routines, with Python in <a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 3)\">part 3<\/a><\/td><td>Python scripts and Flask<\/td><\/tr><\/tbody><\/table><\/figure>\n<\/div>\n\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">During indexing, Python reads WordPress posts from MySQL, extracts their text, requests summaries and keywords from OCI, requests embeddings, and writes the results back to MySQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">During semantic search, Flask sends the query text to OCI&rsquo;s embedding endpoint. Python receives a numerical vector and passes it to a parameterized SQL query. MySQL converts it to a VECTOR and ranks the stored articles. Hybrid search also runs the keyword query, then combines both rankings in Python. Keyword-only searches make no OCI call.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are <strong>no calls<\/strong> to <a href=\"https:\/\/dev.mysql.com\/doc\/heatwave\/en\/mys-hwgenai-ml-embed-row.html\" target=\"_blank\" rel=\"noopener\" title=\"ML_EMBED_ROW\">sys.ML_EMBED_ROW<\/a>, <a href=\"https:\/\/dev.mysql.com\/doc\/heatwave\/en\/mys-hwgenai-ml-embed-table.html\" target=\"_blank\" rel=\"noopener\" title=\"ML_EMBED_TABLE\">sys.ML_EMBED_TABLE<\/a>, or the MySQL HeatWave <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/9.7\/en\/srjs-genai-api.html\" target=\"_blank\" rel=\"noopener\" title=\"JavaScript GenAI API\">JavaScript GenAI API<\/a> in this implementation. <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/9.7\/en\/vector-functions.html#function_string-to-vector\" target=\"_blank\" rel=\"noopener\" title=\"STRING_TO_VECTOR()\">STRING_TO_VECTOR<\/a> and <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/9.7\/en\/vector-functions.html#function_distance\" target=\"_blank\" rel=\"noopener\" title=\"DISTANCE()\">DISTANCE<\/a> remain essential database operations: neither invokes an LLM.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">MySQL HeatWave can also integrate with OCI models through its own GenAI interfaces, as <a href=\"https:\/\/dasini.net\/blog\/2025\/03\/13\/build-an-ai-powered-search-engine-with-heatwave-genai-part-1\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 1)\">part 1<\/a> already noted. The distinguishing choice here is to call OCI directly from the application. The comparison concerns these implementations, rather than a limit on what either service can do.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Choose the models and serving mode<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The original demo uses <strong>cohere.command-a-03-2025 for summaries and keywords<\/strong>, and <strong>cohere.embed-multilingual-v3.0 for embeddings<\/strong>. These are separate jobs: <strong>a chat model produces text<\/strong>; <strong>an embedding model produces the vectors used for retrieval<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For a new deployment, check the <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/deprecating-on-demand.htm\">current model <\/a><a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/deprecating-on-demand.htm\" target=\"_blank\" rel=\"noopener\">retirement<\/a><a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/deprecating-on-demand.htm\"> <\/a><a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/deprecating-on-demand.htm\" target=\"_blank\" rel=\"noopener\" title=\"Model Retirement Dates (On-Demand Mode)\">schedule<\/a>. Oracle\u2019s schedule listed September 30, 2026 as the on-demand retirement date for Cohere Embed Multilingual 3, with Embed 4 as the suggested replacement. The shared scripts require explicit model IDs in the environment configuration rather than relying on the original default.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>The helper already contains an Embed 4 branch that requests 1,024 output dimensions. This matches the demo&rsquo;s VECTOR(1024) columns. Embed 4 otherwise defaults to 1,536 dimensions; both sizes are supported through the API. See the <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/cohere-embed-4.htm\" target=\"_blank\" rel=\"noopener\" title=\"Cohere Embed 4\">Embed 4 documentation<\/a>.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><em>Region and serving mode must be chosen together, and availability may have changed since this article was written. The demo constructs OnDemandServingMode requests, so select a region that currently supports your chosen models on demand. Using a dedicated endpoint requires adapting the client; changing only the model ID is insufficient. Consult the <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/model-endpoint-regions.htm\" target=\"_blank\" rel=\"noopener\" title=\"Generative AI Models by Region\">current models-by-region documentation<\/a> before running the example.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A model change also requires rebuilding the document embeddings and changing the query model to match. Two models can both return 1,024 numbers without producing compatible vectors. Keep the model ID, output dimensions, and preprocessing version with the index configuration, and switch the application only after the new index is ready.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Prepare the database and Python environment<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use a MySQL HeatWave deployment with the vector functions required by the query. Having a <code>VECTOR<\/code> column alone does not establish that a database supports <code>DISTANCE<\/code>; consult the <a href=\"https:\/\/dev.mysql.com\/doc\/refman\/9.7\/en\/vector-functions.html#function_distance\" target=\"_blank\" rel=\"noopener\" title=\"MySQL's Vector Functions\">vector function documentation<\/a> for your deployment and version.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The following minimal schema is included as <strong><em>schema.sql<\/em><\/strong> with the shared code. Run it in the configured database before ingestion; the scripts assume the destination table already exists:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"sql\" class=\"language-sql\">CREATE TABLE wp_posts_embeddings_cohere (\n    ID BIGINT UNSIGNED NOT NULL PRIMARY KEY,\n    post_title TEXT NOT NULL,\n    post_excerpt TEXT NOT NULL,\n    guid VARCHAR(255) NOT NULL,\n    post_summary TEXT NOT NULL,\n    post_keywords JSON NOT NULL,\n    post_title_embedding VECTOR(1024) NOT NULL,\n    post_excerpt_embedding VECTOR(1024) NOT NULL,\n    post_summary_embedding VECTOR(1024) NOT NULL,\n    FULLTEXT KEY title_excerpt_summary\n        (post_title, post_excerpt, post_summary)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The keyword query can run without a JSON multi-valued index. Such an index is an optional optimization for JSON_OVERLAPS; verify its definition and the actual execution plan on the target deployment. The <code>FULLTEXT<\/code> index over the three MATCH columns is required for the demonstrated full-text query.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The Python dependencies are oci, mysql-connector-python, and Flask. Configure OCI SDK authentication and permissions for inference in the chosen compartment, plus connectivity and credentials for MySQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br><br>The scripts share configuration through <em><strong>settings.py<\/strong><\/em>. They read OCI_COMPARTMENT_ID, OCI_GENAI_ENDPOINT, OCI_CONFIG_FILE, OCI_CONFIG_PROFILE, OCI_EMBEDDING_MODEL_ID, and the MYSQL_* settings from environment variables. Ingestion also requires OCI_SUMMARY_MODEL_ID for a compatible Cohere chat model. The scripts do not load a .env file automatically.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br>Copy <strong><em>.env.example<\/em><\/strong> to .env, replace the placeholders, and export the settings as described in <strong><em>README.md<\/em><\/strong>. The shared scripts contain no hard-coded credentials. The package includes <em><strong>requirements.txt<\/strong><\/em> and .gitignore, and <em>index.html<\/em> is already placed under Flask\u2019s templates directory. The web application uses the embedding model selected by the server configuration, so the search form cannot override it.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Generate summaries and keywords with OCI<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The ingestion script selects published posts, removes HTML markup, ignores script and style content, and normalizes whitespace. Empty excerpts fall back to the title; empty article content falls back to the excerpt or title.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It then calls OCI&rsquo;s chat API. This condensed extract shows the request structure used in <em><strong><code>generate_embeddings_gpt.py<\/code><\/strong><\/em>; imports and response validation are omitted here for readability.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">request = CohereChatRequest(\n    message=(\n        \"Summarize the following sanitized blog post. \"\n        \"Return a concise, factual summary and exactly \"\n        \"five distinct, relevant keywords.\\n\\n\"\n        f\"Post:\\n{content}\"\n    ),\n    response_format=CohereResponseJsonFormat(schema={\n        \"type\": \"object\",\n        \"properties\": {\n            \"summary\": {\"type\": \"string\"},\n            \"keywords\": {\n                \"type\": \"array\",\n                \"items\": {\"type\": \"string\"},\n            },\n        },\n        \"required\": [\"summary\", \"keywords\"],\n        \"additionalProperties\": False,\n    }),\n    max_tokens=400,\n    temperature=0,\n)\nresponse = helper.client.chat(ChatDetails(\n    serving_mode=OnDemandServingMode(\n        model_id=SUMMARY_MODEL_ID\n    ),\n    compartment_id=helper.compartment_id,\n    chat_request=request,\n))\npayload = json.loads(response.data.chat_response.text)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Structured output makes the summary and keywords easier to consume; see <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/tools\/python\/latest\/api\/generative_ai_inference\/models\/oci.generative_ai_inference.models.CohereResponseJsonFormat.html\" target=\"_blank\" rel=\"noopener\" title=\"CohereResponseJsonFormat\">CohereResponseJsonFormat<\/a>. <strong>It does not establish that the summary is factually correct<\/strong>. The prompt requests five distinct keywords, but the supplied schema and Python filtering do not enforce that count or uniqueness. Validate these properties explicitly if they are part of the application&rsquo;s contract.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">HTML cleanup also <strong>does not make article text safe to treat as instructions<\/strong>. Keep source content separate from trusted task instructions and inspect generated summaries, especially when indexing content from outside your control.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Generate and store document embeddings<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">OCIEmbeddingHelper wraps the SDK client and serializes the returned vector with json.dumps. The following excerpt preserves its main behavior while making input_type explicit. The helper&rsquo;s client is configured with OCI&rsquo;s default retry strategy.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">details = {\n    \"serving_mode\": OnDemandServingMode(model_id=model_id),\n    \"compartment_id\": self.compartment_id,\n    \"inputs\": [text],\n    \"input_type\": input_type,\n    \"truncate\": \"END\",\n}\nif model_id == \"cohere.embed-v4.0\":\n    details[\"output_dimensions\"] = 1024\n\nresponse = self.client.embed_text(EmbedTextDetails(**details))\nvector = response.data.embeddings[0]\nsql_vector = json.dumps(vector)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Use SEARCH_DOCUMENT for stored text and SEARCH_QUERY for user searches. These paired input types tell the model how the text will be used. The OCI SDK parameter names and accepted values are documented in <a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/tools\/python\/latest\/api\/generative_ai_inference\/models\/oci.generative_ai_inference.models.EmbedTextDetails.html\" target=\"_blank\" rel=\"noopener\" title=\"EmbedTextDetails\">EmbedTextDetails<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><br>The indexing loop creates three embeddings per article:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">_, title_vector = helper.embed(\n    title, EMBEDDING_MODEL_ID, \"SEARCH_DOCUMENT\")\n_, excerpt_vector = helper.embed(\n    excerpt, EMBEDDING_MODEL_ID, \"SEARCH_DOCUMENT\")\n_, summary_vector = helper.embed(\n    summary, EMBEDDING_MODEL_ID, \"SEARCH_DOCUMENT\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All three vectors go into the same row as the article metadata. Here is the insertion pattern, condensed from the demo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">writer.execute(\"\"\"\n    INSERT INTO wp_posts_embeddings_cohere\n      (ID, post_title, post_excerpt, guid,\n       post_title_embedding, post_excerpt_embedding,\n       post_summary, post_summary_embedding, post_keywords)\n    VALUES (%s, %s, %s, %s,\n            STRING_TO_VECTOR(%s), STRING_TO_VECTOR(%s),\n            %s, STRING_TO_VECTOR(%s), %s)\n\"\"\", (row[\"ID\"], title, excerpt, row[\"guid\"],\n      title_vector, excerpt_vector, summary,\n      summary_vector, keywords))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">OCI produces the vector values. MySQL converts their JSON string representation into its native vector format. Passing the values as parameters keeps the vector text out of the SQL statement itself.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The helper uses truncate=\u00a0\u00bbEND\u00a0\u00bb, so text exceeding the embedding model&rsquo;s limit can lose its final tokens. Account for that when choosing summary length. For longer documents, the chunking approach from <a href=\"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/\" target=\"_blank\" rel=\"noopener\" title=\"Build an AI-Powered Search Engine with HeatWave GenAI (part 3)\">part 3<\/a> remains useful; the current ingestion script sends the cleaned article to the summarizer without an explicit length-management step.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Search with an OCI query embedding and SQL distances<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The query follows the same boundary between services: Python generates an embedding, then SQL performs retrieval. The following is a shortened equivalent of the weighted semantic query in <strong><em><code>app.py<\/code><\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"SQL\" class=\"language-SQL\">_, query_vector = helper.embed(\n    query, EMBEDDING_MODEL_ID, \"SEARCH_QUERY\")\n\ncursor.execute(\"\"\"\n    WITH q AS (\n        SELECT STRING_TO_VECTOR(%s) AS embedding\n    )\n    SELECT ID, post_title, post_excerpt, guid,\n      0.1 * DISTANCE(q.embedding,\n                    post_title_embedding, 'COSINE') +\n      0.2 * DISTANCE(q.embedding,\n                    post_excerpt_embedding, 'COSINE') +\n      0.7 * DISTANCE(q.embedding,\n                    post_summary_embedding, 'COSINE')\n          AS combined_distance\n    FROM wp_posts_embeddings_cohere\n    CROSS JOIN q\n    ORDER BY combined_distance ASC, ID ASC\n    LIMIT %s\n\"\"\", (query_vector, limit))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Lower distance means a closer match. The weights assign 10% to the title, 20% to the excerpt, and 70% to the summary. These are starting values for this demo, not measured optimal settings. ID supplies a stable tie-breaker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A semantic query returns the nearest available articles even when none is particularly relevant. Returning ten results does not imply ten good matches. Evaluate a relevance threshold or a suitable empty-result policy using representative queries.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The statement expresses a weighted distance ranking. It does not demonstrate approximate nearest-neighbor indexing, a particular offload plan, or a measured latency. Use execution plans and timings to establish how it behaves on your deployment.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Add keyword and hybrid search<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Semantic search is useful for paraphrases and related concepts. Literal names, acronyms, and technical identifiers also deserve a keyword route. The demo provides both.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The keyword score combines MySQL full-text relevance over title, excerpt, and summary with an exact overlap against generated keyword strings:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"sql\" class=\"language-sql\">0.7 * MATCH(post_title, post_excerpt, post_summary)\n      AGAINST(%s IN NATURAL LANGUAGE MODE)\n+ 0.3 * COALESCE(JSON_OVERLAPS(post_keywords, %s), 0)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Python splits the keyword input on commas before building the JSON array. <code><em>JSON_OVERLAPS<\/em><\/code> tests shared values; it does not provide semantic matching or partial phrase matching. Establish a consistent normalization policy for generated keywords and query terms.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The coefficients do not mean that full-text and keyword overlap contribute calibrated percentages of relevance. Full-text scores are variable, while <code><em>JSON_OVERLAPS<\/em><\/code> contributes a binary value. The overlap therefore acts as a fixed bonus in this implementation.<\/p>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Hybrid search combines positions in the two result lists using reciprocal-rank fusion. This compact equivalent preserves the demo&rsquo;s equal contributions and constant of 60:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">merged = {}\nfor results in (semantic_results, keyword_results):\n    for rank, result in enumerate(results, start=1):\n        item = merged.setdefault(\n            result[\"ID\"], {**result, \"fusion_score\": 0.0})\n        item[\"fusion_score\"] += 1.0 \/ (60 + rank)\n\nresults = sorted(\n    merged.values(),\n    key=lambda item: (-item[\"fusion_score\"], item[\"ID\"]),\n)[:limit]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">An article present in both rankings receives both contributions. Higher fusion scores rank first. This avoids adding cosine distances directly to full-text relevance scores, which have different scales and directions.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The demo retrieves ten candidates from each method by default and returns ten fused results. Increasing the candidate pools independently of the final result count is a useful experiment. Fusion cannot recover an article absent from both lists.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This is rank fusion, with no additional LLM or dedicated reranking model involved. The actual Flask implementation also preserves source labels so readers can see which methods found each article. Its displayed distances, keyword scores, and fusion scores are different quantities, not probabilities.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Benefits and trade offs<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The direct SDK architecture suits a team that wants to own preprocessing, model requests, structured responses, and retry behavior in Python. These components can be tested and deployed with the application, and model experimentation can proceed independently of stored-routine changes. MySQL HeatWave remains the place where article metadata and vectors are joined and retrieved.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That flexibility carries operational work. The application now coordinates database access and inference access, manages two failure paths, and must keep model configuration consistent across indexing and search. On-demand inference introduces service usage charges and potential throttling, in addition to the database and application costs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The earlier integrated implementation keeps more of this workflow behind SQL and HeatWave&rsquo;s GenAI interfaces. That can be attractive for teams who already organize data processing around the database. Using its embedded models also avoids adding a separate OCI inference service dependency for those model calls. The appropriate choice depends on the models, controls, and deployment practices the application needs.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><td><strong>Consideration<\/strong><\/td><td><strong>Integrated approach from the series<\/strong><\/td><td><strong>Direct OCI SDK approach<\/strong><\/td><\/tr><\/thead><tbody><tr><td>Application code<\/td><td>Model work exposed through database APIs<\/td><td>Application explicitly manages model requests<\/td><\/tr><tr><td>Change ownership<\/td><td>More behavior maintained with database routines<\/td><td>More behavior maintained with Python services<\/td><\/tr><tr><td>Data movement<\/td><td>Embedded model work stays within MySQL HeatWave<\/td><td>Article text and queries go to the configured OCI endpoint<\/td><\/tr><tr><td>Operations<\/td><td>Database and integrated model workflow<\/td><td>Database plus OCI authentication, quotas, and network calls<\/td><\/tr><tr><td>Cost evaluation<\/td><td>Database and embedded-model resource needs<\/td><td>Database plus inference usage and application operations<\/td><\/tr><tr><td>Quality evaluation<\/td><td>Depends on representation, models, and ranking<\/td><td>Depends on representation, models, and ranking<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">These are architectural considerations, not benchmark results. Neither design guarantees better relevance or lower latency. Compare them with the same corpus and judged queries, and distinguish a change of model from a change of orchestration.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ignoring retries, the current ingestion loop makes one chat request and three embedding requests per article. Each semantic or hybrid search adds one embedding request; keyword-only search adds none. Committing every 100 rows groups database transactions, but does not batch OCI calls. This request pattern is a useful starting point for cost and throughput estimates, rather than a monetary estimate by itself.<\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">P\u00e9roraison<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">MySQL HeatWave and OCI Generative AI can share the work in this way: OCI handles model inference, Python coordinates the application, and SQL handles retrieval. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The integrated solution from the earlier series remains a valid choice. This demo adds an alternative for applications that need direct control of their model calls while keeping search data in MySQL.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"#top\"><em>Back to top<\/em><\/a><\/p>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/www.oracle.com\/mysql\/\" target=\"_blank\" rel=\"noopener\" title=\"MySQL HeatWave Overview\">MySQL HeatWave Overview<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/dev.mysql.com\/doc\/heatwave\/en\/\" target=\"_blank\" rel=\"noopener\" title=\"HeatWave User Guide\">HeatWave User Guide<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.mysql.com\/\" target=\"_blank\" rel=\"noopener\" title=\"MySQL\">MySQL<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.oracle.com\/cloud\/\" target=\"_blank\" rel=\"noopener\" title=\"Oracle Cloud Infrastructure\">Oracle Cloud Overview<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/docs.oracle.com\/en-us\/iaas\/Content\/generative-ai\/home.htm\" target=\"_blank\" rel=\"noopener\" title=\"OCI Generative AI\">OCI Generative AI<\/a><\/li>\n<\/ul>\n\n\n\n<div style=\"height:50px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<div style=\"height:20px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"wp-block-paragraph\"><a href=\"https:\/\/www.linkedin.com\/groups\/12524512\/\" target=\"_blank\" rel=\"noopener\" title=\"Olivier DASINI on Linkedin\">Follow me on Linkedin<\/a><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Watch my videos on my <a href=\"https:\/\/www.youtube.com\/channel\/UC12TulyJsJZHoCmby3Nm3WQ\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier's MySQL Channel\">YouTube channel<\/a> and <a href=\"https:\/\/www.youtube.com\/channel\/UC12TulyJsJZHoCmby3Nm3WQ\/?sub_confirmation=1\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Subscribe\">subscribe<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My <a href=\"https:\/\/speakerdeck.com\/freshdaz\/\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier DASINI on Speaker Deck\">Speaker Deck account<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My <a href=\"https:\/\/www.slideshare.net\/freshdaz\" target=\"_blank\" rel=\"noreferrer noopener\" title=\"Olivier DASINI on Slideshare\">Slideshare account<\/a> (<em>archive<\/em>).<\/p>\n\n\n\n<div style=\"height:25px\" aria-hidden=\"true\" class=\"wp-block-spacer\"><\/div>\n\n\n\n<p class=\"has-vivid-red-color has-text-color wp-block-paragraph\"><strong>Thanks for using HeatWave &amp; MySQL!<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Discover a flexible architecture for building an AI-powered search engine for your WordPress blog. This guide demonstrates how to combine Python, OCI Generative AI, and MySQL HeatWave to generate embeddings, manage vector storage, and deliver fast semantic, keyword, and hybrid search results.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1702,1740,1716,203],"tags":[1704,306,1700,1697,1782,1735],"class_list":["post-9014","post","type-post","status-publish","format-standard","hentry","category-ai","category-artificial-intelligence","category-howto-en","category-mysql-en","tag-ai","tag-cloud","tag-genai","tag-heatwave-fr-en","tag-mysql-en","tag-python-en"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.4 - aioseo.com -->\n\t<meta name=\"description\" content=\"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Olivier DASINI\"\/>\n\t<meta name=\"google-site-verification\" content=\"HN6x37mM2NBzUxcsZUKC8OY_DxAioI6oKazYPPNq02c\" \/>\n\t<meta name=\"keywords\" content=\"ai,cloud,genai,heatwave,mysql,python,artificial intelligence,howto\" \/>\n\t<link rel=\"canonical\" href=\"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.4\" \/>\n\t\t<meta property=\"og:locale\" content=\"fr_FR\" \/>\n\t\t<meta property=\"og:site_name\" content=\"Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights | Exploring Beyond the Limits of Knowledge\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights\" \/>\n\t\t<meta property=\"og:description\" content=\"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2026-10-27T15:17:41+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2026-10-27T15:17:45+00:00\" \/>\n\t\t<meta name=\"twitter:card\" content=\"summary\" \/>\n\t\t<meta name=\"twitter:site\" content=\"@freshdaz\" \/>\n\t\t<meta name=\"twitter:title\" content=\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights\" \/>\n\t\t<meta name=\"twitter:description\" content=\"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the\" \/>\n\t\t<meta name=\"twitter:creator\" content=\"@freshdaz\" \/>\n\t\t<script type=\"application\/ld+json\" class=\"aioseo-schema\">\n\t\t\t{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#article\",\"name\":\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights\",\"headline\":\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI\",\"author\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/author\\\/freshdaz\\\/#author\"},\"publisher\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#person\"},\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#articleImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Olivier DASINI\"},\"datePublished\":\"2026-10-27T16:17:41+01:00\",\"dateModified\":\"2026-10-27T16:17:45+01:00\",\"inLanguage\":\"fr-FR\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#webpage\"},\"isPartOf\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#webpage\"},\"articleSection\":\"AI, Artificial Intelligence, Howto, MySQL, AI, Cloud, GenAI, Heatwave, MySQL, Python, English\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#breadcrumblist\",\"itemListElement\":[{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#listItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/dasini.net\\\/blog\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/category\\\/mysql-en\\\/#listItem\",\"name\":\"MySQL\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/category\\\/mysql-en\\\/#listItem\",\"position\":2,\"name\":\"MySQL\",\"item\":\"https:\\\/\\\/dasini.net\\\/blog\\\/category\\\/mysql-en\\\/\",\"nextItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#listItem\",\"name\":\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI\"},\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#listItem\",\"name\":\"Home\"}},{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#listItem\",\"position\":3,\"name\":\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI\",\"previousItem\":{\"@type\":\"ListItem\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/category\\\/mysql-en\\\/#listItem\",\"name\":\"MySQL\"}}]},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#person\",\"name\":\"Olivier DASINI\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#personImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Olivier DASINI\"}},{\"@type\":\"Person\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/author\\\/freshdaz\\\/#author\",\"url\":\"https:\\\/\\\/dasini.net\\\/blog\\\/author\\\/freshdaz\\\/\",\"name\":\"Olivier DASINI\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#authorImage\",\"url\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g\",\"width\":96,\"height\":96,\"caption\":\"Olivier DASINI\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#webpage\",\"url\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/\",\"name\":\"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights\",\"description\":\"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the\",\"inLanguage\":\"fr-FR\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#website\"},\"breadcrumb\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/2026\\\/10\\\/27\\\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\\\/#breadcrumblist\"},\"author\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/author\\\/freshdaz\\\/#author\"},\"creator\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/author\\\/freshdaz\\\/#author\"},\"datePublished\":\"2026-10-27T16:17:41+01:00\",\"dateModified\":\"2026-10-27T16:17:45+01:00\"},{\"@type\":\"WebSite\",\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#website\",\"url\":\"https:\\\/\\\/dasini.net\\\/blog\\\/\",\"name\":\"dasini.net - Diary of a MySQL expert\",\"description\":\"Exploring Beyond the Limits of Knowledge\",\"inLanguage\":\"fr-FR\",\"publisher\":{\"@id\":\"https:\\\/\\\/dasini.net\\\/blog\\\/#person\"}}]}\n\t\t<\/script>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights","description":"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the","canonical_url":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/","robots":"max-image-preview:large","keywords":"ai,cloud,genai,heatwave,mysql,python,artificial intelligence,howto","webmasterTools":{"google-site-verification":"HN6x37mM2NBzUxcsZUKC8OY_DxAioI6oKazYPPNq02c","miscellaneous":""},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#article","name":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights","headline":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI","author":{"@id":"https:\/\/dasini.net\/blog\/author\/freshdaz\/#author"},"publisher":{"@id":"https:\/\/dasini.net\/blog\/#person"},"image":{"@type":"ImageObject","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#articleImage","url":"https:\/\/secure.gravatar.com\/avatar\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g","width":96,"height":96,"caption":"Olivier DASINI"},"datePublished":"2026-10-27T16:17:41+01:00","dateModified":"2026-10-27T16:17:45+01:00","inLanguage":"fr-FR","mainEntityOfPage":{"@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#webpage"},"isPartOf":{"@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#webpage"},"articleSection":"AI, Artificial Intelligence, Howto, MySQL, AI, Cloud, GenAI, Heatwave, MySQL, Python, English"},{"@type":"BreadcrumbList","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#breadcrumblist","itemListElement":[{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/#listItem","position":1,"name":"Home","item":"https:\/\/dasini.net\/blog\/","nextItem":{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/category\/mysql-en\/#listItem","name":"MySQL"}},{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/category\/mysql-en\/#listItem","position":2,"name":"MySQL","item":"https:\/\/dasini.net\/blog\/category\/mysql-en\/","nextItem":{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#listItem","name":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI"},"previousItem":{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/#listItem","name":"Home"}},{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#listItem","position":3,"name":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI","previousItem":{"@type":"ListItem","@id":"https:\/\/dasini.net\/blog\/category\/mysql-en\/#listItem","name":"MySQL"}}]},{"@type":"Person","@id":"https:\/\/dasini.net\/blog\/#person","name":"Olivier DASINI","image":{"@type":"ImageObject","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#personImage","url":"https:\/\/secure.gravatar.com\/avatar\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g","width":96,"height":96,"caption":"Olivier DASINI"}},{"@type":"Person","@id":"https:\/\/dasini.net\/blog\/author\/freshdaz\/#author","url":"https:\/\/dasini.net\/blog\/author\/freshdaz\/","name":"Olivier DASINI","image":{"@type":"ImageObject","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#authorImage","url":"https:\/\/secure.gravatar.com\/avatar\/d46da4fb4b99ea9ce527a93ce9b998a446577e42e1f132d8437235fa5b963636?s=96&d=mm&r=g","width":96,"height":96,"caption":"Olivier DASINI"}},{"@type":"WebPage","@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#webpage","url":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/","name":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights","description":"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the","inLanguage":"fr-FR","isPartOf":{"@id":"https:\/\/dasini.net\/blog\/#website"},"breadcrumb":{"@id":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/#breadcrumblist"},"author":{"@id":"https:\/\/dasini.net\/blog\/author\/freshdaz\/#author"},"creator":{"@id":"https:\/\/dasini.net\/blog\/author\/freshdaz\/#author"},"datePublished":"2026-10-27T16:17:41+01:00","dateModified":"2026-10-27T16:17:45+01:00"},{"@type":"WebSite","@id":"https:\/\/dasini.net\/blog\/#website","url":"https:\/\/dasini.net\/blog\/","name":"dasini.net - Diary of a MySQL expert","description":"Exploring Beyond the Limits of Knowledge","inLanguage":"fr-FR","publisher":{"@id":"https:\/\/dasini.net\/blog\/#person"}}]},"og:locale":"fr_FR","og:site_name":"Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights | Exploring Beyond the Limits of Knowledge","og:type":"article","og:title":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights","og:description":"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the","og:url":"https:\/\/dasini.net\/blog\/2026\/10\/27\/build-an-ai-powered-search-engine-with-mysql-heatwave-and-oci-genai\/","article:published_time":"2026-10-27T15:17:41+00:00","article:modified_time":"2026-10-27T15:17:45+00:00","twitter:card":"summary","twitter:site":"@freshdaz","twitter:title":"Build an AI powered search engine with MySQL HeatWave and OCI GenAI | Data Daz (dasini.net) - Data Systems, AI, and Real-World Insights","twitter:description":"Note on model availability: This article was written before September 30, 2026. The models, model IDs, regional availability, and serving modes mentioned below may have been updated, replaced, or retired since then. Before running the examples, check the current OCI documentation and select models supported in your region and serving mode. If you change the","twitter:creator":"@freshdaz"},"aioseo_meta_data":{"post_id":"9014","title":null,"description":null,"keywords":null,"keyphrases":{"focus":{"keyphrase":"","score":0,"analysis":{"keyphraseInTitle":{"score":0,"maxScore":9,"error":1}}},"additional":[]},"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":"","og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"Article","isEnabled":true},"graphs":[]},"schema_type":"default","schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":"-1","robots_max_videopreview":"-1","robots_max_imagepreview":"large","priority":null,"frequency":"default","location":null,"local_seo":null,"breadcrumb_settings":null,"limit_modified_date":false,"ai":{"faqs":[],"keyPoints":[],"schemas":[],"titles":[],"descriptions":[],"socialPosts":{"email":{"subject":"","preview":"","content":""},"linkedin":[],"twitter":[],"facebook":[],"instagram":[]}},"created":"2026-10-01 13:52:17","updated":"2026-10-27 15:17:45","seo_analyzer_scan_date":null,"focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p9LfWW-2lo","jetpack-related-posts":[{"id":8533,"url":"https:\/\/dasini.net\/blog\/2026\/05\/14\/building-an-ai-vision-search-engine-with-mysql-heatwave-genai\/","url_meta":{"origin":9014,"position":0},"title":"Building an AI Vision Search Engine with MySQL HeatWave GenAI","author":"Olivier DASINI","date":"14 mai 2026","format":false,"excerpt":"Modern AI systems increasingly rely on multimodal data: text, images, documents, audio, and video. Among these modalities, image understanding has become one of the most important capabilities for AI-powered applications. Traditionally, implementing these capabilities required specialized computer vision infrastructure, external vector databases, custom ML pipelines, and multiple frameworks. With MySQL\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2026\/05\/5.2_workflow_Reverse_Image_Search.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":7363,"url":"https:\/\/dasini.net\/blog\/2025\/03\/13\/build-an-ai-powered-search-engine-with-heatwave-genai-part-1\/","url_meta":{"origin":9014,"position":1},"title":"Build an AI-Powered Search Engine with HeatWave GenAI (part 1)","author":"Olivier DASINI","date":"13 mars 2025","format":false,"excerpt":"Discover how to build an AI-powered search engine for your applications using HeatWave GenAI. This approach leverages large language models (LLMs) for semantic search, offering a smarter alternative to traditional SQL and full-text search methods. By using embeddings\u2014vector representations of words\u2014the search engine understands context and intent, delivering more relevant\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/03\/HW_GenaI_search_engine.gif?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":7711,"url":"https:\/\/dasini.net\/blog\/2025\/04\/15\/build-an-ai-powered-search-engine-with-heatwave-genai-part-3\/","url_meta":{"origin":9014,"position":2},"title":"Build an AI-Powered Search Engine with HeatWave GenAI (part 3)","author":"Olivier DASINI","date":"15 avril 2025","format":false,"excerpt":"In this latest post, the final part of my series on building an AI-powered search engine with HeatWave GenAI, I dive into enhancing AI-powered search by embedding full article content into HeatWave. By cleaning HTML, chunking content, generating embeddings, and running semantic similarity searches directly within HeatWave, we unlock highly\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":7566,"url":"https:\/\/dasini.net\/blog\/2025\/04\/08\/build-an-ai-powered-search-engine-with-heatwave-genai-part-2\/","url_meta":{"origin":9014,"position":3},"title":"Build an AI-Powered Search Engine with HeatWave GenAI (part 2)","author":"Olivier DASINI","date":"8 avril 2025","format":false,"excerpt":"In this part 2 we focused on enhancing search relevance. We introduced reranking techniques using weighted distances of titles and excerpts to refine initial search results. Then we delved into leveraging article summaries for more effective semantic search, utilizing HeatWave's capability to execute JavaScript stored procedures for sanitizing HTML content\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"Similarity search across title, excerpt and summary","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/04\/HWGenAIsearchEngine2.gif?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":8285,"url":"https:\/\/dasini.net\/blog\/2025\/09\/30\/ask-your-database-anything-natural-language-to-sql-in-mysql-heatwave\/","url_meta":{"origin":9014,"position":4},"title":"Ask Your Database Anything: Natural Language to SQL in MySQL HeatWave","author":"Olivier DASINI","date":"30 septembre 2025","format":false,"excerpt":"Natural Language to SQL in MySQL HeatWave makes data accessible to everyone, not just SQL experts. By turning plain English into optimized queries, it removes barriers and speeds up insights. While best practices help ensure accuracy, the feature already shows how LLMs are transforming the way we work with data.\u2026","rel":"","context":"Dans &quot;AI&quot;","block_context":{"text":"AI","link":"https:\/\/dasini.net\/blog\/category\/ai\/"},"img":{"alt_text":"","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=1050%2C600&ssl=1 3x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/09\/nl2sql-demo3.gif?resize=1400%2C800&ssl=1 4x"},"classes":[]},{"id":7252,"url":"https:\/\/dasini.net\/blog\/2025\/02\/11\/building-an-interactive-llm-chatbot-with-heatwave-using-python\/","url_meta":{"origin":9014,"position":5},"title":"Building an Interactive LLM Chatbot with  HeatWave Using Python","author":"Olivier DASINI","date":"11 f\u00e9vrier 2025","format":false,"excerpt":"AI-powered applications require robust and scalable database solutions to manage and process large amounts of data efficiently. HeatWave is an excellent choice for such applications, providing high-performance OLTP, analytics, machine learning and generative artificial intelligence capabilities. In this article, we will explore a Python 3 script that connects to an\u2026","rel":"","context":"Dans &quot;HeatWave&quot;","block_context":{"text":"HeatWave","link":"https:\/\/dasini.net\/blog\/category\/heatwave-en\/"},"img":{"alt_text":"simple but robust chatbot system leveraging HeatWave GenAI and its in-database Mistral LLM","src":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/02\/HW-Chat-mistral-7b.gif?resize=350%2C200&ssl=1","width":350,"height":200,"srcset":"https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/02\/HW-Chat-mistral-7b.gif?resize=350%2C200&ssl=1 1x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/02\/HW-Chat-mistral-7b.gif?resize=525%2C300&ssl=1 1.5x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/02\/HW-Chat-mistral-7b.gif?resize=700%2C400&ssl=1 2x, https:\/\/i0.wp.com\/dasini.net\/blog\/wp-content\/uploads\/2025\/02\/HW-Chat-mistral-7b.gif?resize=1050%2C600&ssl=1 3x"},"classes":[]}],"jetpack_likes_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/9014","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/comments?post=9014"}],"version-history":[{"count":87,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/9014\/revisions"}],"predecessor-version":[{"id":9103,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/posts\/9014\/revisions\/9103"}],"wp:attachment":[{"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/media?parent=9014"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/categories?post=9014"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dasini.net\/blog\/wp-json\/wp\/v2\/tags?post=9014"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}