Skip to content

Commit 1526dc2

Browse files
committed
重构文档结构:移动 objectdocs.json 文件到 content 目录,并更新相关命令中的路径
1 parent 9adf88b commit 1526dc2

5 files changed

Lines changed: 162 additions & 4 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
---
2+
title: Advanced Types
3+
description: "Deep dive into Enterprise Data Types: Currency, Lookup, Formulas, and Summaries."
4+
---
5+
6+
# Advanced Enterprise Types
7+
8+
While basic types like `text` and `boolean` handle standard data, Enterprise applications require specialized types to handle money, relationships, and on-the-fly calculations.
9+
10+
ObjectQL provides these "Smart Types" out of the box. They are not just storage definitions; they carry **Logic** and **Compilation Rules** that the engine enforces automatically.
11+
12+
## 1. Currency (High-Precision Money)
13+
14+
In financial systems, floating-point math (e.g., `0.1 + 0.2`) is dangerous due to rounding errors. ObjectQL's `currency` type ensures mathematical exactness.
15+
16+
### Definition
17+
18+
```yaml
19+
fields:
20+
amount:
21+
type: currency
22+
label: Invoice Amount
23+
precision: 18 # Total digits
24+
scale: 2 # Decimal places
25+
required: true
26+
27+
```
28+
29+
### Protocol Behavior
30+
31+
* **Storage:** Compiles to `DECIMAL(18,2)` in SQL or `Decimal128` in MongoDB. It never uses `FLOAT` or `DOUBLE`.
32+
* **Runtime:** In the TypeScript runtime, values are treated as **Strings** or **BigInt** wrappers to prevent JavaScript's `number` type from introducing precision loss during API serialization.
33+
* **Formatting:** ObjectUI automatically renders this with the correct locale symbol (e.g., `$1,000.00`) based on the system configuration.
34+
35+
## 2. Lookup (Foreign Key Relationships)
36+
37+
The `lookup` type defines a Many-to-One relationship. Unlike simple SQL Foreign Keys, ObjectQL Lookups support **Polymorphism** and **Virtual Expansion**.
38+
39+
### Definition
40+
41+
```yaml
42+
fields:
43+
owner:
44+
type: lookup
45+
reference_to: users
46+
label: Project Owner
47+
index: true
48+
49+
```
50+
51+
### Advanced: Polymorphic Lookups
52+
53+
ObjectQL allows a field to point to *multiple* types of objects. This is essential for CRM features like "Regarding" on a Task (which could link to a Lead, Account, or Opportunity).
54+
55+
```yaml
56+
fields:
57+
related_to:
58+
type: lookup
59+
reference_to: [lead, account, opportunity] # Polymorphic Array
60+
61+
```
62+
63+
### Under the Hood
64+
65+
* **Compiler:** When you query a Lookup field, the engine does not just return the ID. It allows for **Graph Expansion**:
66+
```json
67+
// Query Intent
68+
{ "expand": ["owner"] }
69+
70+
```
71+
72+
73+
The compiler translates this into a highly optimized `LEFT JOIN users ON ...`.
74+
75+
## 3. Formula (Database-Compiled Logic)
76+
77+
Formula fields are **Read-Only Virtual Columns**. They allow you to define business logic in the schema that is compiled into the database query execution plan.
78+
79+
### Why not use JavaScript?
80+
81+
If you calculate `Total = Price * Qty` in JavaScript (Node.js), you cannot sort or filter by `Total` in the database. By using Formula fields, the calculation happens inside the DB engine, allowing high-performance sorting and filtering on millions of rows.
82+
83+
### Definition
84+
85+
Use the `${field_name}` syntax to reference other fields.
86+
87+
```yaml
88+
fields:
89+
price: { type: currency }
90+
quantity: { type: number }
91+
92+
# The Magic Field
93+
subtotal:
94+
type: formula
95+
data_type: currency
96+
formula: "${price} * ${quantity}"
97+
98+
```
99+
100+
### Compiler Output (PostgreSQL Example)
101+
102+
When ObjectQL generates the SQL for this object, it injects the logic into the `SELECT` list:
103+
104+
```sql
105+
SELECT
106+
t1.price,
107+
t1.quantity,
108+
(t1.price * t1.quantity) AS subtotal
109+
FROM order_lines t1
110+
111+
```
112+
113+
## 4. Summary (Rollup Aggregations)
114+
115+
Summary fields allow a parent object to calculate metrics from its child records (One-to-Many).
116+
117+
### The Challenge
118+
119+
In traditional development, calculating "Total Value of Open Orders" for a Customer requires writing a complex SQL query or a trigger.
120+
121+
### The ObjectQL Solution
122+
123+
Define the aggregation declaratively. The engine handles the sub-queries or materialized views.
124+
125+
### Definition
126+
127+
```yaml
128+
# Object: account
129+
fields:
130+
# Metric: How much has this customer spent?
131+
total_lifetime_value:
132+
type: summary
133+
summary_object: order # The child object
134+
summary_type: sum # Operation: count, sum, min, max
135+
summary_field: grand_total # The field on child to aggregate
136+
filters: # Conditional Aggregation
137+
- ["status", "=", "paid"]
138+
139+
```
140+
141+
### Use Cases
142+
143+
* **CRM:** "Number of Open Tickets" on a Customer profile.
144+
* **Project Management:** "Max Due Date" of all Tasks in a Project.
145+
* **Inventory:** "Sum of Quantity" from Stock Movements.
146+
147+
## Summary of Capabilities
148+
149+
| Type | Data Location | Computation Time | Primary Use Case |
150+
| --- | --- | --- | --- |
151+
| **Currency** | Physical Column | N/A | Financial transactions, Prices. |
152+
| **Lookup** | Physical Column (FK) | N/A | Linking records, Polymorphic relations. |
153+
| **Formula** | **Virtual** | Read Time (Query) | Row-level math, String concatenation. |
154+
| **Summary** | **Virtual** | Read Time (Query) | Parent-level aggregation (Sum/Count). |
155+
156+
:::tip Performance Note
157+
Virtual fields (Formula & Summary) are powerful, but relying on them heavily for complex sorting on massive datasets can impact DB performance. For datasets >10M rows, consider using ObjectOS Triggers to materialize these values into physical columns.
158+
:::
File renamed without changes.

