Line data Source code
1 : import { ScalarFilter } from './sql/sqlframe_builder.js';
2 : import { VariantKind } from "../utils/variant.js";
3 : import { OrdinalGridColumnGroup, StringGridColumnGroup } from "./computation_types.js";
4 :
5 1 : export const HISTOGRAM_FILTER = Symbol("HISTOGRAM_FILTER");
6 1 : export const MOST_FREQUENT_FILTER = Symbol("MOST_FREQUENT_FILTER");
7 :
8 : export type CrossFilterPredicate =
9 : | VariantKind<typeof HISTOGRAM_FILTER, HistogramFilterPredicate>
10 : | VariantKind<typeof MOST_FREQUENT_FILTER, MostFrequentFilterPredicate>
11 : ;
12 :
13 : export interface HistogramFilterPredicate {
14 : /// The selection range
15 : selection: [number, number];
16 : /// The scalar filters
17 : filters: ScalarFilter[];
18 : }
19 :
20 : export interface MostFrequentFilterPredicate {
21 : /// The stable identifier of the selected value
22 : valueId: number;
23 : /// The scalar filters
24 : filters: ScalarFilter[];
25 : }
26 :
27 1 : export class CrossFilters {
28 : /// The column filters
29 1 : columnFilters: { [key: number]: CrossFilterPredicate };
30 :
31 1 : constructor() {
32 1 : this.columnFilters = {};
33 1 : }
34 :
35 1 : public clone(): CrossFilters {
36 1 : const copy = new CrossFilters();
37 1 : copy.columnFilters = {
38 1 : ...this.columnFilters
39 1 : };
40 1 : return copy;
41 1 : }
42 :
43 1 : public equals(other: CrossFilters): boolean {
44 1 : if (Object.keys(this.columnFilters).length != Object.keys(other.columnFilters).length) {
45 1 : return false;
46 : }
47 1 : for (const [key, a] of Object.entries(this.columnFilters)) {
48 1 : const bFilter = other.columnFilters[+key];
49 1 : if (bFilter === undefined || a.type != bFilter.type) {
50 1 : return false;
51 : }
52 1 : switch (a.type) {
53 1 : case HISTOGRAM_FILTER: {
54 1 : const b = bFilter.value as HistogramFilterPredicate;
55 1 : if (a.value.selection == null) {
56 0 : if (b.selection != null) {
57 0 : return false;
58 : }
59 1 : } else {
60 1 : if (b.selection == null) {
61 0 : return false;
62 : }
63 1 : if (
64 1 : a.value.selection[0] != b.selection[0] ||
65 1 : a.value.selection[1] != b.selection[1]
66 : ) {
67 1 : return false;
68 : }
69 1 : }
70 1 : break;
71 1 : }
72 1 : case MOST_FREQUENT_FILTER: {
73 1 : const b = bFilter.value as MostFrequentFilterPredicate;
74 1 : if (a.value.valueId != b.valueId) {
75 1 : return false;
76 : }
77 1 : break;
78 1 : }
79 1 : }
80 1 : }
81 1 : return true;
82 1 : }
83 :
84 1 : public createFilterTransforms(): ScalarFilter[] {
85 1 : const transforms: ScalarFilter[] = [];
86 1 : for (const v of Object.values(this.columnFilters)) {
87 1 : switch (v.type) {
88 1 : case HISTOGRAM_FILTER:
89 1 : for (const filter of v.value.filters) {
90 1 : transforms.push(filter);
91 : }
92 1 : break;
93 1 : case MOST_FREQUENT_FILTER:
94 1 : for (const filter of v.value.filters) {
95 1 : transforms.push(filter);
96 : }
97 : break;
98 1 : }
99 : }
100 1 : return transforms;
101 1 : }
102 :
103 1 : public containsHistogramFilter(columnGroupId: number, brush: [number, number] | null): boolean {
104 1 : const existing = this.columnFilters[columnGroupId];
105 1 : if (brush == null) {
106 1 : return existing === undefined;
107 : }
108 1 : if (existing === undefined || existing.type != HISTOGRAM_FILTER) {
109 1 : return false;
110 : }
111 1 : return existing.value.selection[0] == brush[0]
112 1 : && existing.value.selection[1] == brush[1];
113 1 : }
114 :
115 1 : public addHistogramFilter(columnGroupId: number, columnGroup: OrdinalGridColumnGroup, brush: [number, number] | null) {
116 1 : if (brush == null) {
117 1 : delete this.columnFilters[columnGroupId];
118 1 : return;
119 1 : }
120 1 : let filters: ScalarFilter[] = [];
121 1 : if (columnGroup.binFieldName != null && brush != null) {
122 1 : filters.push({ fieldName: columnGroup.binFieldName, op: ">=", value: brush[0] });
123 1 : filters.push({ fieldName: columnGroup.binFieldName, op: "<=", value: brush[1] });
124 1 : }
125 1 : this.columnFilters[columnGroupId] = {
126 1 : type: HISTOGRAM_FILTER,
127 1 : value: {
128 1 : selection: brush,
129 1 : filters,
130 1 : }
131 1 : };
132 1 : }
133 :
134 1 : public containsMostFrequentValueFilter(columnGroupId: number, valueId: number | null): boolean {
135 1 : const existing = this.columnFilters[columnGroupId];
136 1 : if (valueId == null) {
137 1 : return existing === undefined;
138 : }
139 1 : return existing?.type == MOST_FREQUENT_FILTER && existing.value.valueId == valueId;
140 1 : }
141 :
142 1 : public addMostFrequentValueFilter(columnGroupId: number, columnGroup: StringGridColumnGroup, valueId: number | null) {
143 1 : if (valueId == null) {
144 1 : delete this.columnFilters[columnGroupId];
145 1 : return;
146 1 : }
147 1 : if (columnGroup.valueIdFieldName == null) {
148 1 : return;
149 : }
150 1 : this.columnFilters[columnGroupId] = {
151 1 : type: MOST_FREQUENT_FILTER,
152 1 : value: {
153 1 : valueId,
154 1 : filters: [{ fieldName: columnGroup.valueIdFieldName, op: "=", value: valueId }],
155 1 : }
156 1 : };
157 1 : }
158 1 : }
|