Search implementation

This commit is contained in:
Keerthi Niranjan 2017-07-25 20:58:38 +05:30 committed by Keerthi Niranjan
parent c6f84adc44
commit 1e9648deed
6 changed files with 373 additions and 6 deletions

156
demo/search.html Normal file
View File

@ -0,0 +1,156 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Search</title>
<style>
table, th, td {
border: 1px solid black;
border-collapse: collapse;
padding: 5px;
text-align: center;
}
.hidden {
display: none;
}
input {
padding: 5px;
margin: 2px 0;
border: 1px solid #ccc;
}
label {
padding-right: 10px;
}
button {
padding: 10px;
background-color: rgba(0,64,98,0.7);
color: white;
}
</style>
</head>
<body>
<div style="padding: 20px;">
<h1>Symphony Electron API Demo</h1>
<div>
<p>Search</p>
</div>
<div>
<label for="query">Query:</label><input id="query" size=60>
</div>
<br>
<div>
<label for="offset">Offset:</label><input id="offset" value="0" size=5>
<label for="limit">Limit:</label><input id="limit" value="25" size=5>
</div>
<br>
<div>
<label for="start">Start:</label><input id="start" value="000" size=20>
<label for="end">End:</label><input id="end" value="999" size=20>
</div>
<br>
<div>
<label for="senderId">SenderId:</label><input id="senderId" size=20>
</div>
<br>
<div>
<label for="threadId">ThreadId:</label><input id="threadId" size=20>
</div>
<br>
<div>
<button id='search'>Search</button>
</div>
<br>
<div>
<label for="var1">var1:</label><input id="var1" value="0" size=5>
</div>
<br>
<div>
<button id='index'>IndexMessages</button>
<button id='merge'>Merge</button>
</div>
<br>
<div>
<p>Results:</p>
<p id="results"></p>
<table id="table" class="hidden" style="width:100%">
<tr>
<th>ThreadId</th>
<th>SenderId</th>
<th>Text</th>
</tr>
</table>
<br>
</div>
</div>
</body>
<script>
var search = new ssf.Search("akon");
var buttonEl = document.getElementById('search');
var merge = document.getElementById('merge');
var buttonIndex = document.getElementById('index')
var queryEl = document.getElementById('query');
var offsetEl = document.getElementById('offset');
var limitEl = document.getElementById('limit');
var startEl = document.getElementById('start');
var endEl = document.getElementById('end');
var resultsEl = document.getElementById('results');
var senderIdEl = document.getElementById('senderId');
var threadIdEl = document.getElementById('threadId');
var var1El = document.getElementById('var1');
var table = document.getElementById('table');
buttonIndex.addEventListener('click', function () {
search.readJson().then(function (res) {
search.indexBatch(res).then(function () {
resultsEl.innerHTML = "Index created";
});
}).catch(function (err) {
console.log(err);
});
});
buttonEl.addEventListener('click', function () {
if (!search.isLibInit()) {
alert('Library is not initiated. Init first...');
return;
}
let out;
table.innerHTML = '';
table.classList.remove('hidden');
search.query(queryEl.value, offsetEl.value, limitEl.value, startEl.value, endEl.value, senderIdEl.value, threadIdEl.value).then(function (result) {
out = result;
var th = document.createElement('tr');
var th1 = document.createElement('td');
th1.innerText = "ThreadId";
var th2 = document.createElement('td');
th2.innerText = 'SenderId';
var th3 = document.createElement('td');
th3.innerText = 'Text';
th.appendChild(th1);
th.appendChild(th2);
th.appendChild(th3);
table.appendChild(th);
out.messages.forEach(function (msg) {
var tr = document.createElement('tr');
var t1 = document.createElement('td');
t1.innerText = msg.threadId;
var t2 = document.createElement('td');
t2.innerText = msg.senderId;
var t3 = document.createElement('td');
t3.innerText = msg.text;
tr.appendChild(t1);
tr.appendChild(t2);
tr.appendChild(t3);
table.appendChild(tr);
});
/*search.freeResults(JSON.stringify(out));*/
});
});
merge.addEventListener('click', function () {
search.mergeIndexBatches();
})
</script>
</html>

View File

