(core) Linking summary tables grouped by list columns

Summary:
Prefix keys of `LinkingState.filterColValues` with `_contains:` when the source column is a ChoiceList or ReferenceList.

This is parsed out to make a boolean `isContainsFilter` which is kept in each value of `QueryRefs.filterTuples` (previously `filterPairs`).

Then when converting back in `convertQueryFromRefs` we construct `Query.contains: {[colId: string]: boolean}`.

Finally `getFilterFunc` uses `Query.contains` to decide what kind of filtering to do.

This is not pretty, but the existing code is already very complex and it was hard to find something that wouldn't require touching loads of code just to make things compile.

Test Plan: Added a new nbrowser test and fixture, tests that selecting a source table by summary tables grouped by a choicelist column, non-list column, and both all filter the correct data.

Reviewers: dsagal

Reviewed By: dsagal

Differential Revision: https://phab.getgrist.com/D2940
This commit is contained in:
Alex Hall
2021-08-10 20:21:03 +02:00
parent 4d526da58f
commit 7f1f8fc9e6
8 changed files with 128 additions and 68 deletions

View File

@@ -22,8 +22,8 @@ import {
DataSourceTransformed,
ForkResult,
ImportResult,
Query,
QueryResult
QueryResult,
ServerQuery
} from 'app/common/ActiveDocAPI';
import {ApiError} from 'app/common/ApiError';
import {mapGetOrSet, MapWithTTL} from 'app/common/AsyncCreate';
@@ -612,7 +612,7 @@ export class ActiveDoc extends EventEmitter {
* @param {Boolean} waitForFormulas: If true, wait for all data to be loaded/calculated. If false,
* special "pending" values may be returned.
*/
public async fetchQuery(docSession: OptDocSession, query: Query,
public async fetchQuery(docSession: OptDocSession, query: ServerQuery,
waitForFormulas: boolean = false): Promise<TableDataAction> {
this._inactivityTimer.ping(); // The doc is in active use; ping it to stay open longer.
@@ -693,7 +693,7 @@ export class ActiveDoc extends EventEmitter {
* Makes a query (documented elsewhere) and subscribes to it, so that the client receives
* docActions that affect this query's results.
*/
public async useQuerySet(docSession: OptDocSession, query: Query): Promise<QueryResult> {
public async useQuerySet(docSession: OptDocSession, query: ServerQuery): Promise<QueryResult> {
this.logInfo(docSession, "useQuerySet(%s, %s)", docSession, query);
// TODO implement subscribing to the query.
// - Convert tableId+colIds to TableData/ColData references
@@ -1356,7 +1356,7 @@ export class ActiveDoc extends EventEmitter {
}
}
private async _fetchQueryFromDB(query: Query, onDemand: boolean): Promise<TableDataAction> {
private async _fetchQueryFromDB(query: ServerQuery, onDemand: boolean): Promise<TableDataAction> {
// Expand query to compute formulas (or include placeholders for them).
const expandedQuery = expandQuery(query, this.docData!, onDemand);
const marshalled = await this.docStorage.fetchQuery(expandedQuery);
@@ -1372,7 +1372,7 @@ export class ActiveDoc extends EventEmitter {
return toTableDataAction(query.tableId, table);
}
private async _fetchQueryFromDataEngine(query: Query): Promise<TableDataAction> {
private async _fetchQueryFromDataEngine(query: ServerQuery): Promise<TableDataAction> {
return this._pyCall('fetch_table', query.tableId, true, query.filters);
}

View File

@@ -1,4 +1,4 @@
import {Query} from 'app/common/ActiveDocAPI';
import {ServerQuery} from 'app/common/ActiveDocAPI';
import {ApiError} from 'app/common/ApiError';
import {DocData} from 'app/common/DocData';
import {parseFormula} from 'app/common/Formula';
@@ -10,7 +10,7 @@ import {quoteIdent} from 'app/server/lib/SQLiteDB';
* formulas. Use of this representation should be limited to within a
* trusted part of Grist since it assembles SQL strings.
*/
export interface ExpandedQuery extends Query {
export interface ExpandedQuery extends ServerQuery {
// Errors detected for given columns because of formula issues. We
// need to make sure the result of the query contains these error
// objects. It is awkward to write a sql selection that constructs
@@ -38,7 +38,7 @@ export interface ExpandedQuery extends Query {
*
* If onDemandFormulas is set, ignore stored formula columns, and compute them using SQL.
*/
export function expandQuery(iquery: Query, docData: DocData, onDemandFormulas: boolean = true): ExpandedQuery {
export function expandQuery(iquery: ServerQuery, docData: DocData, onDemandFormulas: boolean = true): ExpandedQuery {
const query: ExpandedQuery = {
tableId: iquery.tableId,
filters: iquery.filters,

View File

@@ -2,7 +2,7 @@ import { ALL_PERMISSION_PROPS } from 'app/common/ACLPermissions';
import { ACLRuleCollection, SPECIAL_RULES_TABLE_ID } from 'app/common/ACLRuleCollection';
import { ActionGroup } from 'app/common/ActionGroup';
import { createEmptyActionSummary } from 'app/common/ActionSummary';
import { Query } from 'app/common/ActiveDocAPI';
import { ServerQuery } from 'app/common/ActiveDocAPI';
import { ApiError } from 'app/common/ApiError';
import { AddRecord, BulkAddRecord, BulkColValues, BulkRemoveRecord, BulkUpdateRecord } from 'app/common/DocActions';
import { RemoveRecord, ReplaceTableData, UpdateRecord } from 'app/common/DocActions';
@@ -170,7 +170,7 @@ export class GranularAccess implements GranularAccessForBundle {
public constructor(
private _docData: DocData,
private _docClients: DocClients,
private _fetchQueryFromDB: (query: Query) => Promise<TableDataAction>,
private _fetchQueryFromDB: (query: ServerQuery) => Promise<TableDataAction>,
private _recoveryMode: boolean,
private _homeDbManager: HomeDBManager | null,
private _docId: string) {
@@ -204,13 +204,6 @@ export class GranularAccess implements GranularAccessForBundle {
this._userAttributesMap = new WeakMap();
}
/**
* Check whether user can carry out query.
*/
public hasQueryAccess(docSession: OptDocSession, query: Query) {
return this.hasTableAccess(docSession, query.tableId);
}
public getUser(docSession: OptDocSession): Promise<UserInfo> {
return this._getUser(docSession);
}