@an5/orm 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -83
- package/dist/an5Metadata.d.ts +10 -0
- package/dist/an5Metadata.js +93 -0
- package/dist/an5Orm.d.ts +95 -3
- package/dist/an5Orm.js +808 -512
- package/dist/config.d.ts +2 -0
- package/dist/config.js +24 -0
- package/dist/errors.d.ts +14 -0
- package/dist/errors.js +16 -0
- package/dist/generator/src/code-generator.d.ts +1 -0
- package/dist/generator/src/code-generator.js +44 -20
- package/dist/generator/src/dotnet-generator.d.ts +2 -0
- package/dist/generator/src/dotnet-generator.js +332 -6
- package/dist/generator/src/index.js +18 -2
- package/dist/generator/src/metadata-generator.d.ts +4 -1
- package/dist/generator/src/metadata-generator.js +22 -8
- package/dist/generator/src/python-generator.d.ts +10 -0
- package/dist/generator/src/python-generator.js +242 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +23 -8
- package/dist/logger.d.ts +53 -0
- package/dist/logger.js +117 -0
- package/dist/metadata.d.ts +24 -0
- package/dist/metadata.js +15 -0
- package/dist/migration-core.d.ts +87 -0
- package/dist/migration-core.js +692 -0
- package/dist/sql-utils.d.ts +31 -0
- package/dist/sql-utils.js +290 -0
- package/generator/src/code-generator.ts +45 -20
- package/generator/src/dotnet-generator.ts +344 -6
- package/generator/src/golang-generator.ts +1242 -0
- package/generator/src/index.ts +8 -1
- package/generator/src/metadata-generator.ts +29 -12
- package/generator/src/python-generator.ts +314 -4
- package/package.json +12 -10
- package/dist/cleanup.d.ts +0 -2
- package/dist/cleanup.js +0 -120
- package/dist/cli.js +0 -64
- package/dist/migrate.d.ts +0 -11
- package/dist/migrate.js +0 -301
- package/dist/pull.d.ts +0 -2
- package/dist/pull.js +0 -339
- package/dist/push.d.ts +0 -2
- package/dist/push.js +0 -281
- package/dist/seed.d.ts +0 -2
- package/dist/seed.js +0 -401
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.config = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Workspace Configuration for an5Orm
|
|
9
|
+
* Reads from an5Orm.config.js and environment variables.
|
|
10
|
+
*/
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
let workspaceConfig = {};
|
|
13
|
+
try {
|
|
14
|
+
const configPath = path_1.default.join(__dirname, '..', 'an5Orm.config.js');
|
|
15
|
+
workspaceConfig = require(configPath);
|
|
16
|
+
}
|
|
17
|
+
catch { /* ignore */ }
|
|
18
|
+
exports.config = {
|
|
19
|
+
...workspaceConfig,
|
|
20
|
+
llm: workspaceConfig.llm || {
|
|
21
|
+
providers: [],
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=config.js.map
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core error types for AN5 ORM.
|
|
3
|
+
* Owned by @an5/orm so generated clients can re-export the same class and
|
|
4
|
+
* `instanceof` checks keep working across the ORM and its clients.
|
|
5
|
+
*/
|
|
6
|
+
export declare class An5ClientKnownRequestError extends Error {
|
|
7
|
+
code: string;
|
|
8
|
+
meta?: any;
|
|
9
|
+
constructor(message: string, { code }: {
|
|
10
|
+
code: string;
|
|
11
|
+
clientVersion: string;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=errors.d.ts.map
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core error types for AN5 ORM.
|
|
4
|
+
* Owned by @an5/orm so generated clients can re-export the same class and
|
|
5
|
+
* `instanceof` checks keep working across the ORM and its clients.
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.An5ClientKnownRequestError = void 0;
|
|
9
|
+
class An5ClientKnownRequestError extends Error {
|
|
10
|
+
constructor(message, { code }) {
|
|
11
|
+
super(message);
|
|
12
|
+
this.code = code;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.An5ClientKnownRequestError = An5ClientKnownRequestError;
|
|
16
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -22,15 +22,11 @@ class CodeGenerator {
|
|
|
22
22
|
}
|
|
23
23
|
generateBaseTs() {
|
|
24
24
|
const baseTs = `
|
|
25
|
+
import { An5ClientKnownRequestError as An5KnownError } from "@an5/orm";
|
|
26
|
+
|
|
25
27
|
export namespace An5 {
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
meta?: any;
|
|
29
|
-
constructor(message: string, { code, clientVersion }: { code: string; clientVersion: string }) {
|
|
30
|
-
super(message);
|
|
31
|
-
this.code = code;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
28
|
+
export const An5ClientKnownRequestError: typeof An5KnownError = An5KnownError;
|
|
29
|
+
export type An5ClientKnownRequestError = An5KnownError;
|
|
34
30
|
export type SortOrder = 'asc' | 'desc';
|
|
35
31
|
export type StringFilter = { equals?: string; in?: string[]; notIn?: string[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringFilter; };
|
|
36
32
|
export type StringNullableFilter = { equals?: string | null; in?: (string | null)[]; notIn?: (string | null)[]; lt?: string; lte?: string; gt?: string; gte?: string; contains?: string; startsWith?: string; endsWith?: string; not?: string | StringNullableFilter | null; };
|
|
@@ -40,11 +36,11 @@ export namespace An5 {
|
|
|
40
36
|
export type NumberNullableFilter = { equals?: number | null; in?: (number | null)[]; notIn?: (number | null)[]; lt?: number; lte?: number; gt?: number; gte?: number; not?: number | NumberNullableFilter | null; };
|
|
41
37
|
export type BooleanFilter = { equals?: boolean; not?: boolean | BooleanFilter; };
|
|
42
38
|
export type BooleanNullableFilter = { equals?: boolean | null; not?: boolean | BooleanNullableFilter | null; };
|
|
43
|
-
export type DateTimeFilter = { equals?: Date; in?: Date[]; notIn?: Date[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeFilter; };
|
|
44
|
-
export type DateTimeNullableFilter = { equals?: Date | null; in?: (Date | null)[]; notIn?: (Date | null)[]; lt?: Date; lte?: Date; gt?: Date; gte?: Date; not?: Date | DateTimeNullableFilter | null; };
|
|
39
|
+
export type DateTimeFilter = { equals?: Date | string; in?: (Date | string)[]; notIn?: (Date | string)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeFilter; };
|
|
40
|
+
export type DateTimeNullableFilter = { equals?: Date | string | null; in?: ((Date | string) | null)[]; notIn?: ((Date | string) | null)[]; lt?: Date | string; lte?: Date | string; gt?: Date | string; gte?: Date | string; not?: Date | string | DateTimeNullableFilter | null; };
|
|
45
41
|
}
|
|
46
42
|
|
|
47
|
-
export interface TableClient<T, WhereInput = any,
|
|
43
|
+
export interface TableClient<T, WhereInput = any, _Select = any, Include = any, CreateInput = any, UpdateInput = any, FindManyArgs = any, FindFirstArgs = any, FindUniqueArgs = any, CreateArgs = any, UpdateArgs = any, UpsertArgs = any, DeleteArgs = any, AggregateArgs = any, GroupByArgs = any> {
|
|
48
44
|
findMany(args?: FindManyArgs): Promise<T[]>;
|
|
49
45
|
vectorSearch(args: { vector: number[]; take?: number; where?: WhereInput; include?: Include; vectorField?: string; distanceMetric?: 'cosine' | 'euclidean' | 'dot'; }): Promise<(T & { distance: number })[]>;
|
|
50
46
|
findFirst(args?: FindFirstArgs): Promise<T | null>;
|
|
@@ -56,8 +52,8 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
56
52
|
updateMany(args: { where?: WhereInput; data: UpdateInput; }): Promise<{ count: number }>;
|
|
57
53
|
delete(args: DeleteArgs): Promise<T>;
|
|
58
54
|
deleteMany(args?: { where?: WhereInput; }): Promise<{ count: number }>;
|
|
59
|
-
aggregate(args:
|
|
60
|
-
groupBy(args:
|
|
55
|
+
aggregate(args: AggregateArgs): Promise<any>;
|
|
56
|
+
groupBy(args: GroupByArgs): Promise<any[]>;
|
|
61
57
|
upsert(args: UpsertArgs): Promise<T>;
|
|
62
58
|
}
|
|
63
59
|
`;
|
|
@@ -88,7 +84,7 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
88
84
|
content += ` NOT?: ${model.name}WhereInput | ${model.name}WhereInput[];\n`;
|
|
89
85
|
for (const field of model.fields) {
|
|
90
86
|
const filter = this.getFieldFilterType(field);
|
|
91
|
-
content += ` ${field.name}?: ${field.type} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
|
|
87
|
+
content += ` ${field.name}?: ${this.inputType(field.type)} | ${filter}${field.isOptional ? ' | null' : ''};\n`;
|
|
92
88
|
}
|
|
93
89
|
for (const rel of model.relations) {
|
|
94
90
|
if (rel.isArray) {
|
|
@@ -116,16 +112,23 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
116
112
|
includeRels.push(`_count?: boolean | { select?: { ${model.relations.map(r => `${r.name}?: boolean`).join('; ')} } }`);
|
|
117
113
|
}
|
|
118
114
|
content += `export type ${model.name}Include = { ${includeRels.join('; ')}${includeRels.length > 0 ? ';' : ''} };\n`;
|
|
119
|
-
content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
|
|
120
|
-
content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : f.type}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
|
|
121
|
-
content += `export type ${model.name}
|
|
122
|
-
content += `export type ${model.name}
|
|
115
|
+
content += `export type ${model.name}CreateInput = { ${model.fields.map(f => `${f.name}${f.isOptional || f.hasDefault ? '?' : ''}: ${this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; }`).join('; ')} };\n`;
|
|
116
|
+
content += `export type ${model.name}UpdateInput = { ${model.fields.filter(f => !f.isId).map(f => `${f.name}?: ${f.type === 'number' ? 'number | An5.IntFieldUpdateOperationsInput' : this.inputType(f.type)}${f.isOptional ? ' | null' : ''}`).join('; ')}; ${model.relations.map(r => `${r.name}?: { create?: ${r.type}CreateInput | ${r.type}CreateInput[]; connect?: ${r.type}WhereInput | ${r.type}WhereInput[]; set?: ${r.type}WhereInput | ${r.type}WhereInput[]; disconnect?: ${r.type}WhereInput | ${r.type}WhereInput[]; delete?: ${r.type}WhereInput | ${r.type}WhereInput[]; update?: { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; data: ${r.type}UpdateInput; }[]; upsert?: { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; } | { where: ${r.type}WhereInput; create: ${r.type}CreateInput; update: ${r.type}UpdateInput; }[]; }`).join('; ')} };\n`;
|
|
117
|
+
content += `export type ${model.name}OrderByInput = { ${model.fields.map(f => `${f.name}?: An5.SortOrder`).join('; ')} };\n`;
|
|
118
|
+
content += `export type ${model.name}FindManyArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; take?: number; skip?: number; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
119
|
+
content += `export type ${model.name}FindFirstArgs = { where?: ${model.name}WhereInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
123
120
|
content += `export type ${model.name}FindUniqueArgs = { where?: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
124
121
|
content += `export type ${model.name}CreateArgs = { data: ${model.name}CreateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
125
122
|
content += `export type ${model.name}UpdateArgs = { where: ${model.name}WhereInput; data: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
126
123
|
content += `export type ${model.name}UpsertArgs = { where: ${model.name}WhereInput; create: ${model.name}CreateInput; update: ${model.name}UpdateInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
127
124
|
content += `export type ${model.name}DeleteArgs = { where: ${model.name}WhereInput; include?: ${model.name}Include; select?: ${model.name}Select; };\n`;
|
|
128
|
-
content += `export type ${model.name}
|
|
125
|
+
content += `export type ${model.name}ScalarFieldEnum = ${model.fields.map(f => `'${f.name}'`).join(' | ') || 'never'};\n`;
|
|
126
|
+
content += `export type ${model.name}AggregateArgs = { where?: ${model.name}WhereInput; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
|
|
127
|
+
const numericHavingFields = model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: An5.NumberFilter | number`).join('; ');
|
|
128
|
+
const scalarHavingFields = model.fields.map(f => `${f.name}?: any`).join('; ');
|
|
129
|
+
content += `export type ${model.name}AggregateHavingInput = { _count?: { _all?: An5.NumberFilter | number; ${model.fields.map(f => `${f.name}?: An5.NumberFilter | number`).join('; ')} }; _sum?: { ${numericHavingFields} }; _avg?: { ${numericHavingFields} }; _min?: { ${scalarHavingFields} }; _max?: { ${scalarHavingFields} }; };\n`;
|
|
130
|
+
content += `export type ${model.name}GroupByArgs = { by: ${model.name}ScalarFieldEnum | ${model.name}ScalarFieldEnum[]; where?: ${model.name}WhereInput; having?: ${model.name}AggregateHavingInput; orderBy?: ${model.name}OrderByInput | ${model.name}OrderByInput[]; skip?: number; take?: number; _count?: true | { _all?: true; ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _sum?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _avg?: { ${model.fields.filter(f => this.normalizeType(f.type) === 'number').map(f => `${f.name}?: true`).join('; ')} }; _min?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; _max?: { ${model.fields.map(f => `${f.name}?: true`).join('; ')} }; };\n`;
|
|
131
|
+
content += `export type ${model.name}TableClient = TableClient<\n ${model.name},\n ${model.name}WhereInput,\n ${model.name}Select,\n ${model.name}Include,\n ${model.name}CreateInput,\n ${model.name}UpdateInput,\n ${model.name}FindManyArgs,\n ${model.name}FindFirstArgs,\n ${model.name}FindUniqueArgs,\n ${model.name}CreateArgs,\n ${model.name}UpdateArgs,\n ${model.name}UpsertArgs,\n ${model.name}DeleteArgs,\n ${model.name}AggregateArgs,\n ${model.name}GroupByArgs\n>;\n`;
|
|
129
132
|
fs_1.default.writeFileSync(path_1.default.join(this.outputDir, `${model.name}.ts`), content);
|
|
130
133
|
}
|
|
131
134
|
generateIndexTs(models) {
|
|
@@ -157,9 +160,21 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
157
160
|
content += ` export type ${model.name}Include = ${model.name}Types.${model.name}Include;\n`;
|
|
158
161
|
content += ` export type ${model.name}CreateInput = ${model.name}Types.${model.name}CreateInput;\n`;
|
|
159
162
|
content += ` export type ${model.name}UpdateInput = ${model.name}Types.${model.name}UpdateInput;\n`;
|
|
163
|
+
content += ` export type ${model.name}OrderByInput = ${model.name}Types.${model.name}OrderByInput;\n`;
|
|
164
|
+
content += ` export type ${model.name}FindManyArgs = ${model.name}Types.${model.name}FindManyArgs;\n`;
|
|
165
|
+
content += ` export type ${model.name}FindFirstArgs = ${model.name}Types.${model.name}FindFirstArgs;\n`;
|
|
166
|
+
content += ` export type ${model.name}FindUniqueArgs = ${model.name}Types.${model.name}FindUniqueArgs;\n`;
|
|
167
|
+
content += ` export type ${model.name}CreateArgs = ${model.name}Types.${model.name}CreateArgs;\n`;
|
|
168
|
+
content += ` export type ${model.name}UpdateArgs = ${model.name}Types.${model.name}UpdateArgs;\n`;
|
|
169
|
+
content += ` export type ${model.name}UpsertArgs = ${model.name}Types.${model.name}UpsertArgs;\n`;
|
|
170
|
+
content += ` export type ${model.name}DeleteArgs = ${model.name}Types.${model.name}DeleteArgs;\n`;
|
|
171
|
+
content += ` export type ${model.name}TableClient = ${model.name}Types.${model.name}TableClient;\n`;
|
|
172
|
+
content += ` export type ${model.name}ScalarFieldEnum = ${model.name}Types.${model.name}ScalarFieldEnum;\n`;
|
|
173
|
+
content += ` export type ${model.name}AggregateArgs = ${model.name}Types.${model.name}AggregateArgs;\n`;
|
|
174
|
+
content += ` export type ${model.name}GroupByArgs = ${model.name}Types.${model.name}GroupByArgs;\n`;
|
|
160
175
|
}
|
|
161
176
|
content += `}\n`;
|
|
162
|
-
content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any,
|
|
177
|
+
content += `\nexport class An5Client {\n $connect(): Promise<void> { return Promise.resolve(); }\n $disconnect(): Promise<void> { return Promise.resolve(); }\n $transaction<R>(fn: (tx: An5Client) => Promise<R>, options?: { timeout?: number }): Promise<R>;\n $transaction<R>(list: Promise<R>[]): Promise<R[]>;\n $transaction(fn: any, _options?: any): Promise<any> { return typeof fn === 'function' ? fn(this) : Promise.all(fn); }\n $begin(): Promise<An5Client> { return Promise.resolve(this); }\n $commit(): Promise<void> { return Promise.resolve(); }\n $rollback(): Promise<void> { return Promise.resolve(); }\n $queryRaw<T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<T> { return Promise.resolve([] as any); }\n $queryRawUnsafe<R = any>(_query: string, ..._values: any[]): Promise<R> { return Promise.resolve([] as any); }\n $executeRaw<_T = any>(_queryParts: TemplateStringsArray | string, ..._values: any[]): Promise<any> { return Promise.resolve(0 as any); }\n $executeRawUnsafe(_query: string, ..._values: any[]): Promise<number> { return Promise.resolve(0); }\n`;
|
|
163
178
|
const addedProps = new Set();
|
|
164
179
|
for (const model of models) {
|
|
165
180
|
const props = this.getAllPropertyVariations(model.name);
|
|
@@ -175,12 +190,16 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
175
190
|
}
|
|
176
191
|
getAllPropertyVariations(modelName) {
|
|
177
192
|
const variations = new Set();
|
|
193
|
+
variations.add(modelName);
|
|
178
194
|
variations.add(this.toCamelCase(modelName));
|
|
195
|
+
variations.add(modelName + 's');
|
|
196
|
+
variations.add(this.toCamelCase(modelName) + 's');
|
|
179
197
|
const acronyms = ['LLM', 'AI', 'MCP', 'IT', 'QC', 'HR', 'MR', 'WH', 'SSIS', 'API', 'URL', 'ID', 'JSON'];
|
|
180
198
|
for (const acronym of acronyms) {
|
|
181
199
|
if (modelName.startsWith(acronym)) {
|
|
182
200
|
variations.add(acronym.toLowerCase() + modelName.slice(acronym.length));
|
|
183
201
|
variations.add(acronym[0].toLowerCase() + acronym.slice(1) + modelName.slice(acronym.length));
|
|
202
|
+
variations.add(acronym.toLowerCase() + modelName.slice(acronym.length) + 's');
|
|
184
203
|
}
|
|
185
204
|
}
|
|
186
205
|
return Array.from(variations);
|
|
@@ -197,6 +216,11 @@ export interface TableClient<T, WhereInput = any, Select = any, Include = any, C
|
|
|
197
216
|
return field.isOptional ? 'An5.DateTimeNullableFilter' : 'An5.DateTimeFilter';
|
|
198
217
|
return 'any';
|
|
199
218
|
}
|
|
219
|
+
// Input positions (create/update/where scalars) accept ISO strings as well
|
|
220
|
+
// as Date objects — Prisma parity for DateTime fields.
|
|
221
|
+
inputType(tsType) {
|
|
222
|
+
return tsType === 'Date' ? 'Date | string' : tsType;
|
|
223
|
+
}
|
|
200
224
|
normalizeType(type) {
|
|
201
225
|
const lowerType = type.toLowerCase();
|
|
202
226
|
if (['string', 'varchar', 'nvarchar', 'char', 'nchar', 'text', 'ntext', 'xml', 'guid', 'uuid', 'uniqueidentifier', 'sysname', 'hierarchyid', 'geography', 'geometry', 'sql_variant', 'sqlvariant', 'rowversion', 'variant'].includes(lowerType)) {
|
|
@@ -18,11 +18,173 @@ class DotnetGenerator {
|
|
|
18
18
|
for (const model of models) {
|
|
19
19
|
this.generateEntityClass(model);
|
|
20
20
|
}
|
|
21
|
-
// 2. Generate
|
|
21
|
+
// 2. Generate per-model ORM types (WhereInput, OrderByInput, FindManyArgs, etc.)
|
|
22
|
+
this.generateOrmTypes(models);
|
|
23
|
+
// 3. Generate DbContext / Client metadata file
|
|
22
24
|
this.generateDbContext(models);
|
|
23
|
-
//
|
|
25
|
+
// 4. Generate configuration helper class
|
|
24
26
|
this.generateConfigClass();
|
|
25
27
|
}
|
|
28
|
+
getCsFilterType(fieldType, isOptional) {
|
|
29
|
+
const lower = fieldType.toLowerCase();
|
|
30
|
+
if (['datetime', 'datetime2', 'smalldatetime', 'date', 'datetimeoffset'].includes(lower))
|
|
31
|
+
return 'DateTimeFilter';
|
|
32
|
+
if (['bool', 'boolean', 'bit'].includes(lower))
|
|
33
|
+
return 'BoolFilter';
|
|
34
|
+
if (['int', 'integer', 'smallint', 'tinyint', 'number'].includes(lower))
|
|
35
|
+
return 'IntFilter';
|
|
36
|
+
if (['bigint', 'long', 'float', 'real', 'double', 'decimal', 'numeric', 'money', 'smallmoney'].includes(lower))
|
|
37
|
+
return 'NumberFilter';
|
|
38
|
+
return 'StringFilter';
|
|
39
|
+
}
|
|
40
|
+
generateOrmTypes(models) {
|
|
41
|
+
let content = `// This file is auto-generated. Do not edit directly.
|
|
42
|
+
using System;
|
|
43
|
+
using System.Collections.Generic;
|
|
44
|
+
|
|
45
|
+
namespace An5Orm
|
|
46
|
+
{
|
|
47
|
+
// ── Base Filter Types ──────────────────────────────────────────────────────
|
|
48
|
+
|
|
49
|
+
/// <summary>Type-safe filter for string fields.</summary>
|
|
50
|
+
public class StringFilter
|
|
51
|
+
{
|
|
52
|
+
public new string Equals { get; set; }
|
|
53
|
+
public string Not { get; set; }
|
|
54
|
+
public List<string> In { get; set; }
|
|
55
|
+
public List<string> NotIn { get; set; }
|
|
56
|
+
public string Contains { get; set; }
|
|
57
|
+
public string StartsWith { get; set; }
|
|
58
|
+
public string EndsWith { get; set; }
|
|
59
|
+
public string Gt { get; set; }
|
|
60
|
+
public string Gte { get; set; }
|
|
61
|
+
public string Lt { get; set; }
|
|
62
|
+
public string Lte { get; set; }
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/// <summary>Type-safe filter for integer fields.</summary>
|
|
66
|
+
public class IntFilter
|
|
67
|
+
{
|
|
68
|
+
public new int? Equals { get; set; }
|
|
69
|
+
public int? Not { get; set; }
|
|
70
|
+
public List<int> In { get; set; }
|
|
71
|
+
public List<int> NotIn { get; set; }
|
|
72
|
+
public int? Gt { get; set; }
|
|
73
|
+
public int? Gte { get; set; }
|
|
74
|
+
public int? Lt { get; set; }
|
|
75
|
+
public int? Lte { get; set; }
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// <summary>Type-safe filter for numeric/float fields.</summary>
|
|
79
|
+
public class NumberFilter
|
|
80
|
+
{
|
|
81
|
+
public new double? Equals { get; set; }
|
|
82
|
+
public double? Not { get; set; }
|
|
83
|
+
public List<double> In { get; set; }
|
|
84
|
+
public List<double> NotIn { get; set; }
|
|
85
|
+
public double? Gt { get; set; }
|
|
86
|
+
public double? Gte { get; set; }
|
|
87
|
+
public double? Lt { get; set; }
|
|
88
|
+
public double? Lte { get; set; }
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// <summary>Type-safe filter for boolean fields.</summary>
|
|
92
|
+
public class BoolFilter
|
|
93
|
+
{
|
|
94
|
+
public new bool? Equals { get; set; }
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// <summary>Type-safe filter for DateTime fields.</summary>
|
|
98
|
+
public class DateTimeFilter
|
|
99
|
+
{
|
|
100
|
+
public new DateTime? Equals { get; set; }
|
|
101
|
+
public DateTime? Not { get; set; }
|
|
102
|
+
public List<DateTime> In { get; set; }
|
|
103
|
+
public List<DateTime> NotIn { get; set; }
|
|
104
|
+
public DateTime? Gt { get; set; }
|
|
105
|
+
public DateTime? Gte { get; set; }
|
|
106
|
+
public DateTime? Lt { get; set; }
|
|
107
|
+
public DateTime? Lte { get; set; }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
`;
|
|
111
|
+
for (const model of models) {
|
|
112
|
+
const name = this.capitalize(model.name);
|
|
113
|
+
const csType = (f) => this.mapType(f.type, f.isOptional);
|
|
114
|
+
content += ` // ── ${name} ORM Types ──────────────────────────────────────────────────────\n\n`;
|
|
115
|
+
// WhereInput
|
|
116
|
+
content += ` /// <summary>Type-safe WHERE filter for ${name} queries.</summary>\n`;
|
|
117
|
+
content += ` public class ${name}WhereInput\n {\n`;
|
|
118
|
+
content += ` public List<${name}WhereInput> AND { get; set; }\n`;
|
|
119
|
+
content += ` public List<${name}WhereInput> OR { get; set; }\n`;
|
|
120
|
+
content += ` public ${name}WhereInput NOT { get; set; }\n`;
|
|
121
|
+
for (const f of model.fields) {
|
|
122
|
+
const ft = this.getCsFilterType(f.type, f.isOptional);
|
|
123
|
+
content += ` public ${ft} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
124
|
+
}
|
|
125
|
+
content += ` }\n\n`;
|
|
126
|
+
// OrderByInput
|
|
127
|
+
content += ` /// <summary>Type-safe ORDER BY for ${name} queries.</summary>\n`;
|
|
128
|
+
content += ` public class ${name}OrderByInput\n {\n`;
|
|
129
|
+
for (const f of model.fields) {
|
|
130
|
+
content += ` public string ${this.capitalize(f.name)} { get; set; } // "asc" or "desc"\n`;
|
|
131
|
+
}
|
|
132
|
+
content += ` }\n\n`;
|
|
133
|
+
// CreateInput
|
|
134
|
+
content += ` /// <summary>Typed data for creating a new ${name} record.</summary>\n`;
|
|
135
|
+
content += ` public class ${name}CreateInput\n {\n`;
|
|
136
|
+
for (const f of model.fields) {
|
|
137
|
+
if (!f.isId) {
|
|
138
|
+
const ct = this.mapType(f.type, f.isOptional || f.hasDefault);
|
|
139
|
+
content += ` public ${ct} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
content += ` }\n\n`;
|
|
143
|
+
// UpdateInput
|
|
144
|
+
content += ` /// <summary>Typed data for updating an existing ${name} record.</summary>\n`;
|
|
145
|
+
content += ` public class ${name}UpdateInput\n {\n`;
|
|
146
|
+
for (const f of model.fields) {
|
|
147
|
+
if (!f.isId) {
|
|
148
|
+
const ut = this.mapType(f.type, true); // always nullable for update
|
|
149
|
+
content += ` public ${ut} ${this.capitalize(f.name)} { get; set; }\n`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
content += ` }\n\n`;
|
|
153
|
+
// FindManyArgs
|
|
154
|
+
content += ` /// <summary>ORM-style args for ${name}.FindMany().</summary>\n`;
|
|
155
|
+
content += ` public class ${name}FindManyArgs\n {\n`;
|
|
156
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
157
|
+
content += ` public ${name}OrderByInput OrderBy { get; set; }\n`;
|
|
158
|
+
content += ` public int? Take { get; set; }\n`;
|
|
159
|
+
content += ` public int? Skip { get; set; }\n`;
|
|
160
|
+
content += ` public List<string> Select { get; set; }\n`;
|
|
161
|
+
content += ` }\n\n`;
|
|
162
|
+
// FindFirstArgs
|
|
163
|
+
content += ` /// <summary>ORM-style args for ${name}.FindFirst().</summary>\n`;
|
|
164
|
+
content += ` public class ${name}FindFirstArgs\n {\n`;
|
|
165
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
166
|
+
content += ` public ${name}OrderByInput OrderBy { get; set; }\n`;
|
|
167
|
+
content += ` public List<string> Select { get; set; }\n`;
|
|
168
|
+
content += ` }\n\n`;
|
|
169
|
+
// FindUniqueArgs
|
|
170
|
+
content += ` /// <summary>ORM-style args for ${name}.FindUnique().</summary>\n`;
|
|
171
|
+
content += ` public class ${name}FindUniqueArgs\n {\n`;
|
|
172
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
173
|
+
content += ` }\n\n`;
|
|
174
|
+
// DeleteManyArgs
|
|
175
|
+
content += ` /// <summary>ORM-style args for ${name}.DeleteMany().</summary>\n`;
|
|
176
|
+
content += ` public class ${name}DeleteManyArgs\n {\n`;
|
|
177
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
178
|
+
content += ` }\n\n`;
|
|
179
|
+
// CountArgs
|
|
180
|
+
content += ` /// <summary>ORM-style args for ${name}.Count().</summary>\n`;
|
|
181
|
+
content += ` public class ${name}CountArgs\n {\n`;
|
|
182
|
+
content += ` public ${name}WhereInput Where { get; set; }\n`;
|
|
183
|
+
content += ` }\n\n`;
|
|
184
|
+
}
|
|
185
|
+
content += `}\n`;
|
|
186
|
+
fs_1.default.writeFileSync(path_1.default.join(this.outputDir, 'An5OrmTypes.cs'), content);
|
|
187
|
+
}
|
|
26
188
|
generateConfigClass() {
|
|
27
189
|
const content = `// This file is auto-generated. Do not edit directly.
|
|
28
190
|
using System;
|
|
@@ -119,6 +281,7 @@ using System;
|
|
|
119
281
|
using System.Collections.Generic;
|
|
120
282
|
using System.Data;
|
|
121
283
|
using System.Reflection;
|
|
284
|
+
using System.Text.Json;
|
|
122
285
|
using Microsoft.Data.SqlClient;
|
|
123
286
|
using An5Orm.Entities;
|
|
124
287
|
|
|
@@ -169,7 +332,9 @@ namespace An5Orm
|
|
|
169
332
|
// ── Tables / Repositories ──────────────────────────────────────────────
|
|
170
333
|
`;
|
|
171
334
|
for (const model of models) {
|
|
172
|
-
|
|
335
|
+
const name = this.capitalize(model.name);
|
|
336
|
+
content += ` public TableClient<${model.name}> ${name}s => new TableClient<${model.name}>(ConnectionString, "${model.schemaName}.${model.tableName}");\n`;
|
|
337
|
+
content += ` public TableClient<${model.name}> ${name} => ${name}s;\n`;
|
|
173
338
|
}
|
|
174
339
|
content += ` }
|
|
175
340
|
|
|
@@ -233,6 +398,51 @@ namespace An5Orm
|
|
|
233
398
|
return cmd;
|
|
234
399
|
}
|
|
235
400
|
|
|
401
|
+
public List<T> QueryRaw(string query, Dictionary<string, object> parameters = null)
|
|
402
|
+
{
|
|
403
|
+
var list = new List<T>();
|
|
404
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
405
|
+
try
|
|
406
|
+
{
|
|
407
|
+
using (var cmd = CreateCommand(conn, query))
|
|
408
|
+
{
|
|
409
|
+
if (parameters != null)
|
|
410
|
+
{
|
|
411
|
+
foreach (var kvp in parameters)
|
|
412
|
+
{
|
|
413
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
using (var reader = cmd.ExecuteReader())
|
|
418
|
+
{
|
|
419
|
+
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
420
|
+
while (reader.Read())
|
|
421
|
+
{
|
|
422
|
+
var item = new T();
|
|
423
|
+
foreach (var prop in properties)
|
|
424
|
+
{
|
|
425
|
+
if (HasColumn(reader, prop.Name))
|
|
426
|
+
{
|
|
427
|
+
var val = reader[prop.Name];
|
|
428
|
+
if (val != DBNull.Value)
|
|
429
|
+
{
|
|
430
|
+
prop.SetValue(item, val);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
list.Add(item);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
finally
|
|
440
|
+
{
|
|
441
|
+
if (!isTx) conn.Dispose();
|
|
442
|
+
}
|
|
443
|
+
return list;
|
|
444
|
+
}
|
|
445
|
+
|
|
236
446
|
public List<T> FindMany(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
237
447
|
{
|
|
238
448
|
var list = new List<T>();
|
|
@@ -441,15 +651,132 @@ namespace An5Orm
|
|
|
441
651
|
}
|
|
442
652
|
}
|
|
443
653
|
|
|
654
|
+
public int Count(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
655
|
+
{
|
|
656
|
+
string query = $"SELECT COUNT(*) FROM {TableName}";
|
|
657
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
658
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
659
|
+
try
|
|
660
|
+
{
|
|
661
|
+
using (var cmd = CreateCommand(conn, query))
|
|
662
|
+
{
|
|
663
|
+
if (parameters != null)
|
|
664
|
+
{
|
|
665
|
+
foreach (var kvp in parameters)
|
|
666
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
667
|
+
}
|
|
668
|
+
var res = cmd.ExecuteScalar();
|
|
669
|
+
return res != null && res != DBNull.Value ? Convert.ToInt32(res) : 0;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
public int CreateMany(IEnumerable<T> entities)
|
|
676
|
+
{
|
|
677
|
+
int count = 0;
|
|
678
|
+
foreach (var entity in entities)
|
|
679
|
+
{
|
|
680
|
+
Create(entity);
|
|
681
|
+
count++;
|
|
682
|
+
}
|
|
683
|
+
return count;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
public int UpdateMany(string whereClause, Dictionary<string, object> updateData, Dictionary<string, object> parameters = null)
|
|
687
|
+
{
|
|
688
|
+
if (updateData == null || updateData.Count == 0) return 0;
|
|
689
|
+
var sets = new List<string>();
|
|
690
|
+
var sqlParams = new List<SqlParameter>();
|
|
691
|
+
int pIndex = 0;
|
|
692
|
+
foreach (var kvp in updateData)
|
|
693
|
+
{
|
|
694
|
+
string paramName = "@u_" + pIndex++;
|
|
695
|
+
sets.Add($"{kvp.Key} = {paramName}");
|
|
696
|
+
sqlParams.Add(new SqlParameter(paramName, kvp.Value ?? DBNull.Value));
|
|
697
|
+
}
|
|
698
|
+
string query = $"UPDATE {TableName} SET {string.Join(", ", sets)}";
|
|
699
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
700
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
701
|
+
try
|
|
702
|
+
{
|
|
703
|
+
using (var cmd = CreateCommand(conn, query))
|
|
704
|
+
{
|
|
705
|
+
cmd.Parameters.AddRange(sqlParams.ToArray());
|
|
706
|
+
if (parameters != null)
|
|
707
|
+
{
|
|
708
|
+
foreach (var kvp in parameters)
|
|
709
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
710
|
+
}
|
|
711
|
+
return cmd.ExecuteNonQuery();
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
public int DeleteMany(string whereClause = null, Dictionary<string, object> parameters = null)
|
|
718
|
+
{
|
|
719
|
+
string query = $"DELETE FROM {TableName}";
|
|
720
|
+
if (!string.IsNullOrEmpty(whereClause)) query += $" WHERE {whereClause}";
|
|
721
|
+
var conn = An5DbContext.GetActiveConnection(ConnectionString, out bool isTx);
|
|
722
|
+
try
|
|
723
|
+
{
|
|
724
|
+
using (var cmd = CreateCommand(conn, query))
|
|
725
|
+
{
|
|
726
|
+
if (parameters != null)
|
|
727
|
+
{
|
|
728
|
+
foreach (var kvp in parameters)
|
|
729
|
+
cmd.Parameters.AddWithValue(kvp.Key.StartsWith("@") ? kvp.Key : "@" + kvp.Key, kvp.Value ?? DBNull.Value);
|
|
730
|
+
}
|
|
731
|
+
return cmd.ExecuteNonQuery();
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
finally { if (!isTx) conn.Dispose(); }
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
public T Upsert(T entity, string idColumnName = "Id")
|
|
738
|
+
{
|
|
739
|
+
var prop = typeof(T).GetProperty(idColumnName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
740
|
+
if (prop == null) throw new InvalidOperationException($"Property '{idColumnName}' not found on entity.");
|
|
741
|
+
var idVal = prop.GetValue(entity);
|
|
742
|
+
var existing = idVal != null ? FindUnique(idVal) : default;
|
|
743
|
+
if (existing != null) return Update(entity);
|
|
744
|
+
return Create(entity);
|
|
745
|
+
}
|
|
746
|
+
|
|
444
747
|
public List<T> VectorSearch(List<double> vector, int take = 10, string whereClause = null, Dictionary<string, object> parameters = null, string vectorField = "Embedding", string distanceMetric = "cosine")
|
|
445
748
|
{
|
|
749
|
+
// 1. Primary path: Native database SQL vector query execution (VECTOR_DISTANCE)
|
|
750
|
+
try
|
|
751
|
+
{
|
|
752
|
+
var dim = vector.Count;
|
|
753
|
+
var vecJson = JsonSerializer.Serialize(vector);
|
|
754
|
+
var sql = $"SELECT TOP ({take}) *, VECTOR_DISTANCE('{distanceMetric}', CAST([{vectorField}] AS VECTOR({dim}, float32)), CAST(@query_vector AS VECTOR({dim}, float32))) AS distance FROM {TableName} WITH (NOLOCK)";
|
|
755
|
+
|
|
756
|
+
var p = parameters != null ? new Dictionary<string, object>(parameters) : new Dictionary<string, object>();
|
|
757
|
+
p["query_vector"] = vecJson;
|
|
758
|
+
|
|
759
|
+
if (!string.IsNullOrWhiteSpace(whereClause))
|
|
760
|
+
sql += $" WHERE [{vectorField}] IS NOT NULL AND ({whereClause})";
|
|
761
|
+
else
|
|
762
|
+
sql += $" WHERE [{vectorField}] IS NOT NULL";
|
|
763
|
+
sql += " ORDER BY distance ASC";
|
|
764
|
+
|
|
765
|
+
var nativeRows = QueryRaw(sql, p);
|
|
766
|
+
if (nativeRows != null) return nativeRows;
|
|
767
|
+
}
|
|
768
|
+
catch
|
|
769
|
+
{
|
|
770
|
+
// Fallback to in-memory similarity computation if DB instance lacks native VECTOR_DISTANCE
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// 2. Secondary fallback: In-memory similarity computation
|
|
446
774
|
var rows = FindMany(whereClause, parameters);
|
|
447
775
|
var results = new List<Tuple<T, double>>();
|
|
448
776
|
|
|
449
777
|
var propInfo = typeof(T).GetProperty(vectorField, BindingFlags.Public | BindingFlags.Instance);
|
|
450
778
|
if (propInfo == null)
|
|
451
779
|
{
|
|
452
|
-
// Fallback case-insensitive
|
|
453
780
|
propInfo = typeof(T).GetProperty(vectorField, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
454
781
|
}
|
|
455
782
|
if (propInfo == null) return rows;
|
|
@@ -479,12 +806,11 @@ namespace An5Orm
|
|
|
479
806
|
}
|
|
480
807
|
|
|
481
808
|
results.Sort((a, b) => a.Item2.CompareTo(b.Item2));
|
|
482
|
-
|
|
809
|
+
|
|
483
810
|
var output = new List<T>();
|
|
484
811
|
int limit = Math.Min(take, results.Count);
|
|
485
812
|
for (int i = 0; i < limit; i++)
|
|
486
813
|
{
|
|
487
|
-
// Set the distance property if the model has a public property named Distance
|
|
488
814
|
var distanceProp = typeof(T).GetProperty("Distance", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
489
815
|
if (distanceProp != null && distanceProp.PropertyType == typeof(double))
|
|
490
816
|
{
|