Skip to content

Add Fields

Add computed fields to documents using $addFields stage.

Basic Usage

Add new fields to documents:

konduct.collection<Product>()
    .addFields {
        "totalValue" from (Product::stock * Product::price)
        "inStock" from (Product::quantity gt 0)
    }
    .toList()

Field Sources

From Property References

.addFields {
    "productName" from Product::name
    "cost" from Product::price
}

From String Fields

.addFields {
    "uppercaseName" from "\$name"
    "nestedField" from "\$metadata.createdBy"
}

From Literal Values

.addFields {
    "status" from "pending"
    "processedAt" from Date()
    "version" from 1
}

From Expressions

.addFields {
    "total" from (Order::quantity * Order::price)
    "profit" from ((Product::sellingPrice - Product::costPrice) * Product::stock)
    "discountedPrice" from (Product::price * (1 - (Product::discount / 100)))
}

Array Operations

Sum Array Elements

.addFields {
    "totalHomework" sumOf Student::homework
    "totalQuiz" sumOf Student::quiz
}

Average Array

.addFields {
    "avgScore" avgOf Student::scores
}

Min/Max Array

.addFields {
    "highestScore" maxOf Student::scores
    "lowestScore" minOf Student::scores
}

Array Size

.addFields {
    "homeworkCount" sizeOf Student::homework
    "tagCount" sizeOf Product::tags
}

String Concatenation

Combine strings:

.addFields {
    "fullName" concat listOf(User::firstName, " ", User::lastName)
    "displayName" concat listOf(User::title, ". ", User::name)
}

Conditional Fields

Add fields based on conditions:

.addFields {
    "stockStatus" from when {
        Product::stock eq 0 -> "out_of_stock"
        Product::stock lt Product::reorderPoint -> "low_stock"
        otherwise -> "in_stock"
    }
}

Real-World Examples

E-Commerce: Calculate Order Total

data class OrderWithTotal(
    val id: String,
    val items: List<OrderItem>,
    val subtotal: Double,
    val tax: Double,
    val total: Double
)

konduct.collection<Order>()
    .addFields {
        "subtotal" sumOf "items.price"
        "tax" from ("\$subtotal" * 0.1)
        "total" from ("\$subtotal" + "\$tax")
    }
    .into<OrderWithTotal>()
    .toList()

Inventory: Stock Value

konduct.collection<Product>()
    .addFields {
        "stockValue" from (Product::quantity * Product::costPrice)
        "potentialRevenue" from (Product::quantity * Product::sellingPrice)
        "potentialProfit" from (
            (Product::quantity * Product::sellingPrice) - 
            (Product::quantity * Product::costPrice)
        )
        "marginPercentage" from (
            ((Product::sellingPrice - Product::costPrice) / Product::sellingPrice) * 100
        )
    }
    .match { "stockValue" gte 10000 }
    .sort { "potentialProfit".desc() }
    .toList()

User Profile: Full Name and Age

konduct.collection<User>()
    .addFields {
        "fullName" concat listOf(User::firstName, " ", User::lastName)
        "age" from yearsDiff(User::birthDate, Date())
        "isAdult" from (yearsDiff(User::birthDate, Date()) gte 18)
    }
    .toList()

Sales: Commission Calculation

konduct.collection<Sale>()
    .addFields {
        "revenue" from (Sale::quantity * Sale::price)
        "commission" from (
            (Sale::quantity * Sale::price) * (Sale::commissionRate / 100)
        )
        "netRevenue" from (
            (Sale::quantity * Sale::price) - 
            ((Sale::quantity * Sale::price) * (Sale::commissionRate / 100))
        )
    }
    .toList()

Computed Flags

Add boolean flags:

konduct.collection<Product>()
    .addFields {
        "isFeatured" from (Product::rating gte 4.5)
        "needsRestock" from (Product::stock lte Product::reorderPoint)
        "onSale" from (Product::discount gt 0)
        "isPremium" from (Product::price gte 1000)
    }
    .toList()

Replacing Fields

Override existing fields:

konduct.collection<Product>()
    .addFields {
        // Update price with discount applied
        "price" from (Product::price * (1 - (Product::discount / 100)))

        // Normalize status
        "status" from when {
            Product::stock eq 0 -> "unavailable"
            Product::active eq false -> "inactive"
            otherwise -> "active"
        }
    }
    .toList()

Nested Field Creation

Create nested objects:

.addFields {
    "address" from doc {
        "street" from User::street
        "city" from User::city
        "country" from User::country
    }
    "metadata" from doc {
        "createdAt" from Date()
        "version" from 1
    }
}

Combining with Other Stages

Match → AddFields → Sort

konduct.collection<Product>()
    .match { Product::status eq "active" }
    .addFields {
        "profitMargin" from (
            ((Product::sellingPrice - Product::costPrice) / Product::sellingPrice) * 100
        )
    }
    .sort { "profitMargin".desc() }
    .limit(10)
    .toList()

Group → AddFields

konduct.collection<Sale>()
    .group {
        by(Sale::productId)
        accumulate {
            "revenue" sum (Sale::quantity * Sale::price)
            "unitsSold" sum Sale::quantity
        }
    }
    .addFields {
        "avgPricePerUnit" from ("\$revenue" / "\$unitsSold")
    }
    .toList()

Using Expressions

Leverage the expression system:

import io.github.denofbits.konduct.expressions.*

konduct.collection<Order>()
    .addFields {
        "total" from (
            (OrderItem::quantity * OrderItem::price) +
            (OrderItem::quantity * OrderItem::price * (OrderItem::taxRate / 100)) -
            (OrderItem::quantity * OrderItem::price * (OrderItem::discountRate / 100))
        )
    }
    .toList()

Performance Tips

  1. Add fields after filtering:

       // ✅ Good
       .match { Product::status eq "active" }
       .addFields { /* computations */ }
    
       // ❌ Bad - computing for all documents
       .addFields { /* computations */ }
       .match { Product::status eq "active" }
    

  2. Avoid expensive computations:

       // Consider pre-computing in application or database
       .addFields {
           "complexCalculation" from heavyComputation()  // May be slow
       }
    

  3. Reuse computed fields:

       .addFields {
           "subtotal" from (quantity * price)
       }
       .addFields {
           "total" from ("\$subtotal" + "\$tax")  // Reuse subtotal
       }
    

See Also