2017-07-25 10:28:38 -05:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
const randomString = require('randomstring');
|
|
|
|
|
2017-07-27 00:18:11 -05:00
|
|
|
const electron = require('electron');
|
2017-07-31 01:19:16 -05:00
|
|
|
const childProcess = require('child_process');
|
2017-07-27 00:18:11 -05:00
|
|
|
const app = electron.app;
|
|
|
|
const path = require('path');
|
|
|
|
const isDevEnv = require('../utils/misc.js').isDevEnv;
|
|
|
|
const isMac = require('../utils/misc.js').isMac;
|
|
|
|
|
2017-07-31 08:07:30 -05:00
|
|
|
// Search library
|
|
|
|
const libSymphonySearch = require('./searchLibrary');
|
|
|
|
|
|
|
|
// Path for the exec file and the user data folder
|
2017-08-03 04:00:11 -05:00
|
|
|
const userData = path.join(app.getPath('userData'));
|
|
|
|
const execPath = path.dirname(app.getPath('exe'));
|
2017-07-27 09:50:26 -05:00
|
|
|
|
2017-07-31 08:07:30 -05:00
|
|
|
// Constants paths for temp indexing folders
|
|
|
|
const TEMP_BATCH_INDEX_FOLDER = isDevEnv ? './data/temp_batch_indexes' : path.join(userData, 'data/temp_batch_indexes');
|
|
|
|
const TEMP_REALTIME_INDEX = isDevEnv ? './data/temp_realtime_index' : path.join(userData, 'data/temp_realtime_index');
|
|
|
|
const INDEX_PREFIX = isDevEnv ? './data/search_index' : path.join(userData, 'data/search_index');
|
|
|
|
const INDEX_DATA_FOLDER = isDevEnv ? './data' : path.join(userData, 'data');
|
2017-07-25 10:28:38 -05:00
|
|
|
const SEARCH_PERIOD_SUBTRACTOR = 3 * 31 * 24 * 60 * 60 * 1000;//3 months
|
|
|
|
const MINIMUM_DATE = '0000000000000';
|
|
|
|
const MAXIMUM_DATE = '9999999999999';
|
|
|
|
const INDEX_VERSION = 'v1';
|
2017-07-31 08:07:30 -05:00
|
|
|
|
2017-08-03 04:18:56 -05:00
|
|
|
const winArchPath = process.arch === 'ia32' ? 'library/indexvalidator-x86.exe' : 'library/indexvalidator-x64.exe';
|
|
|
|
const rootPath = isMac ? 'library/indexvalidator.exec' : winArchPath;
|
2017-08-03 04:00:11 -05:00
|
|
|
const productionPath = path.join(execPath, isMac ? '..' : '', rootPath);
|
|
|
|
const devPath = path.join(__dirname, '..', '..', rootPath);
|
|
|
|
const libraryPath = isDevEnv ? devPath : productionPath;
|
2017-08-03 00:23:32 -05:00
|
|
|
|
2017-07-31 08:07:30 -05:00
|
|
|
let INDEX_VALIDATOR = libraryPath;
|
2017-07-25 10:28:38 -05:00
|
|
|
|
|
|
|
const SORT_BY_SCORE = 0;
|
|
|
|
|
|
|
|
const BATCH_RANDOM_INDEX_PATH_LENGTH = 20;
|
|
|
|
|
|
|
|
class Search {
|
2017-08-01 07:18:12 -05:00
|
|
|
//TODO: fix eslint class-methods-use-this
|
2017-07-27 09:50:26 -05:00
|
|
|
/*eslint-disable class-methods-use-this */
|
2017-07-25 10:28:38 -05:00
|
|
|
|
|
|
|
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() {
|
2017-07-26 02:43:37 -05:00
|
|
|
return new Promise((resolve) => {
|
2017-07-25 10:28:38 -05:00
|
|
|
if (!this.isInitialized) {
|
|
|
|
this.isInitialized = true;
|
|
|
|
}
|
|
|
|
resolve(libSymphonySearch.symSEInit());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
isLibInit() {
|
|
|
|
return this.initLib();
|
|
|
|
}
|
|
|
|
|
|
|
|
init() {
|
|
|
|
libSymphonySearch.symSEInit();
|
2017-08-01 12:14:10 -05:00
|
|
|
libSymphonySearch.symSEEnsureFolderExists(INDEX_DATA_FOLDER);
|
2017-07-25 10:28:38 -05:00
|
|
|
libSymphonySearch.symSERemoveFolder(TEMP_REALTIME_INDEX);
|
|
|
|
libSymphonySearch.symSERemoveFolder(TEMP_BATCH_INDEX_FOLDER);
|
2017-07-31 08:07:30 -05:00
|
|
|
Search.indexValidator(this.indexFolderName);
|
|
|
|
Search.indexValidator(TEMP_REALTIME_INDEX);
|
2017-07-25 10:28:38 -05:00
|
|
|
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);
|
2017-07-27 09:50:26 -05:00
|
|
|
resolve(res);
|
2017-07-25 10:28:38 -05:00
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
mergeIndexBatches() {
|
2017-07-26 02:43:37 -05:00
|
|
|
libSymphonySearch.symSEMergePartialIndexAsync(this.indexFolderName, TEMP_BATCH_INDEX_FOLDER, function (err) {
|
2017-07-25 10:28:38 -05:00
|
|
|
if (err) throw err;
|
2017-07-31 08:07:30 -05:00
|
|
|
libSymphonySearch.symSERemoveFolder(TEMP_BATCH_INDEX_FOLDER)
|
2017-07-25 10:28:38 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-07-27 09:50:26 -05:00
|
|
|
realTimeIndexing(message) {
|
|
|
|
libSymphonySearch.symSEIndexRealTime(TEMP_REALTIME_INDEX, JSON.stringify(message));
|
|
|
|
}
|
|
|
|
|
|
|
|
readJson(batch) {
|
2017-07-25 10:28:38 -05:00
|
|
|
return new Promise((resolve, reject) => {
|
2017-08-01 08:05:51 -05:00
|
|
|
let dirPath = path.join(execPath, isMac ? '..' : '', 'msgsjson', batch);
|
2017-07-31 08:07:30 -05:00
|
|
|
let messageFolderPath = isDevEnv ? path.join('./msgsjson', batch) : dirPath;
|
2017-07-27 00:18:11 -05:00
|
|
|
let files = fs.readdirSync(messageFolderPath);
|
|
|
|
let messageData = [];
|
2017-07-25 10:28:38 -05:00
|
|
|
files.forEach(function (file) {
|
2017-07-27 09:50:26 -05:00
|
|
|
let tempPath = path.join(messageFolderPath, file);
|
|
|
|
let data = fs.readFileSync(tempPath, "utf8");
|
2017-07-25 10:28:38 -05:00
|
|
|
if (data) {
|
2017-07-27 00:18:11 -05:00
|
|
|
messageData.push(JSON.parse(data));
|
|
|
|
resolve(messageData);
|
2017-07-25 10:28:38 -05:00
|
|
|
} 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;
|
2017-08-01 06:49:50 -05:00
|
|
|
let sd_time = MINIMUM_DATE;
|
2017-08-01 07:10:00 -05:00
|
|
|
if (startDate && startDate !== "" && typeof startDate === 'object') {
|
2017-08-01 06:49:50 -05:00
|
|
|
sd_time = new Date(startDate).getTime();
|
|
|
|
if (sd_time >= sd) {
|
|
|
|
sd_time = sd;
|
2017-07-25 10:28:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:50 -05:00
|
|
|
let ed_time = MAXIMUM_DATE;
|
2017-08-01 07:10:00 -05:00
|
|
|
if (endDate && endDate !== "" && typeof endDate === 'object') {
|
2017-08-01 06:49:50 -05:00
|
|
|
ed_time = new Date(endDate).getTime();
|
2017-07-25 10:28:38 -05:00
|
|
|
}
|
2017-08-01 06:49:50 -05:00
|
|
|
|
2017-08-01 07:18:12 -05:00
|
|
|
//TODO: fix eslint no-param-reassign
|
2017-07-26 02:43:37 -05:00
|
|
|
/*eslint-disable no-param-reassign */
|
2017-08-01 06:49:50 -05:00
|
|
|
if (!limit && limit === "" && typeof limit !== 'number' && Math.round(limit) !== limit) {
|
2017-07-25 10:28:38 -05:00
|
|
|
limit = 25;
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:50 -05:00
|
|
|
if (!offset && offset === "" && typeof offset !== 'number' && Math.round(offset) !== offset) {
|
2017-07-25 10:28:38 -05:00
|
|
|
offset = 0
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:50 -05:00
|
|
|
if (!sortOrder && sortOrder === "" && typeof sortOrder !== 'number' && Math.round(sortOrder) !== sortOrder) {
|
2017-07-25 10:28:38 -05:00
|
|
|
sortOrder = SORT_BY_SCORE;
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:50 -05:00
|
|
|
const returnedResult = libSymphonySearch.symSESearch(this.indexFolderName, TEMP_REALTIME_INDEX, q, sd_time.toString(), ed_time.toString(), offset, limit, sortOrder);
|
|
|
|
try {
|
|
|
|
let ret = returnedResult.readCString();
|
|
|
|
resolve(JSON.parse(ret));
|
|
|
|
} finally {
|
2017-07-27 09:50:26 -05:00
|
|
|
libSymphonySearch.symSEFreeResult(returnedResult);
|
2017-07-27 00:18:11 -05:00
|
|
|
}
|
2017-07-25 10:28:38 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2017-08-01 06:49:50 -05:00
|
|
|
static constructQuery(query, senderId, threadId) {
|
|
|
|
let q = query;
|
2017-08-01 07:10:00 -05:00
|
|
|
if (senderId && senderId !== "") {
|
2017-08-01 06:49:50 -05:00
|
|
|
q += ` AND (senderId: ${senderId})`;
|
|
|
|
}
|
2017-08-01 07:10:00 -05:00
|
|
|
if (threadId && threadId !== "") {
|
2017-08-01 06:49:50 -05:00
|
|
|
q += ` AND (threadId: ${threadId})`;
|
|
|
|
}
|
|
|
|
return q;
|
2017-07-25 10:28:38 -05:00
|
|
|
}
|
|
|
|
|
2017-07-31 08:07:30 -05:00
|
|
|
static indexValidator(file) {
|
|
|
|
let data;
|
|
|
|
let result = childProcess.execFileSync(INDEX_VALIDATOR, [file]).toString();
|
|
|
|
try {
|
|
|
|
data = JSON.parse(result);
|
|
|
|
if (data.status === 'OK') {
|
2017-07-31 10:25:19 -05:00
|
|
|
return data;
|
2017-07-31 08:07:30 -05:00
|
|
|
}
|
2017-07-31 10:25:19 -05:00
|
|
|
return new Error('Unable validate index folder')
|
2017-07-31 08:07:30 -05:00
|
|
|
} catch (err) {
|
2017-08-01 06:49:50 -05:00
|
|
|
throw (err);
|
2017-07-31 08:07:30 -05:00
|
|
|
}
|
|
|
|
}
|
2017-07-25 10:28:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
Search: Search
|
|
|
|
};
|