Merge pull request #96 from bknd-io/feat/report-query-without-index

added index on media entity_id, report query with unindexed fields
This commit is contained in:
dswbx
2025-02-25 08:17:54 +01:00
committed by GitHub
4 changed files with 26 additions and 8 deletions

View File

@@ -73,6 +73,7 @@ export abstract class Connection<DB = any> {
return false;
}
// @todo: add if only first field is used in index
supportsIndices(): boolean {
return false;
}

View File

@@ -8,6 +8,7 @@ import {
UnableToConnectException
} from "../errors";
import { MutatorEvents, RepositoryEvents } from "../events";
import type { Field } from "../fields/Field";
import type { EntityIndex } from "../fields/indices/EntityIndex";
import type { EntityRelation } from "../relations";
import { RelationAccessor } from "../relations/RelationAccessor";
@@ -142,6 +143,16 @@ export class EntityManager<TBD extends object = DefaultDB> {
return this.indices.some((e) => e.name === name);
}
// @todo: add to Connection whether first index is used or not
getIndexedFields(_entity: Entity | string): Field[] {
const entity = this.entity(_entity);
const indices = this.getIndicesOf(entity);
const rel_fields = entity.fields.filter((f) => f.type === "relation");
// assuming only first
const idx_fields = indices.map((index) => index.fields[0]);
return [entity.getPrimaryField(), ...rel_fields, ...idx_fields].filter(Boolean) as Field[];
}
addRelation(relation: EntityRelation) {
// check if entities are registered
if (!this.entity(relation.source.entity.name) || !this.entity(relation.target.entity.name)) {

View File

@@ -66,6 +66,13 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
return this.em.connection.kysely;
}
private checkIndex(entity: string, field: string, clause: string) {
const indexed = this.em.getIndexedFields(entity).map((f) => f.name);
if (!indexed.includes(field)) {
$console.warn(`Field "${entity}.${field}" used in "${clause}" is not indexed`);
}
}
getValidOptions(options?: Partial<RepoQuery>): RepoQuery {
const entity = this.entity;
// @todo: if not cloned deep, it will keep references and error if multiple requests come in
@@ -85,6 +92,7 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
throw new InvalidSearchParamsException(`Invalid sort direction "${options.sort.dir}"`);
}
this.checkIndex(entity.name, options.sort.by, "sort");
validated.sort = options.sort;
}
@@ -137,9 +145,11 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
return true;
}
this.checkIndex(alias, prop, "where");
return !this.em.entity(alias).getField(prop);
}
this.checkIndex(entity.name, field, "where");
return typeof entity.getField(field) === "undefined";
});
@@ -255,12 +265,6 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
...config?.defaults
};
/*$console.log("build query options", {
entity: entity.name,
options,
config
});*/
if (!ignore.includes("select") && options.select) {
qb = qb.select(entity.getAliasedSelectFrom(options.select, alias));
}
@@ -299,7 +303,9 @@ export class Repository<TBD extends object = DefaultDB, TB extends keyof TBD = a
return {
qb: this.addOptionsToQueryBuilder(undefined, options, {
ignore,
alias: entity.name
alias: entity.name,
// already done
validate: false
}),
options
};

View File

@@ -51,7 +51,7 @@ export class AppMedia extends Module<typeof mediaConfigSchema> {
const media = this.getMediaEntity(true);
this.ensureSchema(
em({ [media.name as "media"]: media }, ({ index }, { media }) => {
index(media).on(["path"], true).on(["reference"]);
index(media).on(["path"], true).on(["reference"]).on(["entity_id"]);
})
);
} catch (e) {