Speed up medicine autocomplete with cache and 10-row limit
This commit is contained in:
parent
935d6edd0b
commit
fad0d89260
|
|
@ -2242,8 +2242,17 @@ class Opd_prescription extends BaseController
|
||||||
unset($row);
|
unset($row);
|
||||||
|
|
||||||
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
||||||
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId) : [];
|
$candidateIds = [];
|
||||||
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId) : [];
|
foreach ($rows as $row) {
|
||||||
|
$medId = (int) ($row['id'] ?? 0);
|
||||||
|
if ($medId > 0) {
|
||||||
|
$candidateIds[] = $medId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$candidateIds = array_values(array_unique($candidateIds));
|
||||||
|
|
||||||
|
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId, $candidateIds) : [];
|
||||||
|
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId, $candidateIds) : [];
|
||||||
|
|
||||||
foreach ($rows as &$row) {
|
foreach ($rows as &$row) {
|
||||||
$medId = (int) ($row['id'] ?? 0);
|
$medId = (int) ($row['id'] ?? 0);
|
||||||
|
|
@ -4435,10 +4444,19 @@ class Opd_prescription extends BaseController
|
||||||
{
|
{
|
||||||
$q = trim((string) $this->request->getGet('q'));
|
$q = trim((string) $this->request->getGet('q'));
|
||||||
$scope = strtolower(trim((string) $this->request->getGet('scope')));
|
$scope = strtolower(trim((string) $this->request->getGet('scope')));
|
||||||
|
$limit = (int) ($this->request->getGet('limit') ?? 10);
|
||||||
|
if ($limit <= 0) {
|
||||||
|
$limit = 10;
|
||||||
|
}
|
||||||
|
$limit = min(10, $limit);
|
||||||
if (! in_array($scope, ['active', 'favorite', 'all'], true)) {
|
if (! in_array($scope, ['active', 'favorite', 'all'], true)) {
|
||||||
$scope = 'active';
|
$scope = 'active';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($q === '') {
|
||||||
|
return $this->response->setJSON(['rows' => []]);
|
||||||
|
}
|
||||||
|
|
||||||
$table = $this->findExistingTable(['opd_med_master']);
|
$table = $this->findExistingTable(['opd_med_master']);
|
||||||
if ($table === null) {
|
if ($table === null) {
|
||||||
return $this->response->setJSON(['rows' => []]);
|
return $this->response->setJSON(['rows' => []]);
|
||||||
|
|
@ -4486,6 +4504,18 @@ class Opd_prescription extends BaseController
|
||||||
$select .= ',' . $remarkField . ' as remark';
|
$select .= ',' . $remarkField . ' as remark';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
||||||
|
$cacheKey = 'medicine_search:v2:' . md5(json_encode([
|
||||||
|
'q' => strtoupper($q),
|
||||||
|
'scope' => $scope,
|
||||||
|
'user' => $userId,
|
||||||
|
'limit' => $limit,
|
||||||
|
]));
|
||||||
|
$cachedRows = cache($cacheKey);
|
||||||
|
if (is_array($cachedRows)) {
|
||||||
|
return $this->response->setJSON(['rows' => $cachedRows]);
|
||||||
|
}
|
||||||
|
|
||||||
$builder = $this->db->table($table)->select($select);
|
$builder = $this->db->table($table)->select($select);
|
||||||
if ($q !== '') {
|
if ($q !== '') {
|
||||||
$builder->groupStart()
|
$builder->groupStart()
|
||||||
|
|
@ -4497,7 +4527,7 @@ class Opd_prescription extends BaseController
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $builder
|
$rows = $builder
|
||||||
->limit(500)
|
->limit(120)
|
||||||
->get()
|
->get()
|
||||||
->getResultArray();
|
->getResultArray();
|
||||||
|
|
||||||
|
|
@ -4505,7 +4535,6 @@ class Opd_prescription extends BaseController
|
||||||
return $this->response->setJSON(['rows' => []]);
|
return $this->response->setJSON(['rows' => []]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
|
||||||
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId) : [];
|
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId) : [];
|
||||||
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId) : [];
|
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId) : [];
|
||||||
$qUpper = strtoupper($q);
|
$qUpper = strtoupper($q);
|
||||||
|
|
@ -4572,12 +4601,14 @@ class Opd_prescription extends BaseController
|
||||||
return $nameA <=> $nameB;
|
return $nameA <=> $nameB;
|
||||||
});
|
});
|
||||||
|
|
||||||
$rows = array_slice($rows, 0, 40);
|
$rows = array_slice($rows, 0, $limit);
|
||||||
foreach ($rows as &$row) {
|
foreach ($rows as &$row) {
|
||||||
unset($row['_score']);
|
unset($row['_score']);
|
||||||
}
|
}
|
||||||
unset($row);
|
unset($row);
|
||||||
|
|
||||||
|
cache()->save($cacheKey, $rows, 120);
|
||||||
|
|
||||||
return $this->response->setJSON(['rows' => $rows]);
|
return $this->response->setJSON(['rows' => $rows]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -5105,17 +5136,22 @@ class Opd_prescription extends BaseController
|
||||||
/**
|
/**
|
||||||
* @return array<int, bool>
|
* @return array<int, bool>
|
||||||
*/
|
*/
|
||||||
private function getUserFavoriteMedicineIds(int $userId): array
|
private function getUserFavoriteMedicineIds(int $userId, array $medIds = []): array
|
||||||
{
|
{
|
||||||
if ($userId <= 0 || ! $this->db->tableExists('opd_medicine_favorites')) {
|
if ($userId <= 0 || ! $this->db->tableExists('opd_medicine_favorites')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $this->db->table('opd_medicine_favorites')
|
$builder = $this->db->table('opd_medicine_favorites')
|
||||||
->select('med_id')
|
->select('med_id')
|
||||||
->where('user_id', $userId)
|
->where('user_id', $userId);
|
||||||
->get()
|
|
||||||
->getResultArray();
|
$medIds = array_values(array_unique(array_filter(array_map('intval', $medIds), static fn (int $id): bool => $id > 0)));
|
||||||
|
if (! empty($medIds)) {
|
||||||
|
$builder->whereIn('med_id', $medIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $builder->get()->getResultArray();
|
||||||
|
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
|
@ -5131,17 +5167,22 @@ class Opd_prescription extends BaseController
|
||||||
/**
|
/**
|
||||||
* @return array<int, array{use_count:int,last_used_at:string}>
|
* @return array<int, array{use_count:int,last_used_at:string}>
|
||||||
*/
|
*/
|
||||||
private function getUserMedicineUsageMap(int $userId): array
|
private function getUserMedicineUsageMap(int $userId, array $medIds = []): array
|
||||||
{
|
{
|
||||||
if ($userId <= 0 || ! $this->db->tableExists('opd_medicine_usage')) {
|
if ($userId <= 0 || ! $this->db->tableExists('opd_medicine_usage')) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = $this->db->table('opd_medicine_usage')
|
$builder = $this->db->table('opd_medicine_usage')
|
||||||
->select('med_id,use_count,last_used_at')
|
->select('med_id,use_count,last_used_at')
|
||||||
->where('user_id', $userId)
|
->where('user_id', $userId);
|
||||||
->get()
|
|
||||||
->getResultArray();
|
$medIds = array_values(array_unique(array_filter(array_map('intval', $medIds), static fn (int $id): bool => $id > 0)));
|
||||||
|
if (! empty($medIds)) {
|
||||||
|
$builder->whereIn('med_id', $medIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
$rows = $builder->get()->getResultArray();
|
||||||
|
|
||||||
$out = [];
|
$out = [];
|
||||||
foreach ($rows as $row) {
|
foreach ($rows as $row) {
|
||||||
|
|
|
||||||
|
|
@ -1112,6 +1112,10 @@
|
||||||
var investigationBatchActive = false;
|
var investigationBatchActive = false;
|
||||||
var activeMedicineScope = 'all';
|
var activeMedicineScope = 'all';
|
||||||
var medicineSuggestRows = [];
|
var medicineSuggestRows = [];
|
||||||
|
var medicineSearchCache = {};
|
||||||
|
var medicineSearchCacheTtlMs = 120000;
|
||||||
|
var medInputTimer = null;
|
||||||
|
var crgcMedInputTimer = null;
|
||||||
var medicineDoseMasterCache = { dose: [], when: [], freq: [], where: [] };
|
var medicineDoseMasterCache = { dose: [], when: [], freq: [], where: [] };
|
||||||
var investigationProfiles = {
|
var investigationProfiles = {
|
||||||
cardiac: ['ECG', 'Lipid Profile', 'CBC'],
|
cardiac: ['ECG', 'Lipid Profile', 'CBC'],
|
||||||
|
|
@ -1173,6 +1177,33 @@
|
||||||
}, 'json');
|
}, 'json');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getMedicineSearchCacheKey(query, scope) {
|
||||||
|
return (scope || 'all') + '|' + (query || '').trim().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
function fetchMedicineSuggestions(query, scope, done) {
|
||||||
|
var q = (query || '').trim();
|
||||||
|
if (q.length < 2) {
|
||||||
|
done([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var useScope = (scope || 'all').toString();
|
||||||
|
var cacheKey = getMedicineSearchCacheKey(q, useScope);
|
||||||
|
var now = Date.now();
|
||||||
|
var hit = medicineSearchCache[cacheKey];
|
||||||
|
if (hit && (now - (hit.ts || 0) < medicineSearchCacheTtlMs)) {
|
||||||
|
done(hit.rows || []);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
apiGet('<?= base_url('Opd_prescription/medicine_search') ?>?q=' + encodeURIComponent(q) + '&scope=' + encodeURIComponent(useScope) + '&limit=10', function(data) {
|
||||||
|
var rows = (data && data.rows) ? data.rows : [];
|
||||||
|
medicineSearchCache[cacheKey] = { ts: now, rows: rows };
|
||||||
|
done(rows);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function renderMedicineMasterSelectOptions($select, rows, placeholder) {
|
function renderMedicineMasterSelectOptions($select, rows, placeholder) {
|
||||||
var html = '<option value="">' + $('<div>').text(placeholder || 'Select').html() + '</option>';
|
var html = '<option value="">' + $('<div>').text(placeholder || 'Select').html() + '</option>';
|
||||||
(rows || []).forEach(function(row) {
|
(rows || []).forEach(function(row) {
|
||||||
|
|
@ -4803,13 +4834,18 @@
|
||||||
// Medicine name autocomplete inside create modal
|
// Medicine name autocomplete inside create modal
|
||||||
$('#crgc_med_name').on('input', function() {
|
$('#crgc_med_name').on('input', function() {
|
||||||
var q = ($(this).val() || '').trim();
|
var q = ($(this).val() || '').trim();
|
||||||
|
if (crgcMedInputTimer) {
|
||||||
|
clearTimeout(crgcMedInputTimer);
|
||||||
|
crgcMedInputTimer = null;
|
||||||
|
}
|
||||||
if (q.length < 2) {
|
if (q.length < 2) {
|
||||||
crgcMedSuggestRows = [];
|
crgcMedSuggestRows = [];
|
||||||
$('#crgc_med_suggest').html('');
|
$('#crgc_med_suggest').html('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
apiGet('<?= base_url('Opd_prescription/medicine_search') ?>?q=' + encodeURIComponent(q) + '&scope=all', function(data) {
|
crgcMedInputTimer = setTimeout(function() {
|
||||||
crgcMedSuggestRows = data.rows || [];
|
fetchMedicineSuggestions(q, 'all', function(rows) {
|
||||||
|
crgcMedSuggestRows = rows || [];
|
||||||
var html = '';
|
var html = '';
|
||||||
crgcMedSuggestRows.forEach(function(row) {
|
crgcMedSuggestRows.forEach(function(row) {
|
||||||
html += '<option value="' + $('<div>').text(row.med_name || '').html() + '"'
|
html += '<option value="' + $('<div>').text(row.med_name || '').html() + '"'
|
||||||
|
|
@ -4826,7 +4862,8 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, 180);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#crgc_med_name').on('change', function() {
|
$('#crgc_med_name').on('change', function() {
|
||||||
|
|
@ -5025,14 +5062,19 @@
|
||||||
|
|
||||||
$('#med_name').on('input', function() {
|
$('#med_name').on('input', function() {
|
||||||
var q = ($(this).val() || '').trim();
|
var q = ($(this).val() || '').trim();
|
||||||
|
if (medInputTimer) {
|
||||||
|
clearTimeout(medInputTimer);
|
||||||
|
medInputTimer = null;
|
||||||
|
}
|
||||||
if (q.length < 2) {
|
if (q.length < 2) {
|
||||||
medicineSuggestRows = [];
|
medicineSuggestRows = [];
|
||||||
$('#medicine_suggest').html('');
|
$('#medicine_suggest').html('');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
apiGet('<?= base_url('Opd_prescription/medicine_search') ?>?q=' + encodeURIComponent(q) + '&scope=' + encodeURIComponent(activeMedicineScope), function(data) {
|
medInputTimer = setTimeout(function() {
|
||||||
|
fetchMedicineSuggestions(q, activeMedicineScope, function(rows) {
|
||||||
var html = '';
|
var html = '';
|
||||||
medicineSuggestRows = data.rows || [];
|
medicineSuggestRows = rows || [];
|
||||||
medicineSuggestRows.forEach(function(row) {
|
medicineSuggestRows.forEach(function(row) {
|
||||||
html += '<option '
|
html += '<option '
|
||||||
+ 'value="' + $('<div>').text(row.med_name || '').html() + '" '
|
+ 'value="' + $('<div>').text(row.med_name || '').html() + '" '
|
||||||
|
|
@ -5060,6 +5102,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}, 180);
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#med_name').on('change', function() {
|
$('#med_name').on('change', function() {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user