packages/cli/src/commands/build.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export function registerBuildCommand(cli) {
2525
}
2626

2727
// Copy user config and assets to nextAppDir
28-
const userConfigPath = path.resolve(process.cwd(), 'objectdocs.json');
28+
const userConfigPath = path.resolve(process.cwd(), 'content/objectdocs.json');
2929
if (fs.existsSync(userConfigPath)) {
3030
console.log(` Copying config from ${userConfigPath}`);
3131
fs.cpSync(userConfigPath, path.join(nextAppDir, 'objectdocs.json'));

packages/cli/src/commands/dev.mjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ export function registerDevCommand(cli) {
4444

4545
const startServer = () => {
4646
// Sync config and assets before starting
47-
const userConfigPath = path.resolve(process.cwd(), 'objectdocs.json');
47+
const userConfigPath = path.resolve(process.cwd(), 'content/objectdocs.json');
4848
if (fs.existsSync(userConfigPath)) {
4949
fs.cpSync(userConfigPath, path.join(nextAppDir, 'objectdocs.json'));
5050
}
@@ -79,7 +79,7 @@ export function registerDevCommand(cli) {
7979
startServer();
8080

8181
// Watch for config changes
82-
const configFile = path.resolve(process.cwd(), 'objectdocs.json');
82+
const configFile = path.resolve(process.cwd(), 'content/objectdocs.json');
8383
if (fs.existsSync(configFile)) {
8484
console.log(`Watching config: ${configFile}`);
8585
fs.watch(configFile, (eventType) => {

packages/cli/src/commands/start.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export function registerStartCommand(cli) {
2222
// 2. Check config
2323
let isStatic = false;
2424
try {
25-
const configPath = path.resolve(process.cwd(), 'objectdocs.json');
25+
const configPath = path.resolve(process.cwd(), 'content/objectdocs.json');
2626
if (fs.existsSync(configPath)) {
2727
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
2828
if (config.build?.output === 'export') {

0 commit comments

Comments
 (0)