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);
|
||||
|
||||
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
||||
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId) : [];
|
||||
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId) : [];
|
||||
$candidateIds = [];
|
||||
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) {
|
||||
$medId = (int) ($row['id'] ?? 0);
|
||||
|
|
@ -4435,10 +4444,19 @@ class Opd_prescription extends BaseController
|
|||
{
|
||||
$q = trim((string) $this->request->getGet('q'));
|
||||
$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)) {
|
||||
$scope = 'active';
|
||||
}
|
||||
|
||||
if ($q === '') {
|
||||
return $this->response->setJSON(['rows' => []]);
|
||||
}
|
||||
|
||||
$table = $this->findExistingTable(['opd_med_master']);
|
||||
if ($table === null) {
|
||||
return $this->response->setJSON(['rows' => []]);
|
||||
|
|
@ -4486,6 +4504,18 @@ class Opd_prescription extends BaseController
|
|||
$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);
|
||||
if ($q !== '') {
|
||||
$builder->groupStart()
|
||||
|
|
@ -4497,7 +4527,7 @@ class Opd_prescription extends BaseController
|
|||
}
|
||||
|
||||
$rows = $builder
|
||||
->limit(500)
|
||||
->limit(120)
|
||||
->get()
|
||||
->getResultArray();
|
||||
|
||||
|
|
@ -4505,7 +4535,6 @@ class Opd_prescription extends BaseController
|
|||
return $this->response->setJSON(['rows' => []]);
|
||||
}
|
||||
|
||||
$userId = (int) ($this->getCurrentUserId() ?? 0);
|
||||
$favoriteMap = $userId > 0 ? $this->getUserFavoriteMedicineIds($userId) : [];
|
||||
$usageMap = $userId > 0 ? $this->getUserMedicineUsageMap($userId) : [];
|
||||
$qUpper = strtoupper($q);
|
||||
|
|
@ -4572,12 +4601,14 @@ class Opd_prescription extends BaseController
|
|||
return $nameA <=> $nameB;
|
||||
});
|
||||
|
||||
$rows = array_slice($rows, 0, 40);
|
||||
$rows = array_slice($rows, 0, $limit);
|
||||
foreach ($rows as &$row) {
|
||||
unset($row['_score']);
|
||||
}
|
||||
unset($row);
|
||||
|
||||
cache()->save($cacheKey, $rows, 120);
|
||||
|
||||
return $this->response->setJSON(['rows' => $rows]);
|
||||
}
|
||||
|
||||
|
|
@ -5105,17 +5136,22 @@ class Opd_prescription extends BaseController
|
|||
/**
|
||||
* @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')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->table('opd_medicine_favorites')
|
||||
$builder = $this->db->table('opd_medicine_favorites')
|
||||
->select('med_id')
|
||||
->where('user_id', $userId)
|
||||
->get()
|
||||
->getResultArray();
|
||||
->where('user_id', $userId);
|
||||
|
||||
$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 = [];
|
||||
foreach ($rows as $row) {
|
||||
|
|
@ -5131,17 +5167,22 @@ class Opd_prescription extends BaseController
|
|||
/**
|
||||
* @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')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$rows = $this->db->table('opd_medicine_usage')
|
||||
$builder = $this->db->table('opd_medicine_usage')
|
||||
->select('med_id,use_count,last_used_at')
|
||||
->where('user_id', $userId)
|
||||
->get()
|
||||
->getResultArray();
|
||||
->where('user_id', $userId);
|
||||
|
||||
$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 = [];
|
||||
foreach ($rows as $row) {
|
||||
|
|
|
|||
|
|
@ -1112,6 +1112,10 @@
|
|||
var investigationBatchActive = false;
|
||||
var activeMedicineScope = 'all';
|
||||
var medicineSuggestRows = [];
|
||||
var medicineSearchCache = {};
|
||||
var medicineSearchCacheTtlMs = 120000;
|
||||
var medInputTimer = null;
|
||||
var crgcMedInputTimer = null;
|
||||
var medicineDoseMasterCache = { dose: [], when: [], freq: [], where: [] };
|
||||
var investigationProfiles = {
|
||||
cardiac: ['ECG', 'Lipid Profile', 'CBC'],
|
||||
|
|
@ -1173,6 +1177,33 @@
|
|||
}, '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) {
|
||||
var html = '<option value="">' + $('<div>').text(placeholder || 'Select').html() + '</option>';
|
||||
(rows || []).forEach(function(row) {
|
||||
|
|
@ -4803,13 +4834,18 @@
|
|||
// Medicine name autocomplete inside create modal
|
||||
$('#crgc_med_name').on('input', function() {
|
||||
var q = ($(this).val() || '').trim();
|
||||
if (crgcMedInputTimer) {
|
||||
clearTimeout(crgcMedInputTimer);
|
||||
crgcMedInputTimer = null;
|
||||
}
|
||||
if (q.length < 2) {
|
||||
crgcMedSuggestRows = [];
|
||||
$('#crgc_med_suggest').html('');
|
||||
return;
|
||||
}
|
||||
apiGet('<?= base_url('Opd_prescription/medicine_search') ?>?q=' + encodeURIComponent(q) + '&scope=all', function(data) {
|
||||
crgcMedSuggestRows = data.rows || [];
|
||||
crgcMedInputTimer = setTimeout(function() {
|
||||
fetchMedicineSuggestions(q, 'all', function(rows) {
|
||||
crgcMedSuggestRows = rows || [];
|
||||
var html = '';
|
||||
crgcMedSuggestRows.forEach(function(row) {
|
||||
html += '<option value="' + $('<div>').text(row.med_name || '').html() + '"'
|
||||
|
|
@ -4826,7 +4862,8 @@
|
|||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}, 180);
|
||||
});
|
||||
|
||||
$('#crgc_med_name').on('change', function() {
|
||||
|
|
@ -5025,14 +5062,19 @@
|
|||
|
||||
$('#med_name').on('input', function() {
|
||||
var q = ($(this).val() || '').trim();
|
||||
if (medInputTimer) {
|
||||
clearTimeout(medInputTimer);
|
||||
medInputTimer = null;
|
||||
}
|
||||
if (q.length < 2) {
|
||||
medicineSuggestRows = [];
|
||||
$('#medicine_suggest').html('');
|
||||
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 = '';
|
||||
medicineSuggestRows = data.rows || [];
|
||||
medicineSuggestRows = rows || [];
|
||||
medicineSuggestRows.forEach(function(row) {
|
||||
html += '<option '
|
||||
+ 'value="' + $('<div>').text(row.med_name || '').html() + '" '
|
||||
|
|
@ -5060,6 +5102,7 @@
|
|||
}
|
||||
}
|
||||
});
|
||||
}, 180);
|
||||
});
|
||||
|
||||
$('#med_name').on('change', function() {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user