mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
Search implementation
This commit is contained in:
parent
c6f84adc44
commit
1e9648deed
156
demo/search.html
Normal file
156
demo/search.html
Normal 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>
|
@ -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
146
js/search/search.js
Normal 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
|
||||
};
|
62
js/search/searchLibrary.js
Normal file
62
js/search/searchLibrary.js
Normal 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
BIN
libsymphonysearch.dylib
Executable file
Binary file not shown.
13
package.json
13
package.json
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user