UPDATE # 4
I've successfully run the firstchar
example, but now the problem is with using regex
. Even after including header file, it is not recognizing regex
operator. Any clue how can this be resolved?
UPDATE # 2
I've compiled sqlite3
library in my project. I am now looking for anyone to help me with writing a function for my regex
, attach it to database and call it from query.
UPDATE # 3
I've written some code from this example. Here it is
extern "C"
void
Java_com_kfmwa916_testapp_DatabaseHandler_createFunction() {
sqlite3 *db;
//Open database
sqlite3_open("MyDBName.db", &db);
//Attach function to database
sqlite3_create_function(db, "firstchar", 1, SQLITE_UTF8, NULL, &firstchar, NULL, NULL);
}
And firstchar
function is,
static void firstchar(sqlite3_context *context, int argc, sqlite3_value **argv) {
if (argc == 1) {
char *text = (char *) sqlite3_value_text(argv[0]);
if (text && text[0]) {
char result[2];
result[0] = text[0]; result[1] = '\0';
sqlite3_result_text(context, result, -1, SQLITE_TRANSIENT);
return;
}
}
sqlite3_result_null(context);
}
And the used it in my query like
SELECT firstchar(text) FROM dummy
But it is giving error
no such function firstchar()
Any help is highly appreciated.
Original Question
I am working with Arabic Language saved in UNICODE Format in SQLite. I want to implement a search. But there's a problem.
Let's say the text is
<html>
<head>
<style>
@font-face {
font-family: "Al_Mushaf";
src: url('fonts/al_mushaf.ttf');
}
@font-face {
font-family: "Jameel Noori Nastaleeq";
src: url('fonts/jameel_noori.ttf');
}
</style>
</head>
<body>
<h3 style='font-family:"Al_Mushaf"'>
صحابہ کرام کا انبیائے کرام کی سنّت پر عمل
میٹھے میٹھے اسلامی بھائیو!صدائے مدینہ لگانا انبیائے کِرام عَلَیْہِمُ السَّلَام کی اس قَدْر پیاری سنّت ہے کہ صحابۂ کِرام عَلَیْہِمُ الرِّضْوَان نے بھی اسے خُوب اپنایا اور وہ بھی حضرت سَیِّدُنا داؤد عَلَیْہِ السَّلَام کی طرح اپنے گھر والوں کو جگایا کرتے جیسا کہ حضرت سَیِّدُنا عبد اللہ بن عُمَر رَضِیَاللّٰہُ تَعَالٰی عَنْہُما فرماتے ہیں کہ میرے والِدِ مُحْتَرَم اَمِیرُ الْمُوْمِنِین حضرت سَیِّدُنا عُمَر فَارُوقِ اَعْظَم رَضِیَاللّٰہُ تَعَالٰی عَنْہ رات میں جس قَدْر ربّ تعالیٰ چاہتا،نَماز پڑھتے رہتے،یہاں تک کہ جب رات کا آخری وَقْت ہوتا تو اپنے گھر والوں کو بھی نَماز کے لیے جگا دیتے اور ان سے فرماتے: اَلصَّلٰوة یعنی نماز۔ پھر یہ آیت مُبارَکہ تِلاوَت فرماتے:
وَاۡمُرْ اَہۡلَکَ بِالصَّلٰوۃِ وَ اصْطَبِرْ عَلَیۡہَا ؕ لَا نَسْـَٔلُکَ رِزْقًا ؕ نَحْنُ نَرْزُقُکَ ؕ وَالْعٰقِبَۃُ لِلتَّقْوٰی (پ۱۶،طٰهٰ:۱۳۲)
</h3>
</body>
</html>
And it is stored in SQLite Database. Now I want to search html
, it will return the result and if I search مبارکہ
it won't return a result because in actual text, it is مُبارَکہ
(with these extra UNICODE).
I want to ignore all HTML tags and these extra UNICODE Characters while searching so that html
shouldn't return a result while مبارکہ
should return a result.
What I found so far;
Make extra column and put stripped text into it and then search (I can't do it because there are thousands of books and they will take a lot of memory)
UDF Like SQL (I couldn't find any suitable example/tutorial to implement it)
Using REGEXP (I couldn't figure it out yet how to do this, I just know that I've to implement it myself)
SQL query using LIKE and GLOB operators and wildcard characters.
I'm stuck for two days and couldn't find a working solution. Option#4 is desirable but any working solution will do the charm.
Meanwhile, I've to keep application memory efficient and optimized searching.
Any help is highly appreciated.
UPDATE
I've made regex to ignore html tags and text between style tag and used it in query with REGEXP
.
Now there are two problems,
I want to ignore these extra characters too. I know their UNICODEs, just need to know how to append it in the regex. This is my regex;
(?![^<]*>)(?!<style[^>]*?>)(TEXT)(?![^<]*?<\/style>)
I've used it in query like
SELECT text FROM dummy WHERE text REGEXP <myregex>
It's not giving an error but not returning the desired result too.
Answer to Original Question
NOTE: As I have recently learned it, I maybe wrong at many places, kindly correct my mistakes
There are two solutions
REGEXP
Operator with SQLite QueryThe problem with first one is that it returns either true
or false
but I need data. And the problem with both methods is that you have to use C/C++ Library in your Android Project. So I decided to create my own user defined function.
You can find many tutorials on how to use NDK in your project, but won't find any complete example of using 3rd Party libraries in your project.
After a lot of searching/studying, I combined things from many different places and was able to complete my task. Below are some steps on how you can do it. I also intend to write a complete step-by-step tutorial.
Getting things ready
sqlite3
amalgamated library, which can be downloaded from here. Extract them in cpp
folder of your project.CMakeLists.txt
file by now when you included NDK in your project. It's time to add these libraries in CMakeLists.txt
file. For that, go to your Project
pane, you'll see External Build Files
there and inside it you'll see the desired file. Open it and edit it as follows,# Sets the minimum version of CMake required to build the native
# library. You should either keep the default value or only pass a
# value of 3.4.0 or lower.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds it for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
# Associated headers in the same location as their source
# file are automatically included.
src/main/cpp/native-lib.cpp )
include_directories(${CMAKE_SOURCE_DIR}/src)
add_library(sqlite3 STATIC src/main/cpp/sqlite3.c src/main/cpp/sqlite3.h src/main/cpp/sqlite3ext.h)
add_executable(sqlite src/main/cpp/sqlite3.c src/main/cpp/sqlite3.h src/main/cpp/sqlite3ext.h)
set_target_properties(sqlite PROPERTIES OUTPUT_NAME sqlite3)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because system libraries are included in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in the
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
native-lib
# Links the target library to the log library
# included in the NDK.
sqlite3
log )
You have to first add libraries using add_library
and then link it to your class which you've made, it is by default named native-lib.cpp
.
Implementing Function
Now here comes the main part. Open native-lib.cpp
and include required files
and headers
. What you have to do;
YourActivity.java
. You'll know the pattern once you see your file. In my case it is Java_com_kfmwa916_testapp_SearchResult_createFunction(JNIEnv * env, jobject object, jstring search)
where Java
is a keyword
com_kfmwa916_testapp
is your package
SearchResult
is your Java
class
createFunction
is the name of the function.
Create your function. In my case, I have to apply certain regex
in search. Here is mine,
static void strip_text(sqlite3_context *context, int argc, sqlite3_value **argv) {
if(argc == 1) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "inside strip_text");
char *result = (char *) sqlite3_value_text(argv[0]);
std::string text(result);
std::regex regex_head("YOUR REGEX");
if (!text.empty()) {
text = std::regex_replace(text, regex_head, "");
sqlite3_result_text(context, text.c_str(), -1, SQLITE_TRANSIENT);
__android_log_print(ANDROID_LOG_VERBOSE, "STRIPPED TEXT", "%s", text.c_str());
return;
}
}
sqlite3_result_null(context);
}
Create sqlite3
instance, open database, attach this function to database and use it in your query. Here is a code snippet
extern "C"
void
Java_com_kfmwa916_testapp_SearchResult_createFunction(JNIEnv * env, jobject object, jstring search) {
const char * search_term = env->GetStringUTFChars(search, 0);
env->ReleaseStringUTFChars(search, search_term);
std::string q(search_term);
std::string query = "SELECT text FROM dummy WHERE LIKE('%" + q + "%', strip_text(text))=1";
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "%s", query.c_str());
//GetJStringContent(env, search, search_term);
sqlite3 *db;
//Open database
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Opening database");
int rc = sqlite3_open("/data/data/com.kfmwa916.testapp/databases/MyDBName.db", &db);
//It'll be good to check 'rc' for error(s).
//Attach function to database
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Attaching function");
rc = sqlite3_create_function(db, "strip_text", 1, SQLITE_ANY, NULL, &strip_text, NULL, NULL);
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "Executing query");
rc = sqlite3_exec(db, query.c_str(), callback, NULL, NULL);
}
Implement callback
function to process result. It should look like
static int callback(void *NotUsed, int argc, char **argv, char **azColName) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "FOUND");
int i;
for (i = 0; i < argc; ++i) {
__android_log_print(ANDROID_LOG_VERBOSE, "TAG", "%s = %sn", azColName[i], argv[i] ? argv[i] : "NULL");
}
return 0;
}
Finally come to your Java
class, in my case, it is SearchResult
Load library and define function.
static {
System.loadLibrary("native-lib");
}
public native void createFunction(String search);
and call it where you want it. Let's say onClickEvent
of a button like createFunction(searchterm)
Post is open for correction and modification.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With