diff --git a/docs/en/low-code/custom-endpoints.md b/docs/en/low-code/custom-endpoints.md index ad0057df70..b3af587707 100644 --- a/docs/en/low-code/custom-endpoints.md +++ b/docs/en/low-code/custom-endpoints.md @@ -106,7 +106,7 @@ The full [Scripting API](scripting-api.md) (`db` object) is available for queryi "route": "/api/custom/products/stats", "method": "GET", "requireAuthentication": false, - "javascript": "var totalCount = await db.count('LowCodeDemo.Products.Product');\nvar avgPrice = totalCount > 0 ? await db.query('LowCodeDemo.Products.Product').average(p => p.Price) : 0;\nreturn ok({ totalProducts: totalCount, averagePrice: avgPrice });" + "javascript": "var totalCount = await db.count('LowCodeDemo.Products.Product');\nvar productTable = await db.query('LowCodeDemo.Products.Product');\nvar avgPrice = totalCount > 0 ? await productTable.average(p => p.Price) : 0;\nreturn ok({ totalProducts: totalCount, averagePrice: avgPrice });" } ``` @@ -118,7 +118,7 @@ The full [Scripting API](scripting-api.md) (`db` object) is available for queryi "route": "/api/custom/customers/search", "method": "GET", "requireAuthentication": true, - "javascript": "var searchTerm = query.q || '';\nvar customers = await db.query('LowCodeDemo.Customers.Customer')\n .where(c => c.Name.toLowerCase().includes(searchTerm.toLowerCase()))\n .take(10)\n .toList();\nreturn ok(customers.map(c => ({ id: c.Id, name: c.Name, email: c.EmailAddress })));" + "javascript": "var searchTerm = query.q || '';\nvar customerTable = await db.query('LowCodeDemo.Customers.Customer');\nvar customers = await customerTable\n .where(c => c.Name.toLowerCase().includes(searchTerm.toLowerCase()))\n .take(10)\n .toList();\nreturn ok(customers.map(c => ({ id: c.Id, name: c.Name, email: c.EmailAddress })));" } ``` diff --git a/docs/en/low-code/scripting-api.md b/docs/en/low-code/scripting-api.md index a08b1107ec..5cd790b7aa 100644 --- a/docs/en/low-code/scripting-api.md +++ b/docs/en/low-code/scripting-api.md @@ -21,7 +21,8 @@ The `db` object is the main entry point for all data operations. ```javascript // Immutable pattern — each call creates a new builder -var baseQuery = db.query('Entity').where(x => x.Active); +var entityTable = await db.query('Entity'); +var baseQuery = await entityTable.where(x => x.Active); var cheap = baseQuery.where(x => x.Price < 100); // baseQuery unchanged var expensive = baseQuery.where(x => x.Price > 500); // baseQuery unchanged ``` @@ -31,13 +32,15 @@ var expensive = baseQuery.where(x => x.Price > 500); // baseQuery unchanged ### Basic Queries ```javascript -var products = await db.query('LowCodeDemo.Products.Product') +var productTable = await db.query('LowCodeDemo.Products.Product'); +var products = await productTable .where(x => x.Price > 100) .orderBy(x => x.Price) .take(10) .toList(); -var result = await db.query('LowCodeDemo.Products.Product') +var resultTable = await db.query('LowCodeDemo.Products.Product'); +var result = await resultTable .where(x => x.Price > 100 && x.Price < 500) .where(x => x.StockCount > 0) .orderByDescending(x => x.Price) @@ -63,9 +66,12 @@ var result = await db.query('LowCodeDemo.Products.Product') | `all(x => condition)` | Check if all records match | `Promise` | | `isEmpty()` | Check if no results | `Promise` | | `isSingle()` | Check if exactly one result | `Promise` | -| `first()` / `firstOrDefault()` | Return first match or null | `Promise` | -| `last()` / `lastOrDefault()` | Return last match or null | `Promise` | -| `single()` / `singleOrDefault()` | Return single match or null | `Promise` | +| `first()` | Return first match, throws if empty | `Promise` | +| `firstOrDefault()` | Return first match or null | `Promise` | +| `last()` | Return last match, throws if empty | `Promise` | +| `lastOrDefault()` | Return last match or null | `Promise` | +| `single()` | Return single match, throws if empty/multiple | `Promise` | +| `singleOrDefault()` | Return single match or null (throws if multiple) | `Promise` | | `elementAt(index)` | Return element at index | `Promise` | | `select(x => projection)` | Project to custom shape | `QueryBuilder` | | `join(entity, alias, condition)` | Inner join | `QueryBuilder` | @@ -92,16 +98,18 @@ var minPrice = 100; var config = { minStock: 10 }; var nested = { range: { min: 50, max: 200 } }; -var result = await db.query('Entity').where(x => x.Price > minPrice).toList(); -var result2 = await db.query('Entity').where(x => x.StockCount > config.minStock).toList(); -var result3 = await db.query('Entity').where(x => x.Price >= nested.range.min).toList(); +var entityTable = await db.query('Entity'); +var result = await entityTable.where(x => x.Price > minPrice).toList(); +var result2 = await entityTable.where(x => x.StockCount > config.minStock).toList(); +var result3 = await entityTable.where(x => x.Price >= nested.range.min).toList(); ``` ### Contains / IN Operator ```javascript var targetPrices = [50, 100, 200]; -var products = await db.query('Entity') +var entityTable = await db.query('Entity'); +var products = await entityTable .where(x => targetPrices.includes(x.Price)) .toList(); ``` @@ -109,7 +117,8 @@ var products = await db.query('Entity') ### Select Projection ```javascript -var projected = await db.query('LowCodeDemo.Products.Product') +var productTable = await db.query('LowCodeDemo.Products.Product'); +var projected = await productTable .where(x => x.Price > 0) .select(x => ({ ProductName: x.Name, ProductPrice: x.Price })) .toList(); @@ -120,7 +129,8 @@ var projected = await db.query('LowCodeDemo.Products.Product') ### Explicit Joins ```javascript -var orderLines = await db.query('LowCodeDemo.Orders.OrderLine') +var orderLineTable = await db.query('LowCodeDemo.Orders.OrderLine'); +var orderLines = await orderLineTable .join('LowCodeDemo.Products.Product', 'p', (ol, p) => ol.ProductId === p.Id) .take(10) .toList(); @@ -135,7 +145,8 @@ orderLines.forEach(line => { ### Left Join ```javascript -var orders = await db.query('LowCodeDemo.Orders.Order') +var orderTable = await db.query('LowCodeDemo.Orders.Order'); +var orders = await orderTable .leftJoin('LowCodeDemo.Products.Product', 'p', (o, p) => o.CustomerId === p.Id) .toList(); @@ -149,18 +160,22 @@ orders.forEach(order => { ### LINQ-Style Join ```javascript -db.query('Order') - .join('LowCodeDemo.Products.Product', - o => o.ProductId, - p => p.Id) +var orderTable = await db.query('Order'); +await orderTable.join( + 'LowCodeDemo.Products.Product', + o => o.ProductId, + p => p.Id +); ``` ### Join with Filtered Query ```javascript -var expensiveProducts = db.query('Product').where(p => p.Price > 100); +var productTable = await db.query('Product'); +var expensiveProducts = await productTable.where(p => p.Price > 100); -var orders = await db.query('OrderLine') +var orderLineTable = await db.query('OrderLine'); +var orders = await orderLineTable .join(expensiveProducts, ol => ol.ProductId, p => p.Id) @@ -179,8 +194,9 @@ Set operations execute at the database level using SQL: | `except(query)` | `EXCEPT` | Elements in first, not second | ```javascript -var cheap = db.query('Product').where(x => x.Price <= 100); -var popular = db.query('Product').where(x => x.Rating > 4); +var productTable = await db.query('Product'); +var cheap = await productTable.where(x => x.Price <= 100); +var popular = await productTable.where(x => x.Rating > 4); var bestDeals = await cheap.intersect(popular).toList(); var underrated = await cheap.except(popular).toList(); @@ -200,15 +216,17 @@ All aggregations execute as SQL statements: | `groupBy(x => x.Property)` | `GROUP BY ...` | `Promise` | ```javascript -var totalValue = await db.query('Product').sum(x => x.Price); -var avgPrice = await db.query('Product').where(x => x.InStock).average(x => x.Price); -var cheapest = await db.query('Product').min(x => x.Price); +var productTable = await db.query('Product'); +var totalValue = await productTable.sum(x => x.Price); +var avgPrice = await (await productTable.where(x => x.InStock)).average(x => x.Price); +var cheapest = await productTable.min(x => x.Price); ``` ### GroupBy with Select ```javascript -var grouped = await db.query('Product') +var productTable = await db.query('Product'); +var grouped = await productTable .groupBy(x => x.Category) .select(g => ({ Category: g.Key, @@ -237,7 +255,8 @@ var grouped = await db.query('Product') ### GroupBy with Items ```javascript -var grouped = await db.query('Product') +var productTable = await db.query('Product'); +var grouped = await productTable .groupBy(x => x.Category) .select(g => ({ Category: g.Key, @@ -258,11 +277,12 @@ var grouped = await db.query('Product') Math functions translate to SQL functions (ROUND, FLOOR, CEILING, ABS, etc.): ```javascript -var products = await db.query('Product') +var productTable = await db.query('Product'); +var products = await productTable .where(x => Math.round(x.Price) > 100) .toList(); -var result = await db.query('Product') +var result = await productTable .where(x => Math.abs(x.Balance) < 10 && Math.floor(x.Rating) >= 4) .toList(); ``` @@ -380,7 +400,8 @@ All values are parameterized: ```javascript var malicious = "'; DROP TABLE Products;--"; // Safely treated as a literal string — no injection -var result = await db.query('Entity').where(x => x.Name.includes(malicious)).count(); +var entityTable = await db.query('Entity'); +var result = await (await entityTable.where(x => x.Name.includes(malicious))).count(); ``` ### Blocked Features @@ -397,7 +418,8 @@ if (!context.commandArgs.getValue('Email').includes('@')) { // Try-catch for safe execution try { - var products = await db.query('Entity').where(x => x.Price > 0).toList(); + var entityTable = await db.query('Entity'); + var products = await entityTable.where(x => x.Price > 0).toList(); } catch (error) { context.log('Query failed: ' + error.message); } @@ -422,7 +444,8 @@ try { var productId = context.commandArgs.getValue('ProductId'); var quantity = context.commandArgs.getValue('Quantity'); -var product = await db.query('LowCodeDemo.Products.Product') +var productTable = await db.query('LowCodeDemo.Products.Product'); +var product = await productTable .where(x => x.Id === productId) .first(); @@ -435,11 +458,10 @@ context.commandArgs.setValue('TotalAmount', product.Price * quantity); ### Sales Dashboard (Custom Endpoint) ```javascript -var totalOrders = await db.query('LowCodeDemo.Orders.Order').count(); -var delivered = await db.query('LowCodeDemo.Orders.Order') - .where(x => x.IsDelivered === true).count(); -var revenue = await db.query('LowCodeDemo.Orders.Order') - .where(x => x.IsDelivered === true).sum(x => x.TotalAmount); +var orderTable = await db.query('LowCodeDemo.Orders.Order'); +var totalOrders = await orderTable.count(); +var delivered = await (await orderTable.where(x => x.IsDelivered === true)).count(); +var revenue = await (await orderTable.where(x => x.IsDelivered === true)).sum(x => x.TotalAmount); return ok({ orders: totalOrders,