@ -111,6 +111,8 @@ function createAPI() {
*/
ScreenSnippet: remote.require('./screenSnippet/ScreenSnippet.js').ScreenSnippet,
Search: remote.require('./search/search.js').Search,
/**
* Brings window forward and gives focus.
* @param {String} windowName Name of window. Note: main window name is 'main'

146
js/search/search.js Normal file
View File

@ -0,0 +1,146 @@
'use strict';
const fs = require('fs');
const randomString = require('randomstring');
const libSymphonySearch = require('./searchLibrary');
const TEMP_BATCH_INDEX_FOLDER = './data/temp_batch_indexes';
const TEMP_REALTIME_INDEX = './data/temp_realtime_index';
const INDEX_PREFIX = './data/search_index';
const INDEX_DATA_FOLDER = './data';
const SEARCH_PERIOD_SUBTRACTOR = 3 * 31 * 24 * 60 * 60 * 1000;//3 months
const MINIMUM_DATE = '0000000000000';
const MAXIMUM_DATE = '9999999999999';
const INDEX_VERSION = 'v1';
const SORT_BY_SCORE = 0;
const BATCH_RANDOM_INDEX_PATH_LENGTH = 20;
class Search {
constructor(userId) {
this.isInitialized = false;
this.userId = userId;
this.startIndexingFromDate = (new Date().getTime() - SEARCH_PERIOD_SUBTRACTOR).toString();
this.indexFolderName = INDEX_PREFIX + '_' + userId + '_' + INDEX_VERSION;
this.init();
}
initLib() {
return new Promise((resolve, reject) => {
if (!this.isInitialized) {
this.isInitialized = true;
}
resolve(libSymphonySearch.symSEInit());
});
}
isLibInit() {
return this.initLib();
}
init() {
libSymphonySearch.symSEInit();
libSymphonySearch.symSEEnsureFolderExists(INDEX_DATA_FOLDER);
libSymphonySearch.symSERemoveFolder(TEMP_REALTIME_INDEX);
libSymphonySearch.symSERemoveFolder(TEMP_BATCH_INDEX_FOLDER);
libSymphonySearch.symSEEnsureIndexExists(this.indexFolderName);
libSymphonySearch.symSEEnsureIndexExists(TEMP_REALTIME_INDEX);
let indexDateStartFrom = new Date().getTime() - SEARCH_PERIOD_SUBTRACTOR;
libSymphonySearch.symSEDeleteMessages(this.indexFolderName, null,
MINIMUM_DATE, indexDateStartFrom.toString());
this.isInitialized = true;
}
indexBatch(messages) {
return new Promise((resolve, reject) => {
if (!this.isInitialized) {
reject()
}
let indexId = randomString.generate(BATCH_RANDOM_INDEX_PATH_LENGTH);
libSymphonySearch.symSECreatePartialIndexAsync(TEMP_BATCH_INDEX_FOLDER, indexId, JSON.stringify(messages), function (err, res) {
if (err) reject(err);
resolve(res)
});
});
}
mergeIndexBatches() {
libSymphonySearch.symSEMergePartialIndexAsync(this.indexFolderName, TEMP_BATCH_INDEX_FOLDER, function (err, res) {
if (err) throw err;
libSymphonySearch.symSERemoveFolder(TEMP_BATCH_INDEX_FOLDER);
});
}
readJson() {
return new Promise((resolve, reject) => {
var files = fs.readdirSync('./msgsjson');
let messageData = [];
files.forEach(function (file) {
let data = fs.readFileSync('./msgsjson/' + file, "utf8");
if (data) {
messageData.push(JSON.parse(data));
resolve(messageData);
} else {
reject("err on reading files")
}
});
});
}
query(query, senderIds, threadIds, attachments, startDate,
endDate, limit, offset, sortOrder) {
return new Promise((resolve, reject) => {
if (!this.isInitialized) {
reject(-1);
}
let q = Search.constructQuery(query, senderIds, threadIds, attachments);
if (q === undefined) {
reject(-2);
}
let sd = new Date().getTime() - SEARCH_PERIOD_SUBTRACTOR;
if (!isNaN(startDate)) {
if (startDate >= sd) {
sd = startDate;
}
}
let sd_str = sd.toString();
let ed_str = MAXIMUM_DATE;
if (!isNaN(endDate)) {
ed_str = endDate.toString();
}
if (isNaN(limit)) {
limit = 25;
}
if (isNaN(offset)) {
offset = 0
}
if (isNaN(sortOrder)) {
sortOrder = SORT_BY_SCORE;
}
let returnedResult = libSymphonySearch.symSESearch(this.indexFolderName, TEMP_REALTIME_INDEX, q, sd_str, ed_str, offset, limit, sortOrder);
let ret = JSON.parse(returnedResult);
resolve(ret);
});
}
static constructQuery(query, senderIds, threadIds, attachments) {
return query;
}
}
module.exports = {
Search: Search
};

View File

@ -0,0 +1,62 @@
'use strict';
const ffi = require('ffi');
const ref = require('ref');
var symLucyIndexer = ref.types.void;
var symLucyIndexerPtr = ref.refType(symLucyIndexer);
var libSymphonySearch = ffi.Library('libsymphonysearch', {
//init
'symSE_init': ['void', []],
'symSE_remove_folder': ['int', ['string']],
'symSE_ensure_index_exists': ['int', ['string']],
'symSE_ensure_folder_exists': ['int', ['string']],
//first time indexing and delta indexing
'symSE_get_indexer': [symLucyIndexerPtr, ['string']], //will be removed
'symSE_create_partial_index': ['int', ['string', 'string', 'string']],
'symSE_merge_partial_index': ['int', ['string', 'string']],
//real time indexing
'symSE_index_realtime': ['int', ['string', 'string']],
'symSE_merge_temp_index': ['int', ['string', 'string']],
'symSE_clear_temp_index': ['int', ['string']],
//Search,
'symSE_search': ['string', ['string', 'string', 'string', 'string', 'string', 'int', 'int', 'int']],
//Deletion
'symSE_delete_messages': ['int', ['string', 'string', 'string', 'string']],
//Index commit/optimize
'symSE_commit_index': ['int', [symLucyIndexerPtr, 'int']], //will be removed
//freePointer
'symSE_free_results': ['int', ['string']]
});
module.exports = {
symSEInit: libSymphonySearch.symSE_init,
symSERemoveFolder: libSymphonySearch.symSE_remove_folder,
symSEEnsureIndexExists: libSymphonySearch.symSE_ensure_index_exists,
symSEEnsureFolderExists: libSymphonySearch.symSE_ensure_folder_exists,
symSEGetIndexer: libSymphonySearch.symSE_get_indexer,
symSECreatePartialIndex: libSymphonySearch.symSE_create_partial_index,
symSEMergePartialIndex: libSymphonySearch.symSE_merge_partial_index,
symSEIndexRealTime: libSymphonySearch.symSE_index_realtime,
symSEMergeTempIndex: libSymphonySearch.symSE_merge_temp_index,
symSEClearTempIndex: libSymphonySearch.symSE_clear_temp_index,
symSESearch: libSymphonySearch.symSE_search,
symSEDeleteMessages: libSymphonySearch.symSE_delete_messages,
symSECommitIndex: libSymphonySearch.symSE_commit_index,
symSEFreeResult: libSymphonySearch.symSE_free_results,
symSEInitAsync: libSymphonySearch.symSE_init.async,
symSERemoveFolderAsync: libSymphonySearch.symSE_remove_folder.async,
symSEEnsureIndexExistsAsync: libSymphonySearch.symSE_ensure_index_exists.async,
symSEEnsureFolderExistsAsync: libSymphonySearch.symSE_ensure_folder_exists.async,
symSEGetIndexerAsync: libSymphonySearch.symSE_get_indexer.async,
symSECreatePartialIndexAsync: libSymphonySearch.symSE_create_partial_index.async,
symSEMergePartialIndexAsync: libSymphonySearch.symSE_merge_partial_index.async,
symSEIndexRealTimeAsync: libSymphonySearch.symSE_index_realtime.async,
symSEMergeTempIndexAsync: libSymphonySearch.symSE_merge_temp_index.async,
symSEClearTempIndexAsync: libSymphonySearch.symSE_clear_temp_index.async,
symSESearchAsync: libSymphonySearch.symSE_search.async,
symSEDeleteMessagesAsync: libSymphonySearch.symSE_delete_messages.async,
symSECommitIndexAsync: libSymphonySearch.symSE_commit_index.async,
symSEFreeResultAsync: libSymphonySearch.symSE_free_results.async
};

BIN
libsymphonysearch.dylib Executable file

Binary file not shown.

View File

@ -7,8 +7,8 @@
"main": "js/main.js",
"scripts": {
"dev": "npm run prebuild && cross-env ELECTRON_DEV=true electron .",
"demo-win": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file:///demo/index.html",
"demo-mac": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file://$(pwd)/demo/index.html",
"demo-win": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file:///demo/search.html",
"demo-mac": "npm run prebuild && cross-env ELECTRON_DEV=true electron . --url=file://$(pwd)/demo/search.html",
"unpacked-mac": "npm run prebuild && npm run test && build --mac --dir",
"unpacked-win": "npm run prebuild && npm run test && build --win --x64 --dir && npm run rename-exe",
"unpacked-win-x86": "npm run prebuild && npm run test && build --win --ia32 --dir && npm run rename-exe",
@ -88,11 +88,12 @@
"async": "^2.1.5",
"auto-launch": "^5.0.1",
"electron-context-menu": "^0.8.0",
"electron-squirrel-startup": "^1.0.0",
"keymirror": "0.1.1",
"winreg": "^1.2.3",
"electron-dl": "^1.9.0",
"filesize": "^3.5.10"
"electron-squirrel-startup": "^1.0.0",
"filesize": "^3.5.10",
"keymirror": "0.1.1",
"randomstring": "^1.1.5",
"winreg": "^1.2.3"
},
"optionalDependencies": {
"screen-snippet": "git+https://github.com/symphonyoss/ScreenSnippet.git#v1.0.1"