Sometimes in your search queries you need something more powerful than "... WHERE "COMPANY_NAME" like '%Oil%' ... ". Fortunately SAP HANA provides a built-in search capabilities that allows your application's users "to search tables and views much like they would when searching for information on the Internet." (for details refer "SAP HANA Developer Guide").
In the following lines, you will find how to setup your Cloud HANA DB table, so you could benefit from HANA search capabilities.
Scenario
We got a HANA Cloud application, that uses two DB tables (SNWD_PD, SNWD_TEXTS). These tables contain products, and texts associated with them. We want to give our application's users the ability to search for products by name and description. Here are the specific search requirements:
- Make fault tolerant search (fuzzy search in HANA) for the product's name (e.g. if user searches for "mouzePat", find also products for "Mousepad")
- Make linguistic search for the product's description (e.g. if user searches for "mouse", find also products for "mice")
- Score the results by relevance
- Give higher scores (weight) to the results from linguistic search than the ones from fuzzy search
DB Tables Structure
In the used tables below the columns "NAME_GUID", and "DESC_GUID" refers texts ("PARENT_KEY") for product's name, and description respectively.
- SNWD_TEXTS table structure:
- SNWD_PD table structure:
Prepare Text's (SNWD_TEXTS) DB Table for Search
HANA search capability is enabled per DB table's column. Such a column has defined fulltext index (existing by default for column types TEXT, SHORTTEXT; for details refer "SAP HANA Developer Guide").
CREATE FULLTEXT INDEX SNWD_TEXTS_TEXT ON "JP_OPINT_WEBSHOP_WEB"."SNWD_TEXTS"(TEXT) FAST PREPROCESS OFF FUZZY SEARCH INDEX ON;
- "FAST PREPROCESS OFF" - enables HANA linguistic search over a DB column
- "FUZZY SEARCH INDEX ON" - increase performance of fuzzy search
Create DB Procedure for Search Execution
As a result from a search we expect a table with product's information (from "SNWD_PD" table), name and description (from "SNWD_TEXTS" table). We have created a procedure "findProduct" capsulating SQL queries. Meaning of SQL queries is the following:
- "search" - executes fuzzy, and linguistic search for "searchTerm" over "SNWD_TEXTS" table. The desired weights for both searches is used. Score is used latter to sort the results by relevance
- "name" - join product's table with name's search results
- "descr" - join product's table with description's search results
- "search_all" - union search results for product's name and description
- "max_relevance" - leave the best search relevance for a product
- "result" - group search results by products, and order them by relevance
create procedure findProduct(in searchTerm VARCHAR(100), out result "PRODUCT_RESULT") AS
BEGIN
search = SELECT "PARENT_KEY", "TEXT", SCORE() AS RELEVANCE FROM "SNWD_TEXTS"
WHERE client='000' AND
CONTAINS (TEXT, :searchTerm, FUZZY(0.4), WEIGHT(0.5)) OR
CONTAINS (TEXT, :searchTerm, LINGUISTIC, WEIGHT(0.6));
name = SELECT "PRODUCT".*,
"TEXT"."TEXT" AS NAME,
DESCRIPTION.text AS DESCRIPTION,
RELEVANCE
FROM "SNWD_PD" AS "PRODUCT"
INNER JOIN :search AS "TEXT"
ON "TEXT"."PARENT_KEY" = "PRODUCT"."NAME_GUID"
LEFT OUTER JOIN "SNWD_TEXTS" AS DESCRIPTION
ON "PRODUCT".DESC_GUID=DESCRIPTION.PARENT_KEY
WHERE "PRODUCT".client='000';
descr = SELECT "PRODUCT".*,
"TEXT"."TEXT" AS NAME,
DESCRIPTION.text AS DESCRIPTION,
RELEVANCE
FROM "SNWD_PD" AS "PRODUCT"
INNER JOIN :search AS DESCRIPTION
ON "DESCRIPTION"."PARENT_KEY" = "PRODUCT"."DESC_GUID"
LEFT OUTER JOIN "SNWD_TEXTS" as "TEXT"
ON "PRODUCT".NAME_GUID="TEXT".PARENT_KEY
WHERE "PRODUCT".client='000';
search_all = SELECT * FROM :name UNION SELECT * FROM :descr;
max_relevance = SELECT MAX(TO_DOUBLE(RELEVANCE)) AS RELEVANCE, PRODUCT_ID
FROM :search_all group by PRODUCT_ID;
result = SELECT PRODUCT.* FROM :search_all PRODUCT
INNER JOIN :max_relevance MAX_RELEVANCE
ON PRODUCT.RELEVANCE=MAX_RELEVANCE.RELEVANCE AND
PRODUCT.PRODUCT_ID=MAX_RELEVANCE.PRODUCT_ID
ORDER BY PRODUCT.RELEVANCE DESC;
END;
Search Result
Here is a search result for "mouzePat":
Try It Yourself
You may try the described functionality by downloading search_demo.zip. You may see contents of SearchDemo project on GitHub.
Contents of search_demo.zip
- SearchDemoServlet - servlet with simple HTML UI, so you may try search by yourself
Make it works:
- Unzip search_demo.zip into your web application's "src" folder.
- Configure your web application's web.xml adding the following lines:
<servlet>
<description></description>
<display-name>SearchDemoServlet</display-name>
<servlet-name>SearchDemoServlet</servlet-name>
<servlet-class>com.sap.demo.search.SearchDemoServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>SearchDemoServlet</servlet-name>
<url-pattern>/SearchDemoServlet</url-pattern>
</servlet-mapping>
<resource-ref>
<res-ref-name>jdbc/DefaultDB</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
</resource-ref>
- Deploy your web application on the cloud
- Navigate to SearchDemoServlet URL, and try the search