Browse Source

fixed sorting for string values

pull/15691/head
dashevchenko 6 days ago
parent
commit
12daf918e5
  1. 16
      ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts
  2. 15
      ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts
  3. 31
      ui-ngx/src/app/shared/models/page/page-link.ts

16
ui-ngx/src/app/modules/home/components/widget/lib/alarm/alarms-table-widget.component.ts

@ -47,7 +47,7 @@ import {
isUndefined
} from '@core/utils';
import cssjs from '@core/css/css';
import { sortItems } from '@shared/models/page/page-link';
import { SortColumnType, sortItems } from '@shared/models/page/page-link';
import { Direction } from '@shared/models/page/sort-order';
import { CollectionViewer, DataSource, SelectionModel } from '@angular/cdk/collections';
import { BehaviorSubject, forkJoin, fromEvent, merge, Observable, of, Subject, Subscription } from 'rxjs';
@ -118,6 +118,7 @@ import {
dataKeyToEntityKey,
dataKeyTypeToEntityKeyType,
entityDataPageLinkSortDirection,
EntityKeyType,
KeyFilter
} from '@app/shared/models/query/query.models';
import { DataKeyType } from '@shared/models/telemetry/telemetry.models';
@ -730,8 +731,12 @@ export class AlarmsTableWidgetComponent extends PageComponent implements OnInit,
this.pageLink.sortOrder = null;
}
const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns);
const sortColumnType: SortColumnType = key
? (key.type === EntityKeyType.ENTITY_FIELD || key.type === EntityKeyType.ALARM_FIELD ? 'entityField'
: key.type === EntityKeyType.TIME_SERIES ? 'timeseries' : 'attribute')
: 'entityField';
const keyFilters: KeyFilter[] = null; // TODO:
this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, keyFilters);
this.alarmsDatasource.loadAlarms(this.pageLink, sortOrderLabel, sortColumnType, keyFilters);
this.ctx.detectChanges();
}
@ -1256,6 +1261,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
private appliedPageLink: AlarmDataPageLink;
private appliedSortOrderLabel: string;
private appliedSortColumnType: SortColumnType = 'entityField';
private reserveSpaceForHiddenAction = true;
private cellButtonActions: TableCellButtonActionDescriptor[];
@ -1294,11 +1300,13 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
this.pageDataSubject.complete();
}
loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
loadAlarms(pageLink: AlarmDataPageLink, sortOrderLabel: string,
sortColumnType: SortColumnType, keyFilters: KeyFilter[]) {
this.dataLoading = true;
// this.clear();
this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel;
this.appliedSortColumnType = sortColumnType;
this.subscription.subscribeForAlarms(pageLink, keyFilters);
}
@ -1330,7 +1338,7 @@ class AlarmsDatasource implements DataSource<AlarmDataInfo> {
}
if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) {
const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC;
alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc));
alarms = alarms.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc, this.appliedSortColumnType));
}
if (this.selection.hasValue()) {
const alarmIds = alarms.map((alarm) => alarm.id.id);

15
ui-ngx/src/app/modules/home/components/widget/lib/entity/entities-table-widget.component.ts

@ -100,7 +100,7 @@ import {
EntityKeyType,
KeyFilter
} from '@shared/models/query/query.models';
import { sortItems } from '@shared/models/page/page-link';
import { SortColumnType, sortItems } from '@shared/models/page/page-link';
import { entityFields } from '@shared/models/entity.models';
import { DatePipe } from '@angular/common';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
@ -617,8 +617,12 @@ export class EntitiesTableWidgetComponent extends PageComponent implements OnIni
this.pageLink.sortOrder = null;
}
const sortOrderLabel = fromEntityColumnDef(this.sort.active, this.columns);
const sortColumnType: SortColumnType = key
? (key.type === EntityKeyType.ENTITY_FIELD ? 'entityField'
: key.type === EntityKeyType.TIME_SERIES ? 'timeseries' : 'attribute')
: 'entityField';
const keyFilters: KeyFilter[] = null; // TODO:
this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, keyFilters);
this.entityDatasource.loadEntities(this.pageLink, sortOrderLabel, sortColumnType, keyFilters);
this.ctx.detectChanges();
}
@ -865,6 +869,7 @@ class EntityDatasource implements DataSource<EntityData> {
private appliedPageLink: EntityDataPageLink;
private appliedSortOrderLabel: string;
private appliedSortColumnType: SortColumnType = 'entityField';
private reserveSpaceForHiddenAction = true;
private cellButtonActions: TableCellButtonActionDescriptor[];
@ -905,11 +910,13 @@ class EntityDatasource implements DataSource<EntityData> {
this.pageDataSubject.complete();
}
loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string, keyFilters: KeyFilter[]) {
loadEntities(pageLink: EntityDataPageLink, sortOrderLabel: string,
sortColumnType: SortColumnType, keyFilters: KeyFilter[]) {
this.dataLoading = true;
// this.clear();
this.appliedPageLink = pageLink;
this.appliedSortOrderLabel = sortOrderLabel;
this.appliedSortColumnType = sortColumnType;
this.subscription.subscribeForPaginatedData(0, pageLink, keyFilters);
}
@ -934,7 +941,7 @@ class EntityDatasource implements DataSource<EntityData> {
});
if (this.appliedSortOrderLabel && this.appliedSortOrderLabel.length) {
const asc = this.appliedPageLink.sortOrder.direction === Direction.ASC;
entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc));
entities = entities.sort((a, b) => sortItems(a, b, this.appliedSortOrderLabel, asc, this.appliedSortColumnType));
}
if (!dynamicWidthCellButtonActions && this.cellButtonActions.length && entities.length) {
maxCellButtonAction = entities[0].actionCellButtons.length;

31
ui-ngx/src/app/shared/models/page/page-link.ts

@ -86,24 +86,29 @@ const defaultPageLinkSearch: PageLinkSearchFunction<any> =
return false;
};
export function sortItems(item1: any, item2: any, property: string, asc: boolean): number {
export type SortColumnType = 'entityField' | 'attribute' | 'timeseries';
export function sortItems(item1: any, item2: any, property: string, asc: boolean,
columnType: SortColumnType = 'entityField'): number {
const item1Value = getDescendantProp(item1, property);
const item2Value = getDescendantProp(item2, property);
const item1Empty = item1Value === null || item1Value === undefined || item1Value === '';
const item2Empty = item2Value === null || item2Value === undefined || item2Value === '';
// Mirror backend's nulls ordering only for column types where the SQL ORDER BY sees real NULLs
// (boolean/numeric attribute values and entity-field columns). String attribute/telemetry
// values are coalesced to '' in SQL, so the backend ignores the strategy there and naive
// compare below already matches its order. EDQS has its own fixed null handling (ASC=NULLS
// FIRST, DESC=NULLS LAST) that doesn't honor the strategy at all, so skip this branch then
// — naive compare already matches EDQS's behavior for the values we care about.
// Mirror backend's nulls ordering. EDQS uses fixed NULLS FIRST regardless of strategy and
// naive compare below already matches it, so skip this branch when EDQS is on.
// For entityField columns the ORDER BY hits a real nullable DB column → strategy always applies.
// For attribute/timeseries the strategy only applies to numeric/boolean values; string/json
// are coalesced to '' on the backend, so naive compare below already matches its order.
if (!edqsEnabled && (item1Empty || item2Empty) && !(item1Empty && item2Empty)) {
const other = item1Empty ? item2Value : item1Value;
const otherIsBoolOrNum =
typeof other === 'boolean' || other === 'true' || other === 'false' ||
(typeof other === 'number' && isFinite(other)) ||
(typeof other === 'string' && other.trim() !== '' && !isNaN(Number(other)));
if (otherIsBoolOrNum) {
let applyStrategy = columnType === 'entityField';
if (!applyStrategy) {
const other = item1Empty ? item2Value : item1Value;
applyStrategy =
typeof other === 'boolean' || other === 'true' || other === 'false' ||
(typeof other === 'number' && isFinite(other)) ||
(typeof other === 'string' && other.trim() !== '' && !isNaN(Number(other)));
}
if (applyStrategy) {
const nullsFirst = nullsOrderStrategy === 'nulls_first'
|| (nullsOrderStrategy === 'default' && !asc);
if (item1Empty) {

Loading…
Cancel
Save