重新处理select中的aggr和total

This commit is contained in:
Xu Chang 2023-11-16 20:21:15 +08:00
parent aeb42fbd8a
commit 69c07c8642
11 changed files with 138 additions and 274 deletions

5
es/AspectDict.d.ts vendored
View File

@ -14,9 +14,8 @@ export type CommonAspectDict<ED extends EntityDict & BaseEntityDict, Cxt extends
option?: OP;
getCount?: true;
}, context: Cxt) => Promise<{
ids: string[];
count?: number;
aggr?: (Partial<ED[T]['Schema']> | undefined)[];
data: Record<string, any>;
total?: number;
}>;
aggregate: <T extends keyof ED, OP extends SelectOption>(params: {
entity: T;

5
es/crud.d.ts vendored
View File

@ -13,9 +13,8 @@ export declare function select<ED extends BaseEntityDict & EntityDict, T extends
getCount?: true;
maxCount?: number;
}, context: Cxt): Promise<{
ids: string[];
count?: number | undefined;
aggr?: (Partial<ED[T]["Schema"]> | undefined)[] | undefined;
data: Record<string, any>;
total?: number | undefined;
}>;
export declare function aggregate<ED extends BaseEntityDict & EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends SelectOption>(params: {
entity: T;

View File

@ -21,100 +21,61 @@ export async function operate(params, context) {
}
return await context.operate(entity, operation, option || {});
}
function pruneAggrResult(schema, entity, result) {
const pruneInner = (e, r) => {
const r2 = {};
let hasAggr = false;
/**
* 因为有cascadeSelect这里要按查询的要求build返回的结构树告知每一层上相关的id/total/aggr信息
* @param schema
* @param entity
* @param result
* @returns
*/
function buildResultTree(schema, entity, result) {
const pruneInner = (e, r, tree) => {
for (const attr in r) {
if (attr.endsWith('$$aggr')) {
hasAggr = true;
Object.assign(r2, {
[attr]: r[attr],
});
tree[attr] = r[attr];
}
else if (typeof r[attr] === 'object') {
const rel = judgeRelation(schema, e, attr);
if (rel === 2 || typeof rel === 'string') {
const rr = pruneInner(rel === 2 ? attr : rel, r[attr]);
if (rr) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
}
tree[attr] = {};
pruneInner(rel === 2 ? attr : rel, r[attr], tree[attr]);
}
else if (rel instanceof Array) {
assert(r[attr] instanceof Array);
const rr = r[attr].map((ele) => pruneInner(rel[0], ele));
if (rr.find((ele) => !ele)) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
tree[attr] = {
data: {},
};
if (r[attr].hasOwnProperty('#total')) {
tree[attr].total = r[attr]['#total'];
}
r[attr].forEach((rr) => {
tree[attr].data[rr.id] = {};
pruneInner(rel[0], rr, tree[attr].data[rr.id]);
});
}
}
}
if (hasAggr) {
return r2;
}
return;
};
const result2 = result.map((row) => pruneInner(entity, row));
if (result2.find(ele => !!ele)) {
return result2;
const root = {
data: {},
};
/**
* 这个total是在cascadeStore的selectAsync处理的有点古怪
*/
if (result.hasOwnProperty('#total')) {
root.total = result['#total'];
}
result.forEach((row) => {
root.data[row.id] = {};
pruneInner(entity, row, root.data[row.id]);
});
return root;
}
export async function select(params, context) {
const { entity, selection, option, getCount, maxCount } = params;
const { randomRange, count } = selection;
const { entity, selection, option } = params;
let selection2 = selection;
if (randomRange) {
// 如果是随机取值这里就从randomRange的ids中随机取
const idSelection = Object.assign({}, selection, {
indexFrom: 0,
count: randomRange,
data: {
id: 1,
},
});
const idResults = await context.select(entity, idSelection, option || {});
const possibility = count / idResults.length;
let reduced = idResults.length - count;
const ids2 = idResults.map(ele => ele.id).filter((id) => {
const rand = Math.random();
if (rand > possibility && reduced) {
reduced--;
return false;
}
return true;
});
selection2.filter = {
id: {
$in: ids2,
},
};
}
const data = await context.select(entity, selection2, option || {});
const result = {
ids: data.map(ele => ele.id),
};
if (getCount && !randomRange) {
const { filter } = selection;
const count = await context.count(entity, Object.assign({}, { filter, count: maxCount || 1000 }), option || {});
Object.assign(result, {
count,
});
}
if (data.length === 0) {
}
else {
const aggrData = pruneAggrResult(context.getSchema(), entity, data);
if (aggrData) {
result.aggr = aggrData;
}
}
return result;
return buildResultTree(context.getSchema(), entity, data);
}
export async function aggregate(params, context) {
const { entity, aggregation, option } = params;

2
es/port.d.ts vendored
View File

@ -1,7 +1,7 @@
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { Importation, Exportation } from 'oak-domain/lib/types/Port';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any>[], exportations: Exportation<ED, keyof ED, any>[]): void;
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function clearPorts(): void;
export declare function importEntity<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
entity: string;

5
lib/AspectDict.d.ts vendored
View File

@ -14,9 +14,8 @@ export type CommonAspectDict<ED extends EntityDict & BaseEntityDict, Cxt extends
option?: OP;
getCount?: true;
}, context: Cxt) => Promise<{
ids: string[];
count?: number;
aggr?: (Partial<ED[T]['Schema']> | undefined)[];
data: Record<string, any>;
total?: number;
}>;
aggregate: <T extends keyof ED, OP extends SelectOption>(params: {
entity: T;

5
lib/crud.d.ts vendored
View File

@ -13,9 +13,8 @@ export declare function select<ED extends BaseEntityDict & EntityDict, T extends
getCount?: true;
maxCount?: number;
}, context: Cxt): Promise<{
ids: string[];
count?: number | undefined;
aggr?: (Partial<ED[T]["Schema"]> | undefined)[] | undefined;
data: Record<string, any>;
total?: number | undefined;
}>;
export declare function aggregate<ED extends BaseEntityDict & EntityDict, T extends keyof ED, Cxt extends AsyncContext<ED>, OP extends SelectOption>(params: {
entity: T;

View File

@ -25,100 +25,61 @@ async function operate(params, context) {
return await context.operate(entity, operation, option || {});
}
exports.operate = operate;
function pruneAggrResult(schema, entity, result) {
const pruneInner = (e, r) => {
const r2 = {};
let hasAggr = false;
/**
* 因为有cascadeSelect这里要按查询的要求build返回的结构树告知每一层上相关的id/total/aggr信息
* @param schema
* @param entity
* @param result
* @returns
*/
function buildResultTree(schema, entity, result) {
const pruneInner = (e, r, tree) => {
for (const attr in r) {
if (attr.endsWith('$$aggr')) {
hasAggr = true;
Object.assign(r2, {
[attr]: r[attr],
});
tree[attr] = r[attr];
}
else if (typeof r[attr] === 'object') {
const rel = (0, relation_1.judgeRelation)(schema, e, attr);
if (rel === 2 || typeof rel === 'string') {
const rr = pruneInner(rel === 2 ? attr : rel, r[attr]);
if (rr) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
}
tree[attr] = {};
pruneInner(rel === 2 ? attr : rel, r[attr], tree[attr]);
}
else if (rel instanceof Array) {
(0, assert_1.assert)(r[attr] instanceof Array);
const rr = r[attr].map((ele) => pruneInner(rel[0], ele));
if (rr.find((ele) => !ele)) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
tree[attr] = {
data: {},
};
if (r[attr].hasOwnProperty('#total')) {
tree[attr].total = r[attr]['#total'];
}
r[attr].forEach((rr) => {
tree[attr].data[rr.id] = {};
pruneInner(rel[0], rr, tree[attr].data[rr.id]);
});
}
}
}
if (hasAggr) {
return r2;
}
return;
};
const result2 = result.map((row) => pruneInner(entity, row));
if (result2.find(ele => !!ele)) {
return result2;
const root = {
data: {},
};
/**
* 这个total是在cascadeStore的selectAsync处理的有点古怪
*/
if (result.hasOwnProperty('#total')) {
root.total = result['#total'];
}
result.forEach((row) => {
root.data[row.id] = {};
pruneInner(entity, row, root.data[row.id]);
});
return root;
}
async function select(params, context) {
const { entity, selection, option, getCount, maxCount } = params;
const { randomRange, count } = selection;
const { entity, selection, option } = params;
let selection2 = selection;
if (randomRange) {
// 如果是随机取值这里就从randomRange的ids中随机取
const idSelection = Object.assign({}, selection, {
indexFrom: 0,
count: randomRange,
data: {
id: 1,
},
});
const idResults = await context.select(entity, idSelection, option || {});
const possibility = count / idResults.length;
let reduced = idResults.length - count;
const ids2 = idResults.map(ele => ele.id).filter((id) => {
const rand = Math.random();
if (rand > possibility && reduced) {
reduced--;
return false;
}
return true;
});
selection2.filter = {
id: {
$in: ids2,
},
};
}
const data = await context.select(entity, selection2, option || {});
const result = {
ids: data.map(ele => ele.id),
};
if (getCount && !randomRange) {
const { filter } = selection;
const count = await context.count(entity, Object.assign({}, { filter, count: maxCount || 1000 }), option || {});
Object.assign(result, {
count,
});
}
if (data.length === 0) {
}
else {
const aggrData = pruneAggrResult(context.getSchema(), entity, data);
if (aggrData) {
result.aggr = aggrData;
}
}
return result;
return buildResultTree(context.getSchema(), entity, data);
}
exports.select = select;
async function aggregate(params, context) {

2
lib/port.d.ts vendored
View File

@ -1,7 +1,7 @@
import { EntityDict } from 'oak-domain/lib/types/Entity';
import { Importation, Exportation } from 'oak-domain/lib/types/Port';
import { AsyncContext } from 'oak-domain/lib/store/AsyncRowStore';
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any>[], exportations: Exportation<ED, keyof ED, any>[]): void;
export declare function registerPorts<ED extends EntityDict>(importations: Importation<ED, keyof ED, any, any>[], exportations: Exportation<ED, keyof ED, any, any>[]): void;
export declare function clearPorts(): void;
export declare function importEntity<ED extends EntityDict, Cxt extends AsyncContext<ED>>(params: {
entity: string;

View File

@ -19,9 +19,8 @@ export type CommonAspectDict<ED extends EntityDict & BaseEntityDict, Cxt extends
params: { entity: T; selection: ED[T]['Selection']; option?: OP; getCount?: true },
context: Cxt
) => Promise<{
ids: string[];
count?: number;
aggr?: (Partial<ED[T]['Schema']> | undefined)[];
data: Record<string, any>;
total?: number;
}>;
aggregate: <T extends keyof ED, OP extends SelectOption>(
params: {

View File

@ -53,61 +53,69 @@ export async function operate<
);
}
function pruneAggrResult<
ED extends BaseEntityDict & EntityDict,
T extends keyof ED>(schema: StorageSchema<ED>, entity: T, result: Partial<ED[T]['Schema']>[]) {
const pruneInner = (e: keyof ED, r: Partial<ED[keyof ED]['Schema']>): Partial<ED[keyof ED]['Schema']> | undefined => {
const r2 = {};
let hasAggr = false;
/**
* cascadeSelectbuild返回的结构树id/total/aggr信息
* @param schema
* @param entity
* @param result
* @returns
*/
function buildResultTree<ED extends BaseEntityDict & EntityDict, T extends keyof ED>(
schema: StorageSchema<ED>,
entity: T,
result: Partial<ED[T]['Schema']>[]
) {
const pruneInner = (e: keyof ED, r: Partial<ED[keyof ED]['Schema']>, tree: Record<string, any>) => {
for (const attr in r) {
if (attr.endsWith('$$aggr')) {
hasAggr = true;
Object.assign(r2, {
[attr]: r[attr],
});
tree[attr] = r[attr];
}
else if (typeof r[attr] === 'object') {
const rel = judgeRelation(schema, e, attr);
if (rel === 2 || typeof rel === 'string') {
const rr = pruneInner(rel === 2 ? attr : rel, r[attr]!);
if (rr) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
}
tree[attr] = {};
pruneInner(rel === 2 ? attr : rel, r[attr]!, tree[attr]);
}
else if (rel instanceof Array) {
assert(r[attr] as any instanceof Array);
const rr = r[attr].map(
(ele: any) => pruneInner(rel[0], ele)
);
if (rr.find(
(ele: any) => !ele
)) {
hasAggr = true;
Object.assign(r2, {
[attr]: rr,
});
tree[attr] = {
data: {},
};
if (r[attr].hasOwnProperty('#total')) {
tree[attr].total = r[attr]['#total'];
}
r[attr].forEach(
(rr: any) => {
tree[attr].data[rr.id as string] = {};
pruneInner(rel[0], rr, tree[attr].data[rr.id as string]);
}
)
}
}
}
if (hasAggr) {
return r2;
}
return;
};
const result2 = result.map(
(row) => pruneInner(entity, row)
);
if (result2.find(
ele => !!ele
)) {
return result2;
const root = {
data: {},
} as {
data: Record<string, any>;
total?: number;
}
/**
* total是在cascadeStore的selectAsync处理的
*/
if (result.hasOwnProperty('#total')) {
root.total = (result as any)['#total'];
}
result.forEach(
(row) => {
root.data[row.id!] = {};
pruneInner(entity, row, root.data[row.id!]);
}
);
return root;
}
export async function select<
@ -125,77 +133,16 @@ export async function select<
},
context: Cxt
) {
const { entity, selection, option, getCount, maxCount } = params;
const { randomRange, count } = selection;
const { entity, selection, option } = params;
let selection2 = selection;
if (randomRange) {
// 如果是随机取值这里就从randomRange的ids中随机取
const idSelection = Object.assign({}, selection, {
indexFrom: 0,
count: randomRange,
data: {
id: 1,
},
});
const idResults = await context.select(
entity,
idSelection,
option || {}
);
const possibility = count! / idResults.length;
let reduced = idResults.length - count!;
const ids2 = idResults.map(ele => ele.id).filter(
(id) => {
const rand = Math.random();
if (rand > possibility && reduced) {
reduced --;
return false;
}
return true;
}
);
selection2.filter = {
id: {
$in: ids2,
},
};
}
const data = await context.select(
entity,
selection2,
option || {}
);
const result = {
ids: data.map(ele => ele.id),
} as {
ids: string[];
count?: number;
aggr?: (Partial<ED[T]['Schema']> | undefined)[];
};
if (getCount && !randomRange) {
const { filter } = selection;
const count = await context.count(
entity,
Object.assign({}, { filter, count: maxCount || 1000 }),
option || {}
);
Object.assign(result, {
count,
});
}
if (data.length === 0) {
}
else {
const aggrData = pruneAggrResult(context.getSchema(), entity, data);
if (aggrData) {
result.aggr = aggrData;
}
}
return result;
return buildResultTree(context.getSchema(), entity, data);
}
export async function aggregate<

View File

@ -9,8 +9,8 @@ const Importations: Record<string, any> = {};
const Exportations: Record<string, any> = {};
export function registerPorts<ED extends EntityDict>(
importations: Importation<ED, keyof ED, any>[],
exportations: Exportation<ED, keyof ED, any>[]
importations: Importation<ED, keyof ED, any, any>[],
exportations: Exportation<ED, keyof ED, any, any>[]
) {
for (const i of importations) {
assert(
@ -44,12 +44,12 @@ export function clearPorts() {
function getImportation<ED extends EntityDict, T extends keyof ED>(id: string) {
assert(Importations.hasOwnProperty(id), `id为[${id}]的importation不存在`);
return Importations[id] as Importation<ED, T, any>;
return Importations[id] as Importation<ED, T, any, any>;
}
function getExportation<ED extends EntityDict, T extends keyof ED>(id: string) {
assert(Exportations.hasOwnProperty(id), `id为[${id}]的exportation不存在`);
return Exportations[id] as Exportation<ED, T, any>;
return Exportations[id] as Exportation<ED, T, any, any>;
}
export async function importEntity<