Files
embeddingsearch/src/Server/Views/Home/Index.cshtml

1680 lines
68 KiB
Plaintext

@using Server.Models
@using System.Web
@using Server.Services
@using Server
@inject LocalizationService T
@inject AIProvider AIProvider
@model HomeIndexViewModel
@{
ViewData["Title"] = "Home Page";
int i = 0;
string[] probMethods = [.. Enum.GetValues(typeof(ProbMethodEnum)).Cast<ProbMethodEnum>().Select(e => e.ToString())];
string[] similarityMethods = [.. Enum.GetValues(typeof(SimilarityMethodEnum)).Cast<SimilarityMethodEnum>().Select(e => e.ToString())];
string[] models = AIProvider.GetModels();
Dictionary<int, string> domains = [];
Model.Searchdomains.ForEach(domain =>
{
domains[i++] = domain;
});
}
<div class="container-fluid mt-4">
<h1 class="visually-hidden">embeddingsearch</h1>
<div class="row">
<!-- Sidebar -->
<div class="col-md-4 sidebar" role="complementary">
<div class="card">
<div class="card-body p-2">
<h2 class="visually-hidden">@T["Searchdomain selection"]</h2>
<ul class="list-group list-group-flush mb-2" style="max-height: 60vh; overflow-y: auto;">
@foreach (var domain in domains)
{
<li id="sidebar_domain_@(domain.Key)" class="domain-item list-group-item list-group-item-action text-nowrap" style="width: max-content" title="@domain.Value" onclick="selectDomain(@(domain.Key))">
@domain.Value
</li>
}
</ul>
<button id="searchdomainCreate" class="btn btn-primary w-100">@T["Create"]</button>
</div>
</div>
</div>
<!-- Main Content -->
<div class="col col-md-8" role="main">
<div class="card section-card">
<div class="card-body">
<h2 class="visually-hidden">@T["Searchdomain information and settings"]</h2>
<h3 class="visually-hidden">@T["Actions"]</h3>
<!-- Header -->
<div class="d-flex justify-content-between align-items-center mb-3">
<p id="searchdomainName" class="mb-0 text-nowrap overflow-auto fs-3">Searchdomain</p>
<div class="col-md-3 text-end w-auto">
<button id="searchdomainRename" class="btn btn-warning btn-sm me-2">@T["Rename"]</button>
<button id="searchdomainDelete" class="btn btn-danger btn-sm">@T["Delete"]</button>
</div>
</div>
<!-- Settings -->
<div class="row align-items-center mb-3">
<h3>@T["Settings"]</h3>
<div class="col-md-6">
<input type="checkbox" class="form-check-input" id="searchdomainConfigCacheReconciliation" />
<label class="form-check-label" for="searchdomainConfigCacheReconciliation">@T["Cache reconciliation"]</label>
</div>
<div class="col-md-2 mt-3 mt-md-0">
<button class="btn btn-warning w-100" id="searchdomainConfigUpdate">@T["Update"]</button>
</div>
</div>
<h3 class="visually-hidden">@T["Search cache"]</h3>
<!-- Cache -->
<div class="d-flex align-items-center mb-4">
<div class="me-3">
<strong>@T["Search cache utilization"]:</strong> <span id="cacheUtilization">0.00MiB</span>
</div>
<button id="cacheClear" class="btn btn-warning btn-sm">@T["Clear"]</button>
</div>
<h3 class="visually-hidden">@T["Database size"]</h3>
<!-- Database size -->
<div class="d-flex align-items-center mb-4">
<div class="me-3">
<strong>@T["Database size"]:</strong> <span id="databaseUtilization">0.00MiB</span>
</div>
</div>
<!-- Recent Queries -->
<div class="card section-card mb-4">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<h3>Recent queries</h3>
<input
type="text"
class="form-control form-control-sm w-25"
placeholder="filter"
/>
</div>
<div class="spinner d-none"></div>
<table id="queriesTable" class="table table-striped" style="max-height: 60vh; overflow-y: auto; display: block;">
<thead>
<tr>
<th class="visually-hidden">Name</th>
<th class="visually-hidden">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<!-- Entities -->
<div class="card section-card">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-2">
<h3>Entities</h3>
<input
id="entitiesFilter"
type="text"
class="form-control form-control-sm w-25"
placeholder="filter"
/>
</div>
<div class="spinner d-none"></div>
<table id="entitiesTable" class="table table-striped" style="max-height: 60vh; overflow-y: auto; display: block;">
<thead>
<tr>
<th class="visually-hidden">Name</th>
<th class="visually-hidden">Action</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<buttton class="btn btn-primary" id="entityCreate">@T["Add new entity"]</buttton>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Entity Details Modal -->
<div class="modal fade" id="entityDetailsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-info">
<h2 class="modal-title" id="entityDetailsTitle">@T["Entity Details"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Attributes -->
<h3 class="fs-4">@T["Attributes"]</h3>
<table class="table table-sm table-bordered mb-4">
<thead>
<tr>
<th>@T["Key"]</th>
<th>@T["Value"]</th>
</tr>
</thead>
<tbody id="entityAttributesBody">
</tbody>
</table>
<!-- Datapoints -->
<h3 class="fs-4">@T["Datapoints"]</h3>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>@T["Name"]</th>
<th>@T["ProbMethod"]</th>
<th>@T["SimilarityMethod"]</th>
</tr>
</thead>
<tbody id="entityDatapointsBody">
</tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Query Details Modal -->
<div class="modal fade" id="queryDetailsModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-info">
<h2 class="modal-title" id="queryDetailsTitle">@T["Query Details"] - <span id="queryDetailsQueryName"></span></h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Access times -->
<h3>@T["Access times"]</h3>
<ul id="queryDetailsAccessTimes" class="list-group mb-4"></ul>
<!-- Results -->
<h3>@T["Results"]</h3>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>@T["Score"]</th>
<th>@T["Name"]</th>
</tr>
</thead>
<tbody id="queryDetailsResultsBody"></tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Query Update Modal -->
<div class="modal fade" id="queryUpdateModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-warning">
<h2 class="modal-title" id="queryUpdateTitle">@T["Query Update"] - <span id="queryUpdateQueryName"></span></h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Access times -->
<h3>@T["Access times"]</h3>
<ul id="queryUpdateAccessTimes" class="list-group mb-4"></ul>
<!-- Results -->
<h3>@T["Results"]</h3>
<table class="table table-sm table-striped">
<thead>
<tr>
<th style="width: 85px;">@T["Score"]</th>
<th>@T["Name"]</th>
<th>@T["Action"]</th>
</tr>
</thead>
<tbody id="queryUpdateResultsBody"></tbody>
</table>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" id="queryUpdateConfirm" data-bs-dismiss="modal">
@T["Update"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Rename searchdomain Modal -->
<div class="modal fade" id="renameSearchdomainModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-m modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-warning">
<h2 class="modal-title" id="renameSearchdomainTitle">@T["Rename searchdomain"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- New name -->
<div class="mb-3">
<label for="renameSearchdomainNewName" class="form-label">New name</label>
<input type="text" class="form-control" id="renameSearchdomainNewName" />
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-warning" onclick="renameSearchdomain(getSelectedDomainKey(), document.getElementById('renameSearchdomainNewName').value)" data-bs-dismiss="modal">
Rename
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
Close
</button>
</div>
</div>
</div>
</div>
<!-- Delete searchdomain Modal -->
<div class="modal fade" id="deleteSearchdomainModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-m modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h2 class="modal-title" id="deleteSearchdomainTitle">@T["Delete searchdomain"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>@Html.Raw(T["GenericDeleteConfirmation", "deleteSearchdomainConfirmationModalName"])</p>
<p>@T["IrreversibleActionWarning"]</p>
</div>
<div class="modal-footer">
<button type="button" id="searchdomainConfirmDelete" class="btn btn-danger" data-bs-dismiss="modal">
@T["Delete"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Create searchdomain Modal -->
<div class="modal fade" id="createSearchdomainModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-m modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h2 class="modal-title" id="createSearchdomainTitle">@T["Create searchdomain"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<label for="createSearchdomainName" class="form-label">@T["Searchdomain name"]</label>
<input type="text" class="form-control mb-3" id="createSearchdomainName" placeholder="@T["Searchdomain name"]" />
<input type="checkbox" class="form-check-input" id="createSearchdomainWithCacheReconciliation" />
<label class="form-check-label" for="createSearchdomainWithCacheReconciliation">@T["Enable cache reconciliation"]</label>
</div>
<div class="modal-footer">
<button type="button" id="searchdomainConfirmCreate" class="btn btn-primary" data-bs-dismiss="modal">
@T["Create"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Create entity Modal -->
<div class="modal fade" id="createEntityModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h2 class="modal-title" id="createEntityTitle">@T["Create entity"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Entity base -->
<div class="row g-3">
<div class="col-md-6">
<label for="createEntityName" class="form-label">@T["Entity name"]</label>
<input type="text" class="form-control mb-3" id="createEntityName" placeholder="@T["Entity name"]" />
</div>
<div class="col-md-6">
<label for="createEntityProbMethod" class="form-label">@T["Probmethod"]</label>
<input list="probMethods" id="createEntityProbMethod" class="form-control" aria-label="@T["Probmethod"]">
<datalist id="probMethods">
@foreach (string probMethod in probMethods)
{
<option value=@probMethod>@probMethod</option>
}
</datalist>
</div>
</div>
<!-- Attributes -->
<div class="row g-3">
<h3 class="fs-5">@T["Attributes"]</h3>
<table>
<thead>
<tr>
<td>@T["Name"]</td>
<td>@T["Value"]</td>
<td class="visually-hidden">@T["Action"]</td>
</tr>
</thead>
<tbody id="createEntityAttributesContainer"></tbody>
</table>
<button type="button" id="createEntityAttributesAdd" class="btn btn-primary">@T["Add attribute"]</button>
</div>
<!-- Datapoints -->
<div class="row g-3 mt-3">
<h3 class="fs-5">@T["Datapoints"]</h3>
<table>
<thead>
<tr>
<td>@T["Name"]</td>
<td>@T["Value"]</td>
<td>@T["Probmethod_embedding"]</td>
<td>@T["Similarity method"]</td>
<td>@T["Model"]</td>
<td class="visually-hidden">@T["Action"]</td>
</tr>
</thead>
<tbody id="createEntityDatapointsContainer"></tbody>
</table>
<button type="button" id="createEntityDatapointsAdd" class="btn btn-primary">@T["Add datapoint"]</button>
</div>
</div>
<div class="modal-footer">
<button type="button" id="EntityConfirmCreate" class="btn btn-primary" data-bs-dismiss="modal">
@T["Create"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Delete entity Modal -->
<div class="modal fade" id="deleteEntityModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-m modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h2 class="modal-title" id="deleteEntityTitle">@T["Delete entity"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>@Html.Raw(T["GenericDeleteConfirmation", "deleteEntityConfirmationModalName"])</p>
<p>@T["IrreversibleActionWarning"]</p>
</div>
<div class="modal-footer">
<button type="button" id="EntityConfirmDelete" class="btn btn-danger" data-bs-dismiss="modal">
@T["Delete"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Update entity Modal -->
<div class="modal fade" id="updateEntityModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-xl modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-warning text">
<h2 class="modal-title" id="updateEntityTitle">@T["Update entity"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- Entity base -->
<div class="row g-3">
<div class="col-md-6">
<label for="updateEntityName" class="form-label">@T["Entity name"]</label>
<input type="text" class="form-control mb-3" id="updateEntityName" placeholder="@T["Entity name"]" />
</div>
<div class="col-md-6">
<label for="updateEntityProbMethod" class="form-label">@T["Probmethod"]</label>
<input list="probMethods" id="updateEntityProbMethod" class="form-control" aria-label="@T["Probmethod"]">
<datalist id="probMethods">
@foreach (string probMethod in probMethods)
{
<option value=@probMethod>@probMethod</option>
}
</datalist>
</div>
</div>
<!-- Attributes -->
<div class="row g-3">
<h3 class="fs-5">@T["Attributes"]</h3>
<table>
<thead>
<tr>
<td>@T["Name"]</td>
<td>@T["Value"]</td>
<td class="visually-hidden">@T["Action"]</td>
</tr>
</thead>
<tbody id="updateEntityAttributesContainer"></tbody>
</table>
<button type="button" id="updateEntityAttributesAdd" class="btn btn-primary">@T["Add attribute"]</button>
</div>
<!-- Datapoints -->
<div class="row g-3 mt-3">
<h3 class="fs-5">@T["Datapoints"]</h3>
<table>
<thead>
<tr>
<td>@T["Name"]</td>
<td>@T["Value"]</td>
<td>@T["Probmethod_embedding"]</td>
<td>@T["Similarity method"]</td>
<td>@T["Model"]</td>
<td class="visually-hidden">@T["Action"]</td>
</tr>
</thead>
<tbody id="updateEntityDatapointsContainer"></tbody>
</table>
<button type="button" id="updateEntityDatapointsAdd" class="btn btn-primary">@T["Add datapoint"]</button>
</div>
</div>
<div class="modal-footer">
<button type="button" id="EntityConfirmUpdate" class="btn btn-warning" data-bs-dismiss="modal">
@T["Update"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<!-- Delete query Modal -->
<div class="modal fade" id="deleteQueryModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-m modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h2 class="modal-title" id="deleteQueryTitle">@T["Delete query"]</h2>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<p>@Html.Raw(T["GenericDeleteConfirmation", "deleteQueryConfirmationModalName"])</p>
</div>
<div class="modal-footer">
<button type="button" id="queryConfirmDelete" class="btn btn-danger" data-bs-dismiss="modal">
@T["Delete"]
</button>
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
@T["Close"]
</button>
</div>
</div>
</div>
</div>
<script>
var domains = JSON.parse('@Html.Raw(System.Text.Json.JsonSerializer.Serialize(domains))');
var probMethods = JSON.parse('@Html.Raw(System.Text.Json.JsonSerializer.Serialize(probMethods))');
var similarityMethods = JSON.parse('@Html.Raw(System.Text.Json.JsonSerializer.Serialize(similarityMethods))');
var entities = null;
var queries = null;
var models = JSON.parse('@Html.Raw(System.Text.Json.JsonSerializer.Serialize(models))');
let draggedRow = null;
document.addEventListener("dragstart", e => {
if (e.target.tagName === "TR") {
draggedRow = e.target;
e.target.classList.add("table-warning");
}
});
document.addEventListener("dragend", e => {
if (e.target.tagName === "TR") {
e.target.classList.remove("table-warning");
}
document.querySelectorAll(".table-warning").forEach(x => x.classList.remove("table-warning"));
});
document.addEventListener("dragenter", e => {
if (e.target.tagName === "TR") {
e.target.classList.add("table-warning");
} else if (e.target.tagName == "TD") {
e.target.parentElement.classList.add("table-warning");
}
});
document.addEventListener("dragleave", e => {
if (e.target.tagName === "TR") {
e.target.classList.remove("table-warning");
} else if (e.target.tagName == "TD") {
e.target.parentElement.classList.remove("table-warning");
}
});
document.addEventListener("dragover", e => {
if (e.target.closest("tr")) {
e.preventDefault();
}
});
document.addEventListener("drop", e => {
const targetRow = e.target.closest("tr");
if (draggedRow && targetRow && draggedRow !== targetRow && targetRow.parentElement.tagName !== "THEAD") {
if (!draggedRow || !targetRow || draggedRow === targetRow) return;
const rect = targetRow.getBoundingClientRect();
const isAfter = e.clientY > rect.top + rect.height / 2;
const parent = targetRow.parentNode;
if (isAfter) {
parent.insertBefore(draggedRow, targetRow.nextSibling);
} else {
parent.insertBefore(draggedRow, targetRow);
}
}
});
document.addEventListener('DOMContentLoaded', () => {
const filterInput = document.getElementById('entitiesFilter');
filterInput.addEventListener('input', () => {
populateEntitiesTable(filterInput.value);
});
});
document.addEventListener('DOMContentLoaded', () => {
const entitiesFilter = document.getElementById('entitiesFilter');
entitiesFilter.addEventListener('input', () => {
populateEntitiesTable(entitiesFilter.value);
});
const queriesFilter = document.querySelector(
'#queriesTable'
).closest('.card-body').querySelector('input');
queriesFilter.addEventListener('input', () => {
populateQueriesTable(queriesFilter.value);
});
selectDomain(0);
document
.getElementById('searchdomainRename')
.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('renameSearchdomainModal')
);
// Fill in searchdomain current name
const domainKey = getSelectedDomainKey();
document.getElementById(
'renameSearchdomainNewName'
).value = domains[domainKey];
modal.show();
});
document
.getElementById('searchdomainDelete')
.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('deleteSearchdomainModal')
);
modal.show();
let searchdomain = domains[getSelectedDomainKey()];
let deleteSearchdomainConfirmationModalName = document.getElementById('deleteSearchdomainConfirmationModalName');
deleteSearchdomainConfirmationModalName.textContent = searchdomain;
});
document
.getElementById('searchdomainConfirmDelete')
.addEventListener('click', () => {
const domainKey = getSelectedDomainKey();
deleteSearchdomain(domainKey);
selectDomain(0);
});
document
.getElementById('searchdomainConfigUpdate')
.addEventListener('click', () => {
const domainKey = getSelectedDomainKey();
const cacheReconciliation = document.getElementById('searchdomainConfigCacheReconciliation').checked;
updateSearchdomainConfig(domainKey, { CacheReconciliation: cacheReconciliation});
});
document
.getElementById('searchdomainCreate')
.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('createSearchdomainModal')
);
modal.show();
});
document
.getElementById('entityCreate')
.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('createEntityModal')
);
modal.show();
document.getElementById('createEntityDatapointsAdd').click();
});
document
.getElementById('createEntityAttributesAdd')
.addEventListener('click', () => {
var uuid = crypto.randomUUID();
addEntityAttribute('createEntityAttributesContainer', uuid);
});
document
.getElementById('createEntityDatapointsAdd')
.addEventListener('click', () => {
var uuid = crypto.randomUUID();
addEntityDatapoint('createEntityDatapointsContainer', uuid, "", "");
});
document
.getElementById('EntityConfirmCreate')
.addEventListener('click', () => {
var name = document.getElementById('createEntityName').value;
var probMethod = document.getElementById('createEntityProbMethod').value;
var attributes = getEntityAttributes();
var datapoints = getEntityDatapoints();
var data = [{
"name": name,
"probmethod": probMethod,
"searchdomain": encodeURIComponent(domains[getSelectedDomainKey()]),
"attributes": attributes,
"datapoints": datapoints
}];
showToast("@T["Creating entity"]", "primary");
fetch(`/Entity/Index`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(async response => {
result = await response.json();
if (response.ok && result.Success) {
showToast("@T["Entity was created successfully"]", "success");
console.log('Entity was created successfully');
selectDomain(getSelectedDomainKey());
} else {
showToast("@T["Failed to create entity"]", "danger");
console.error('Failed to create entity:', result.Message);
}
}).catch(error => {
showToast("@T["Failed to create entity"]", "danger");
console.error('Error creating entity:', error);
});
});
document
.getElementById('searchdomainConfirmCreate')
.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('createSearchdomainModal')
);
const name = document.getElementById('createSearchdomainName').value;
const cacheReconciliation = document.getElementById('createSearchdomainWithCacheReconciliation').checked;
const settings = { CacheReconciliation: cacheReconciliation };
// Implement create logic here
fetch(`/Searchdomain/Create?searchdomain=${encodeURIComponent(name)}&settings=${JSON.stringify(settings)}`, {
method: 'GET'
}).then(response => {
if (response.ok) {
showToast("@T["Searchdomain was created successfully"]", "success");
console.log('Searchdomain created successfully');
// Reload the page to show the new searchdomain
location.reload();
} else {
showToast("@T["Failed to create searchdomain"]", "danger");
console.error('Failed to create searchdomain');
}
}).catch(error => {
showToast("@T["Failed to create searchdomain"]", "danger");
console.error('Error creating searchdomain:', error);
});
});
document
.getElementById('cacheClear')
.addEventListener('click', () => {
const domainKey = getSelectedDomainKey();
fetch(`/Searchdomain/ClearSearchCache?searchdomain=${encodeURIComponent(domains[domainKey])}`, {
method: 'GET'
}).then(response => {
if (response.ok) {
showToast("@T["Searchdomain cache was cleared successfully"]", "success");
console.log('Searchdomain cache cleared successfully');
// Update cache utilization display
document.querySelector('#cacheUtilization').innerText = '0.00MiB';
} else {
showToast("@T["Failed to clear searchdomain cache"]", "danger");
console.error('Failed to clear searchdomain cache');
}
}).catch(error => {
showToast("@T["Failed to clear searchdomain cache"]", "danger");
console.error('Error clearing searchdomain cache:', error);
});
});
document
.getElementById('EntityConfirmDelete')
.addEventListener('click', () => {
const domainKey = getSelectedDomainKey();
const entityName = document.getElementById('EntityConfirmDelete').getAttribute('data-name');
fetch(`/Entity/Delete?searchdomain=${encodeURIComponent(domains[domainKey])}&entityName=${entityName}`, {
method: 'GET'
}).then(async response => {
let result = await response.json();
if (response.ok && result.Success) {
showToast("@T["Entity was deleted successfully"]", "success");
console.log('Entity deleted successfully');
selectDomain(getSelectedDomainKey());
} else {
showToast("@T["Failed to delete entity"]", "danger");
console.error('Failed to delete entity:', result.Message);
}
}).catch(error => {
console.error('Error deleting entity:', error);
});
});
document
.getElementById('updateEntityAttributesAdd')
.addEventListener('click', () => {
var uuid = crypto.randomUUID();
addEntityAttribute('updateEntityAttributesContainer', uuid);
});
document
.getElementById('updateEntityDatapointsAdd')
.addEventListener('click', () => {
var uuid = crypto.randomUUID();
addEntityDatapoint('updateEntityDatapointsContainer', uuid, "", "");
});
document
.getElementById('EntityConfirmUpdate')
.addEventListener('click', () => {
var name = document.getElementById('updateEntityName').value;
var probMethod = document.getElementById('updateEntityProbMethod').value;
var attributes = getEntityAttributes('updateEntityAttributesContainer');
var datapoints = getEntityDatapoints('updateEntityDatapointsContainer');
var data = [{
"name": name,
"probmethod": probMethod,
"searchdomain": encodeURIComponent(domains[getSelectedDomainKey()]),
"attributes": attributes,
"datapoints": datapoints
}];
showToast("@T["Updating entity"]", "primary");
fetch(`/Entity/Index`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(async response => {
result = await response.json();
if (response.ok && result.Success) {
showToast("@T["Entity was updated successfully"]", "success");
console.log('Entity was updated successfully');
selectDomain(getSelectedDomainKey());
} else {
showToast("@T["Failed to update entity"]", "danger");
console.error('Failed to update entity:', result.Message);
}
}).catch(error => {
showToast("@T["Failed to update entity"]", "danger");
console.error('Error update entity:', error);
});
});
document
.getElementById('queryConfirmDelete')
.addEventListener('click', () => {
let searchdomain = domains[getSelectedDomainKey()];
let query = document.getElementById('deleteQueryConfirmationModalName').textContent;
fetch(`/Searchdomain/Searches?searchdomain=${searchdomain}&query=${query}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json'
}
}).then(async response => {
result = await response.json();
if (response.ok && result.Success) {
showToast("@T["Search query was deleted successfully"]", "success");
console.log('Search query was deleted successfully');
selectDomain(getSelectedDomainKey());
} else {
showToast("@T["Failed to delete search query"]", "danger");
console.error('Failed to delete search query:', result.Message);
}
}).catch(error => {
showToast("@T["Failed to delete search query"]", "danger");
console.error('Failed to delete search query:', error);
});
});
document
.getElementById('queryUpdateConfirm')
.addEventListener('click', () => {
let searchdomain = domains[getSelectedDomainKey()];
let query = document.getElementById('queryUpdateQueryName').textContent;
let data = getQueryUpdateTableData();
console.log()
fetch(`/Searchdomain/Searches?searchdomain=${searchdomain}&query=${query}`, {
method: 'PATCH',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
}).then(async response => {
result = await response.json();
if (response.ok && result.Success) {
showToast("@T["Searchdomain was created successfully"]", "success");
console.log('Search query was updated successfully');
selectDomain(getSelectedDomainKey());
} else {
showToast("@T["Updating search query failed"]", "danger");
console.error('Updating search query failed:', result.Message);
}
}).catch(error => {
showToast("@T["Updating search query failed"]", "danger");
console.error('Updating search query failed:', error);
});
});
});
function deleteSearchdomain(domainKey) {
// Implement delete logic here
fetch(`/Searchdomain/Delete?searchdomain=${encodeURI(domains[domainKey])}`, {
method: 'GET'
}).then(async response => {
var result = await response.json();;
if (response.ok && result.Success === true) {
showToast("@T["Searchdomain was deleted successfully"]", "success");
// Remove from sidebar
var domainItem = document.getElementById('sidebar_domain_' + domainKey);
domainItem.remove();
console.log('Searchdomain deleted successfully');
} else {
showToast("@T["Failed to delete searchdomain"]", "danger");
console.error('Failed to delete searchdomain:', result.Message);
}
}).catch(error => {
showToast("@T["Failed to delete searchdomain"]", "danger");
console.error('Error deleting searchdomain:', error);
});
}
function renameSearchdomain(domainKey, newName) {
// Implement rename logic here
fetch(`/Searchdomain/Update?searchdomain=${encodeURI(domains[domainKey])}&newName=${newName}`, {
method: 'GET'
}).then(async response => {
var result = await response.json();
if (response.ok && result.Success === true) {
showToast("@T["Searchdomain was renamed successfully"]", "success");
// Update sidebar and header name
var domainItem = document.getElementById('sidebar_domain_' + domainKey);
domainItem.innerText = newName;
document.querySelector('.section-card h3').innerText = newName;
domains[domainKey] = newName;
console.log('Searchdomain renamed successfully');
} else {
showToast("@T["Failed to rename searchdomain"]", "danger");
console.error('Failed to rename searchdomain:', result.Message);
}
}).catch(error => {
showToast("@T["Failed to rename searchdomain"]", "danger");
console.error('Failed to rename searchdomain:', error);
});
}
function updateSearchdomainConfig(domainKey, newSettings) {
// Implement update logic here
fetch(`/Searchdomain/UpdateSettings?searchdomain=${encodeURIComponent(domains[domainKey])}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(newSettings)
}).then(async response => {
var result = await response.json();
if (response.ok && result.Success === true) {
showToast("@T["Searchdomain settings were updated successfully"]", "success");
console.log('Searchdomain settings updated successfully');
} else {
showToast("@T["Updating searchdomain settings failed"]", "danger");
console.error('Failed to update searchdomain settings');
}
}).catch(error => {
showToast("@T["Updating searchdomain settings failed"]", "danger");
console.error('Error updating searchdomain settings:', error);
});
}
function getSelectedDomainKey() {
return document.querySelector('.domain-item.active').id.split("_")[2] - 0;
}
function getSearchdomainConfig(domainKey) {
return fetch(`/Searchdomain/GetSettings?searchdomain=${encodeURIComponent(domains[domainKey])}`)
.then(r => r.json());
}
function getSearchdomainCacheUtilization(domainKey) {
return fetch(`/Searchdomain/GetSearchCacheSize?searchdomain=${encodeURIComponent(domains[domainKey])}`)
.then(r => r.json());
}
function getSearchdomainDatabaseUtilization(domainKey) {
return fetch(`/Searchdomain/GetDatabaseSize?searchdomain=${encodeURIComponent(domains[domainKey])}`)
.then(r => r.json());
}
function selectDomain(domainKey) {
document.querySelectorAll('.domain-item').forEach(item => {
item.classList.remove('active');
});
var selectedItem = document.getElementById('sidebar_domain_' + domainKey);
selectedItem.classList.add('active');
var domainName = domains[domainKey];
document.querySelector('#searchdomainName').innerText = domainName;
let searchdomainConfigPromise = getSearchdomainConfig(getSelectedDomainKey());
let configElementCachereconciliation = document.getElementById('searchdomainConfigCacheReconciliation');
let cacheUtilizationPromise = getSearchdomainCacheUtilization(getSelectedDomainKey());
let databaseUtilizationPromise = getSearchdomainDatabaseUtilization(getSelectedDomainKey());
/* ---------- ENTITIES ---------- */
let entitiesUrl = `/Entity/List?searchdomain=${encodeURIComponent(domainName)}&returnEmbeddings=false&returnModels=true`;
let entitiesCard = document.querySelector("#entitiesTable").parentElement;
clearEntitiesTable();
showThrobber(entitiesCard);
fetch(entitiesUrl)
.then(r => r.json())
.then(data => {
entities = data.Results;
populateEntitiesTable();
hideThrobber(entitiesCard);
})
.catch(err => {
console.error(err);
flagSearchdomainAsErroneous(domainKey);
hideThrobber(entitiesCard);
});
/* ---------- QUERIES ---------- */
let queriesUrl = `/Searchdomain/GetSearches?searchdomain=${encodeURIComponent(domainName)}`;
let queriesCard = document.querySelector("#queriesTable").parentElement;
clearQueriesTable();
showThrobber(queriesCard);
fetch(queriesUrl)
.then(r => r.json())
.then(data => {
queries = Object.entries(data.Searches).map(key => ({"Name": key[0], "AccessDateTimes": key[1].AccessDateTimes, "Results": key[1].Results}));
populateQueriesTable();
hideThrobber(queriesCard);
})
.catch(err => {
console.error('Error fetching queries:', err);
hideThrobber(queriesCard);
});
searchdomainConfigPromise.then(searchdomainConfig => {
if (searchdomainConfig != null && searchdomainConfig.Settings != null)
{
console.log(searchdomainConfig);
configElementCachereconciliation.checked = searchdomainConfig.Settings.CacheReconciliation;
configElementCachereconciliation.disabled = false;
} else {
configElementCachereconciliation.disabled = true;
showToast("@T["Unable to fetch searchdomain config"]", "danger");
console.error('Failed to fetch searchdomain config');
}
});
cacheUtilizationPromise.then(cacheUtilization => {
if (cacheUtilization != null && cacheUtilization.SearchCacheSizeBytes != null)
{
document.querySelector('#cacheUtilization').innerText =
`${NumberOfBytesAsHumanReadable(cacheUtilization.SearchCacheSizeBytes)}`;
} else {
showToast("@T["Unable to fetch searchdomain cache utilization"]", "danger");
console.error('Failed to fetch searchdomain cache utilization');
}
});
databaseUtilizationPromise.then(databaseUtilization => {
if (databaseUtilization != null && databaseUtilization.SearchdomainDatabaseSizeBytes != null)
{
document.querySelector('#databaseUtilization').innerText =
`${NumberOfBytesAsHumanReadable(databaseUtilization.SearchdomainDatabaseSizeBytes)}`;
} else {
showToast("@T["Unable to fetch searchdomain database utilization"]", "danger");
console.error('Failed to fetch searchdomain database utilization');
}
});
}
function clearEntitiesTable() {
var tableBody = document.querySelector('#entitiesTable tbody');
tableBody.innerHTML = '';
}
function clearQueriesTable() {
document.querySelector('#queriesTable tbody').innerHTML = '';
}
function populateEntitiesTable(filterText = '') {
if (!entities) return;
var tableBody = document.querySelector('#entitiesTable tbody');
tableBody.innerHTML = '';
const normalizedFilter = filterText.toLowerCase();
let isFirstEntity = true;
entities
.filter(e => e.Name.toLowerCase().includes(normalizedFilter))
.forEach(entity => {
var row = document.createElement('tr');
var nameCell = document.createElement('td');
nameCell.textContent = entity.Name;
if (isFirstEntity) {
nameCell.classList.add('w-100'); // Otherwise the table doesn't use the full width
isFirstEntity = false;
}
row.appendChild(nameCell);
var actionCell = document.createElement('td');
actionCell.classList.add('btn-group');
var detailsButton = document.createElement('button');
detailsButton.className = 'btn btn-info btn-sm';
detailsButton.textContent = '@T["Details"]';
detailsButton.setAttribute("data-index", entities.findIndex(en => en == entity));
detailsButton.addEventListener('click', () => {
showEntityDetails(entity);
});
var updateButton = document.createElement('button');
updateButton.className = 'btn btn-warning btn-sm';
updateButton.textContent = '@T["Update"]';
updateButton.setAttribute("data-index", entities.findIndex(en => en == entity));
updateButton.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('updateEntityModal')
);
modal.show();
let updateEntityName = document.getElementById('updateEntityName');
updateEntityName.value = entity.Name;
let updateEntityProbMethod = document.getElementById('updateEntityProbMethod');
updateEntityProbMethod.value = entity.ProbMethod;
let updateEntityAttributesContainer = document.getElementById('updateEntityAttributesContainer');
updateEntityAttributesContainer.textContent = "";
for (i in entity.Attributes)
{
let attribute = entity.Attributes[i];
addEntityAttribute('updateEntityAttributesContainer', attribute.Name, attribute.Name, attribute.Value);
}
let updateEntityDatapointsContainer = document.getElementById('updateEntityDatapointsContainer');
updateEntityDatapointsContainer.textContent = "";
for (i in entity.Datapoints)
{
let datapoint = entity.Datapoints[i];
addEntityDatapoint('updateEntityDatapointsContainer', datapoint.Name, datapoint.Name, null, datapoint.ProbMethod, datapoint.SimilarityMethod, Array.from(datapoint.Embeddings, x => x.Model));
}
});
var deleteButton = document.createElement('button');
deleteButton.className = 'btn btn-danger btn-sm';
deleteButton.textContent = '@T["Delete"]';
deleteButton.setAttribute("data-index", entities.findIndex(en => en == entity));
deleteButton.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('deleteEntityModal')
);
modal.show();
let entityConfirmDelete = document.getElementById('EntityConfirmDelete');
entityConfirmDelete.setAttribute('data-name', entity.Name);
let deleteEntityConfirmationModalName = document.getElementById('deleteEntityConfirmationModalName');
deleteEntityConfirmationModalName.textContent = entity.Name;
});
actionCell.appendChild(detailsButton);
actionCell.appendChild(updateButton);
actionCell.appendChild(deleteButton);
row.appendChild(actionCell);
tableBody.appendChild(row);
});
}
function populateQueriesTable(filterText = '') {
if (!queries) return;
const tbody = document.querySelector('#queriesTable tbody');
tbody.innerHTML = '';
const normalizedFilter = filterText.toLowerCase();
queries
.filter(q => q.Name?.toLowerCase().includes(normalizedFilter))
.forEach(query => {
const row = document.createElement('tr');
const nameCell = document.createElement('td');
nameCell.textContent = query.Name;
nameCell.classList.add('col-md-12');
row.appendChild(nameCell);
const actionCell = document.createElement('td');
actionCell.classList.add('btn-group');
const btnDetails = document.createElement('button');
btnDetails.className = 'btn btn-sm btn-info';
btnDetails.textContent = '@T["Details"]';
btnDetails.addEventListener('click', () => {
showQueryDetails(query);
});
const btnUpdate = document.createElement('button');
btnUpdate.className = 'btn btn-sm btn-warning';
btnUpdate.textContent = '@T["Update"]';
btnUpdate.addEventListener('click', () => {
showQueryUpdate(query);
});
const btnDelete = document.createElement('button');
btnDelete.className = 'btn btn-sm btn-danger';
btnDelete.textContent = '@T["Delete"]';
btnDelete.addEventListener('click', () => {
const modal = new bootstrap.Modal(
document.getElementById('deleteQueryModal')
);
modal.show();
let queryConfirmDelete = document.getElementById('queryConfirmDelete');
queryConfirmDelete.setAttribute('data-name', query.Name);
let deleteQueryConfirmationModalName = document.getElementById('deleteQueryConfirmationModalName');
deleteQueryConfirmationModalName.textContent = query.Name;
});
actionCell.appendChild(btnDetails);
actionCell.appendChild(btnUpdate);
actionCell.appendChild(btnDelete);
row.appendChild(actionCell);
tbody.appendChild(row);
});
}
function flagSearchdomainAsErroneous(domainKey) {
var domainItem = document.getElementById('sidebar_domain_' + domainKey);
domainItem.classList.add('list-group-item-danger');
}
function showThrobber(element = null) {
if (element == null) element = document;
element.querySelector('.spinner').classList.remove('d-none');
}
function hideThrobber(element = null) {
if (element == null) element = document;
element.querySelector('.spinner').classList.add('d-none');
}
function showEntityDetails(entity) {
// Title
document.getElementById('entityDetailsTitle').innerText = entity.Name;
// Attributes
const attrBody = document.getElementById('entityAttributesBody');
attrBody.innerHTML = '';
const attributes = entity.Attributes || {};
const attrKeys = Object.keys(attributes);
if (attrKeys.length === 0) {
attrBody.innerHTML = `
<tr>
<td colspan="2" class="text-muted text-center">No attributes</td>
</tr>`;
} else {
attrKeys.forEach(key => {
const row = document.createElement('tr');
attributeKV = attributes[key];
row.innerHTML = `
<td>${attributeKV["Name"]}</td>
<td>${attributeKV["Value"]}</td>
`;
attrBody.appendChild(row);
});
}
// Datapoints
const dpBody = document.getElementById('entityDatapointsBody');
dpBody.innerHTML = '';
(entity.Datapoints || []).forEach(dp => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${dp.Name}</td>
<td>${dp.ProbMethod}</td>
<td>${dp.SimilarityMethod}</td>
`;
dpBody.appendChild(row);
});
// Show modal
const modal = new bootstrap.Modal(
document.getElementById('entityDetailsModal')
);
modal.show();
}
function showQueryDetails(query) {
// Title
document.getElementById('queryDetailsQueryName').innerText = query.Name;
/* ---------- Access times ---------- */
const accessList = document.getElementById('queryDetailsAccessTimes');
accessList.innerHTML = '';
if (!query.AccessDateTimes || query.AccessDateTimes.length === 0) {
accessList.innerHTML = `
<li class="list-group-item text-muted text-center">
No access times
</li>`;
} else {
query.AccessDateTimes.forEach(dt => {
const li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = new Date(dt).toLocaleString();
accessList.appendChild(li);
});
}
/* ---------- Results ---------- */
const resultsBody = document.getElementById('queryDetailsResultsBody');
resultsBody.innerHTML = '';
if (!query.Results || query.Results.length === 0) {
resultsBody.innerHTML = `
<tr>
<td colspan="2" class="text-muted text-center">
No results
</td>
</tr>`;
} else {
query.Results.forEach(r => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${r.Score.toFixed(4)}</td>
<td class="text-break">${r.Name}</td>
`;
resultsBody.appendChild(row);
});
}
// Show modal
const modal = new bootstrap.Modal(
document.getElementById('queryDetailsModal')
);
modal.show();
}
function showQueryUpdate(query) {
// Title
document.getElementById('queryUpdateQueryName').innerText = query.Name;
/* ---------- Access times ---------- */
const accessList = document.getElementById('queryUpdateAccessTimes');
accessList.innerHTML = '';
if (!query.AccessDateTimes || query.AccessDateTimes.length === 0) {
accessList.innerHTML = `
<li class="list-group-item text-muted text-center">
No access times
</li>`;
} else {
query.AccessDateTimes.forEach(dt => {
const li = document.createElement('li');
li.className = 'list-group-item';
li.textContent = new Date(dt).toLocaleString();
accessList.appendChild(li);
});
}
/* ---------- Results ---------- */
const resultsBody = document.getElementById('queryUpdateResultsBody');
resultsBody.innerHTML = '';
if (!query.Results || query.Results.length === 0) {
resultsBody.innerHTML = `
<tr>
<td colspan="2" class="text-muted text-center">
No results
</td>
</tr>`;
} else {
query.Results.forEach(r => {
const row = document.createElement('tr');
row.setAttribute("draggable", true);
const tdScore = document.createElement('td');
const scoreInput = document.createElement('input');
scoreInput.classList.add('form-control');
scoreInput.value = r.Score.toFixed(4);
tdScore.append(scoreInput);
const tdName = document.createElement('td');
tdName.classList.add("text-break");
tdName.innerText = r.Name;
const tdAction = document.createElement('td');
const deleteButton = document.createElement('button');
deleteButton.classList.add('btn', 'btn-danger', 'btn-sm');
deleteButton.innerText = '@T["Delete"]';
deleteButton.onclick = function() {
row.remove();
};
tdAction.append(deleteButton);
row.append(tdScore);
row.append(tdName);
row.append(tdAction);
resultsBody.appendChild(row);
});
}
// Show modal
const modal = new bootstrap.Modal(
document.getElementById('queryUpdateModal')
);
modal.show();
}
function NumberOfBytesAsHumanReadable(bytes, decimals = 2) {
if (bytes === 0) return '0 B';
if (bytes > 1.20892581961*(10**27)) return "∞ B";
const units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']; //
const unitIndex = Math.floor(Math.log2(bytes) / 10);
const unit = units[Math.min(unitIndex, units.length - 1)];
const value = bytes / Math.pow(1024, unitIndex);
return `${value.toFixed(decimals)} ${unit}`;
}
function getEntityAttributes(containerName = 'createEntityAttributesContainer') {
const container = document.getElementById(containerName);
const rows = container.querySelectorAll('tr[data-uid]');
const attributes = {};
rows.forEach(row => {
const keyInput = row.querySelector('input.form-control');
const valueTextarea = row.querySelector('textarea.form-control');
if (keyInput && valueTextarea) {
const key = keyInput.value.trim();
const value = valueTextarea.value.trim();
if (key) {
attributes[key] = value;
}
}
});
return attributes;
}
function getEntityDatapoints(containerName = 'createEntityDatapointsContainer') {
const container = document.getElementById(containerName);
const rows = container.querySelectorAll('tr[data-uid]');
const datapoints = [];
rows.forEach(row => {
const keyInput = row.querySelector('input.form-control');
const valueTextarea = row.querySelector('textarea.form-control');
const probmethodSelect = row.querySelector('select.probmethod');
const similaritymethodSelect = row.querySelector('select.similarityMethod');
const modelSelect = row.querySelector('select.model');
if (keyInput && valueTextarea && probmethodSelect && modelSelect) {
const key = keyInput.value.trim();
var value = valueTextarea.value.trim();
if (value != null && value.length == 0) {
value = null;
}
const probMethod = probmethodSelect.selectedOptions[0].value;
const similarityMethod = similaritymethodSelect.selectedOptions[0].value;
const modelSelection = Array.from(modelSelect.selectedOptions)
.map(option => option.value);
if (key) {
let datapoint = {
"name": key,
"text": value,
"probmethod_embedding": probMethod,
"similarityMethod": similarityMethod,
"model": modelSelection
};
datapoints.push(datapoint);
}
}
});
return datapoints;
}
function addEntityAttribute(containerName = 'createEntityAttributesContainer', uid, key = '', value = '')
{
var container = document.getElementById(containerName);
var tr = document.createElement('tr');
tr.setAttribute('data-uid', uid);
var tdKey = document.createElement('td');
var keyInput = document.createElement('input');
keyInput.classList.add('form-control');
keyInput.ariaLabel = '@T["Key"]';
keyInput.value = key;
tdKey.append(keyInput);
var tdValue = document.createElement('td');
var ValueInput = document.createElement('textarea');
ValueInput.classList.add('form-control');
ValueInput.ariaLabel = '@T["Value"]';
ValueInput.value = value;
tdValue.append(ValueInput);
var tdAction = document.createElement('td');
var tdButton = document.createElement('button');
tdButton.classList.add('btn', 'btn-danger');
tdButton.textContent = '@T["Remove"]';
tdButton.ariaLabel = '@T["Remove attribute"]';
tdAction.append(tdButton);
tdButton.onclick = function() {
tr.remove();
};
tr.append(tdKey);
tr.append(tdValue);
tr.append(tdAction);
container.append(tr);
}
function addEntityDatapoint(containerName = 'createEntityDatapointsContainer', uid, name = null, value = null, probmethod_embedding = null, similarityMethod = null, selectedModels = [])
{
var entityDatapointsContainer = document.getElementById(containerName);
var tr = document.createElement('tr');
tr.setAttribute('data-uid', uid);
var tdName = document.createElement('td');
var nameInput = document.createElement('input');
nameInput.classList.add('form-control')
nameInput.ariaLabel = '@T["Name"]';
if (name != null) nameInput.value = name;
tdName.append(nameInput);
var tdValue = document.createElement('td');
var ValueInput = document.createElement('textarea');
ValueInput.classList.add('form-control')
ValueInput.ariaLabel = '@T["Value"]';
if (value != null) {
ValueInput.value = value;
} else {
nameInput.readOnly = true;
ValueInput.readOnly = true;
}
tdValue.append(ValueInput);
var tdProbmethodEmbedding = document.createElement('td');
var probMethodSelect = document.createElement('select');
probMethodSelect.classList.add('probmethod');
for (i in probMethods)
{
let probMethodSelectElement = document.createElement('option');
probMethodSelectElement.value = probMethods[i];
probMethodSelectElement.text = probMethods[i];
probMethodSelect.append(probMethodSelectElement);
}
probMethodSelect.classList.add('form-control')
probMethodSelect.ariaLabel = '@T["Probmethod_embedding"]';
if (probmethod_embedding != null) probMethodSelect.value = probmethod_embedding;
tdProbmethodEmbedding.append(probMethodSelect);
var tdSimilarityMethod = document.createElement('td');
var similarityMethodSelect = document.createElement('select');
similarityMethodSelect.classList.add('similarityMethod');
for (i in similarityMethods)
{
let similarityMethodSelectElement = document.createElement('option');
similarityMethodSelectElement.value = similarityMethods[i];
similarityMethodSelectElement.text = similarityMethods[i];
similarityMethodSelect.append(similarityMethodSelectElement);
}
similarityMethodSelect.classList.add('form-control')
similarityMethodSelect.ariaLabel = '@T["Similarity method"]';
if (similarityMethod != null) similarityMethodSelect.value = similarityMethod;
tdSimilarityMethod.append(similarityMethodSelect);
var tdModel = document.createElement('td');
var modelSelect = document.createElement('select');
modelSelect.classList.add('model');
modelSelect.multiple = true;
for (i in models)
{
let modelSelectElement = document.createElement('option');
modelSelectElement.value = models[i];
modelSelectElement.text = models[i];
modelSelectElement.selected = selectedModels.findIndex(x => x === models[i] || (x + ":latest") === models[i]) >= 0;
modelSelect.append(modelSelectElement);
}
modelSelect.classList.add('form-control')
modelSelect.ariaLabel = '@T["Probmethod_embedding"]';
if (value == null) {
modelSelect.disabled = true;
}
tdModel.append(modelSelect);
var tdAction = document.createElement('td');
var tdButton = document.createElement('button');
tdButton.classList.add('btn', 'btn-danger');
tdButton.textContent = '@T["Remove"]';
tdButton.ariaLabel = '@T["Remove attribute"]';
tdAction.append(tdButton);
tdButton.onclick = function() {
tr.remove();
};
tr.append(tdName);
tr.append(tdValue);
tr.append(tdProbmethodEmbedding);
tr.append(tdSimilarityMethod);
tr.append(tdModel);
tr.append(tdAction);
entityDatapointsContainer.append(tr);
}
function getQueryUpdateTableData() {
const tbody = document.getElementById('queryUpdateResultsBody');
const rows = tbody.getElementsByTagName('tr');
const result = [];
for (let i = 0; i < rows.length; i++) {
const row = rows[i];
const cells = row.getElementsByTagName('td');
// Get the text content from the second cell (index 1) which contains the path
const score = parseFloat(cells[0].firstChild.value);
const name = cells[1].textContent.trim();
result.push({
"Score": score,
"Name": name
});
}
return result;
}
</script>