Introduction
In Q4 2024, SAP HANA Cloud introduced a feature that enables embedding generation. Previously, embeddings were obtained using SAP AI Core or external LLMs, but now they can be generated directly within SAP HANA Cloud. In this blog, I will demonstrate how to use this feature in CAP.
Prerequisite
Enable Natural Language Processing (NLP) in the SAP HANA Cloud settings.
Basic Syntax
The following syntax is used to retrieve an embedding:
SELECT VECTOR_EMBEDDING(‘Hello world!’,’DOCUMENT’,’SAP_NEB.20240715′) FROM DUMMY;
<text> : The text for which an embedding is created.
<text-type>: The type of the text. Must be either ‘DOCUMENT’ or ‘QUERY’.
<model-and-version>: The name and the version of the model that is used to create the embedding.
VECTOR_EMBEDDING Function (Vector) | Help Portal
Currently, only one model (SAP_NEB.20240715) is available, which generates an embedding with 768 dimensions.
CAP Application
I will introduce the sample CAP application I created. The code is available on GitHub.
DB schema
namespace cap.embedding.function;
using { cuid, managed } from ‘@sap/cds/common’;
entity Dummy {
key id : Integer;
}
entity Notes: cuid, managed {
note: LargeString;
embedding: Vector(768);
}
Key Points in Data Definitions
Use of the “Dummy” table
If you want to retrieve an embedding for a text that does not reside in a database table, you may want to use a syntax as below:
SELECT VECTOR_EMBEDDING(‘Hello world!’,’DOCUMENT’,’SAP_NEB.20240715′) FROM DUMMY;
However, since CAP does not have a DUMMY table, I created one to ensure it always contains exactly one record. I will show how to use this table in the next section.
Embedding Column Dimensions
The embedding column’s dimension should be set based on to the model you use. Since we are using SAP_NEB.20240715, the dimension is 768.
Service definition
using { cap.embedding.function as db } from ‘../db/schema’;
service EmbeddingStorageService {
entity Notes as projection on db.Notes excluding {
embedding
};
action addNotes(notes: array of Notes) returns String;
action deleteNotes() returns String;
function similaritySearch(searchWord: String) returns array of Notes;
}
This service exposes the Notes entity and defines two actions for bulk inserting and deleting notes. Finally, the similaritySearch function returns the top three relevant notes based on the input searchWord.
Event Handlers
Storing an Embedding to the Database
this.before([‘CREATE’, ‘UPDATE’], ‘Notes’, async (req) => {
const embedding = await this.getEmbedding(req.data.note)
req.data.embedding = JSON.stringify(embedding)
})
async getEmbedding(text) {
const db = await cds.connect.to(‘db’)
const { Dummy } = db.entities
// get embedding
const { embedding } = await SELECT.one.from(Dummy).where({ id: 1 })
.columns `vector_embedding(${text}, ‘DOCUMENT’, ‘SAP_NEB.20240715’) as embedding`
return embedding
}
The getEmbedding function retrieves an embedding using vector_embedding function. Since the result is an array, it must be converted to a string before storing it into the database.
Performing a Similarity Search
this.on(‘similaritySearch’, async (req) => {
const searchWord = req.data.searchWord
const embedding = await this.getEmbedding(searchWord)
const db = await cds.connect.to(‘db’)
const { Notes } = db.entities
// retrieve relevant notes
const notes = await SELECT.from(Notes)
.columns(‘ID’, ‘note’)
.limit(3)
// .where`cosine_similarity(embedding, to_real_vector(${JSON.stringify(embedding)})) > 0.7`
.orderBy`cosine_similarity(embedding, to_real_vector(${JSON.stringify(embedding)})) desc`
return notes
})
This function first retrieves an embedding for a given search word, then executes a similarity search to get the top three relevant notes.
Testing
The following notes are stored in the database.
{
“@odata.context”: “$metadata#Notes”,
“value”: [
{
“ID”: “02e197c9-4f06-4abf-b314-37d89226dfb0”,
“note”: “Remember to buy milk, eggs, and bread on the way home.”
},
{
“ID”: “31ccffee-e186-4ec8-90c2-cfdee5e9a77f”,
“note”: “AWS EC2 instance needs to be restarted after applying updates.”
},
{
“ID”: “8a78b77d-7210-465d-861f-f2d4ff249371”,
“note”: “Read ‘The Lean Startup’ by Eric Ries for innovation insights.”
},
{
“ID”: “9841088a-d5b8-44ac-8841-e7f3909d1cc8”,
“note”: “Meeting with John on Monday at 10 AM about project roadmap.”
},
{
“ID”: “d65a1f36-ede1-4808-9bfc-a38f1a7dc888”,
“note”: “Flight to San Francisco departs at 3:45 PM on April 12th.”
}
]
}
Now, let’s perform a similarity search using the search word “shopping list“.
### Similarity Search
@searchWord = shopping list
GET {{server}}/odata/v4/embedding-storage/similaritySearch(searchWord='{{searchWord}}’)
The following three records are returned, with the most relevant appearing at the top.
{
“@odata.context”: “$metadata#Notes”,
“value”: [
{
“ID”: “02e197c9-4f06-4abf-b314-37d89226dfb0”,
“note”: “Remember to buy milk, eggs, and bread on the way home.”
},
{
“ID”: “9841088a-d5b8-44ac-8841-e7f3909d1cc8”,
“note”: “Meeting with John on Monday at 10 AM about project roadmap.”
},
{
“ID”: “d65a1f36-ede1-4808-9bfc-a38f1a7dc888”,
“note”: “Flight to San Francisco departs at 3:45 PM on April 12th.”
}
]
}
Conclusion
With the VECTOR_EMBEDDING function, you can retrieve embeddings without needing SAP AI Core or external LLM services. However, in real-life RAG scenarios, LLMs are still required. Consider this a starting point and extend it to fit your specific use case.
IntroductionIn Q4 2024, SAP HANA Cloud introduced a feature that enables embedding generation. Previously, embeddings were obtained using SAP AI Core or external LLMs, but now they can be generated directly within SAP HANA Cloud. In this blog, I will demonstrate how to use this feature in CAP. PrerequisiteEnable Natural Language Processing (NLP) in the SAP HANA Cloud settings. Basic SyntaxThe following syntax is used to retrieve an embedding: SELECT VECTOR_EMBEDDING(‘Hello world!’,’DOCUMENT’,’SAP_NEB.20240715′) FROM DUMMY;<text> : The text for which an embedding is created.<text-type>: The type of the text. Must be either ‘DOCUMENT’ or ‘QUERY’.<model-and-version>: The name and the version of the model that is used to create the embedding. VECTOR_EMBEDDING Function (Vector) | Help Portal Currently, only one model (SAP_NEB.20240715) is available, which generates an embedding with 768 dimensions. CAP ApplicationI will introduce the sample CAP application I created. The code is available on GitHub.DB schema namespace cap.embedding.function;
using { cuid, managed } from ‘@sap/cds/common’;
entity Dummy {
key id : Integer;
}
entity Notes: cuid, managed {
note: LargeString;
embedding: Vector(768);
} Key Points in Data DefinitionsUse of the “Dummy” tableIf you want to retrieve an embedding for a text that does not reside in a database table, you may want to use a syntax as below: SELECT VECTOR_EMBEDDING(‘Hello world!’,’DOCUMENT’,’SAP_NEB.20240715′) FROM DUMMY; However, since CAP does not have a DUMMY table, I created one to ensure it always contains exactly one record. I will show how to use this table in the next section.Embedding Column DimensionsThe embedding column’s dimension should be set based on to the model you use. Since we are using SAP_NEB.20240715, the dimension is 768. Service definition using { cap.embedding.function as db } from ‘../db/schema’;
service EmbeddingStorageService {
entity Notes as projection on db.Notes excluding {
embedding
};
action addNotes(notes: array of Notes) returns String;
action deleteNotes() returns String;
function similaritySearch(searchWord: String) returns array of Notes;
} This service exposes the Notes entity and defines two actions for bulk inserting and deleting notes. Finally, the similaritySearch function returns the top three relevant notes based on the input searchWord. Event HandlersStoring an Embedding to the Database this.before([‘CREATE’, ‘UPDATE’], ‘Notes’, async (req) => {
const embedding = await this.getEmbedding(req.data.note)
req.data.embedding = JSON.stringify(embedding)
})
async getEmbedding(text) {
const db = await cds.connect.to(‘db’)
const { Dummy } = db.entities
// get embedding
const { embedding } = await SELECT.one.from(Dummy).where({ id: 1 })
.columns `vector_embedding(${text}, ‘DOCUMENT’, ‘SAP_NEB.20240715’) as embedding`
return embedding
} The getEmbedding function retrieves an embedding using vector_embedding function. Since the result is an array, it must be converted to a string before storing it into the database. Performing a Similarity Search this.on(‘similaritySearch’, async (req) => {
const searchWord = req.data.searchWord
const embedding = await this.getEmbedding(searchWord)
const db = await cds.connect.to(‘db’)
const { Notes } = db.entities
// retrieve relevant notes
const notes = await SELECT.from(Notes)
.columns(‘ID’, ‘note’)
.limit(3)
// .where`cosine_similarity(embedding, to_real_vector(${JSON.stringify(embedding)})) > 0.7`
.orderBy`cosine_similarity(embedding, to_real_vector(${JSON.stringify(embedding)})) desc`
return notes
}) This function first retrieves an embedding for a given search word, then executes a similarity search to get the top three relevant notes. TestingThe following notes are stored in the database. {
“@odata.context”: “$metadata#Notes”,
“value”: [
{
“ID”: “02e197c9-4f06-4abf-b314-37d89226dfb0”,
“note”: “Remember to buy milk, eggs, and bread on the way home.”
},
{
“ID”: “31ccffee-e186-4ec8-90c2-cfdee5e9a77f”,
“note”: “AWS EC2 instance needs to be restarted after applying updates.”
},
{
“ID”: “8a78b77d-7210-465d-861f-f2d4ff249371”,
“note”: “Read ‘The Lean Startup’ by Eric Ries for innovation insights.”
},
{
“ID”: “9841088a-d5b8-44ac-8841-e7f3909d1cc8”,
“note”: “Meeting with John on Monday at 10 AM about project roadmap.”
},
{
“ID”: “d65a1f36-ede1-4808-9bfc-a38f1a7dc888”,
“note”: “Flight to San Francisco departs at 3:45 PM on April 12th.”
}
]
} Now, let’s perform a similarity search using the search word “shopping list”. ### Similarity Search
@searchWord = shopping list
GET {{server}}/odata/v4/embedding-storage/similaritySearch(searchWord='{{searchWord}}’) The following three records are returned, with the most relevant appearing at the top. {
“@odata.context”: “$metadata#Notes”,
“value”: [
{
“ID”: “02e197c9-4f06-4abf-b314-37d89226dfb0”,
“note”: “Remember to buy milk, eggs, and bread on the way home.”
},
{
“ID”: “9841088a-d5b8-44ac-8841-e7f3909d1cc8”,
“note”: “Meeting with John on Monday at 10 AM about project roadmap.”
},
{
“ID”: “d65a1f36-ede1-4808-9bfc-a38f1a7dc888”,
“note”: “Flight to San Francisco departs at 3:45 PM on April 12th.”
}
]
} ConclusionWith the VECTOR_EMBEDDING function, you can retrieve embeddings without needing SAP AI Core or external LLM services. However, in real-life RAG scenarios, LLMs are still required. Consider this a starting point and extend it to fit your specific use case. Read More Technology Blogs by Members articles
#SAP
#SAPTechnologyblog