# Operatory wyrażeń (logika i formatowanie wewnątrz etapów)

**Wprowadzenie:**

<p class="callout info">Operatory wyrażeń to „funkcje" używane **wewnątrz etapów potoku** (najczęściej `$project`, `$addFields` i `$group`) do obliczania wartości — warunków, działań arytmetycznych, formatowania tekstu i dat. Różnią się od operatorów zapytań (tych z `find()` i `$match`), które jedynie filtrują dokumenty. Operatory wyrażeń *tworzą* nowe wartości.</p>

Wszystkie poniższe komendy wykonuje się w powłoce mongosh, w ramach potoku agregacji (zob. strona „Aggregation pipelines").

<p class="callout warning">**Kluczowa różnica składni:** operatory porównań w wyrażeniach przyjmują argumenty w **tablicy**, np. `{ $gt: ["$kwota", 100] }`. To nie to samo co forma zapytania `{ kwota: { $gt: 100 } }` używana w `$match`/`find()`. Mylenie tych dwóch form to najczęstszy błąd początkujących.</p>

## 1. Operator `$cond` – warunek if/then/else

```javascript
db.zamowienia.aggregate([
  { $addFields: {
      kategoriaKwoty: {
        $cond: { if: { $gte: ["$kwota", 500] }, then: "duze", else: "male" }
      }
  } }
])
```

Zwraca jedną z dwóch wartości w zależności od warunku — odpowiednik `if/else`. Powyżej każdemu zamówieniu dopisywane jest pole `kategoriaKwoty` równe `"duze"`, gdy kwota wynosi co najmniej 500, w przeciwnym razie `"male"`.

**Skrócona składnia tablicowa** (kolejność: warunek, then, else):

```javascript
{ $cond: [ { $gte: ["$kwota", 500] }, "duze", "male" ] }
```

<p class="callout info">**Screen:** wynik z dopisanym polem `kategoriaKwoty`.</p>

## 2. Operator `$switch` – wielokrotny wybór

```javascript
db.zamowienia.aggregate([
  { $addFields: {
      prog: {
        $switch: {
          branches: [
            { case: { $gte: ["$kwota", 1000] }, then: "premium" },
            { case: { $gte: ["$kwota", 500] },  then: "standard" }
          ],
          default: "podstawowy"
        }
      }
  } }
])
```

Sprawdza kolejno warunki z listy `branches` i zwraca wynik pierwszego pasującego (`then`). Jeśli żaden nie pasuje, zwraca `default`. To czytelniejsza alternatywa dla wielu zagnieżdżonych `$cond`. Warunki są sprawdzane od góry, więc kolejność ma znaczenie.

## 3. Operator `$ifNull` – wartość domyślna dla braków

```javascript
db.zamowienia.aggregate([
  { $addFields: { rabat: { $ifNull: ["$rabat", 0] } } }
])
```

Zwraca pierwszą wartość, jeśli nie jest ona `null` ani nie brakuje jej w dokumencie; w przeciwnym razie zwraca wartość zastępczą. Tutaj brakujące pole `rabat` zostaje uzupełnione zerem, co zabezpiecza dalsze obliczenia przed błędami na wartościach `null`.

<p class="callout info">**Screen:** dokument z uzupełnionym polem `rabat`.</p>

## 4. Operatory porównań w wyrażeniach

```javascript
{ $eq:  ["$status", "oplacone"] }   // równe
{ $ne:  ["$status", "anulowane"] }  // różne
{ $gt:  ["$kwota", 100] }           // wiéksze niż (>)
{ $gte: ["$kwota", 100] }           // wiéksze lub równe
{ $lt:  ["$kwota", 100] }           // mniejsze niż
{ $cmp: ["$a", "$b"] }              // -1, 0 lub 1
```

Zwracają wartość logiczną (`true`/`false`), więc używa się ich zwykle jako warunku w `$cond` lub `$switch`. Pamiętaj o składni tablicowej — dwa porównywane wyrażenia podaje się jako elementy tablicy.

## 5. Operatory logiczne – `$and`, `$or`, `$not`

```javascript
{ $and: [ { $gte: ["$kwota", 100] }, { $eq: ["$status", "oplacone"] } ] }
{ $or:  [ { $eq: ["$vip", true] }, { $gt: ["$kwota", 1000] } ] }
{ $not: [ { $eq: ["$status", "anulowane"] } ] }
```

Łączą kilka warunków logicznych. Przyjmują tablicę wyrażeń i zwracają wartość logiczną — przydatne do budowania złożonych warunków w `$cond`.

## 6. Operatory arytmetyczne

```javascript
{ $add:      ["$kwota", "$rabat"] }   // dodawanie
{ $subtract: ["$kwota", "$rabat"] }   // odejmowanie
{ $multiply: ["$kwota", 1.23] }       // mnożenie (np. brutto)
{ $divide:   ["$suma", "$liczba"] }   // dzielenie
{ $mod:      ["$liczba", 2] }         // reszta z dzielenia
{ $round:    ["$kwota", 2] }          // zaokrąglenie do 2 miejsc
```

Wykonują działania na liczbach. Argumentami mogą być zarówno pola dokumentu (z prefiksem `$`), jak i wartości stałe. Dostępne są też `$abs`, `$ceil` i `$floor`.

## 7. Operatory łańcuchowe (tekstowe)

```javascript
{ $concat:  ["$imie", " ", "$nazwisko"] }  // sklejanie tekstu
{ $toUpper: "$status" }                     // wielkie litery
{ $toLower: "$email" }                      // małe litery
{ $substr:  ["$kod", 0, 3] }                // wycinek (od, długość)
{ $split:   ["$email", "@"] }               // podział na tablicé
{ $trim:    { input: "$nazwa" } }           // usuniécie spacji z brzegów
```

Służą do manipulacji tekstem. `$concat` łączy fragmenty (uwaga: jeśli któryś jest `null`, wynik też będzie `null` — warto opakować w `$ifNull`). `$split` zwraca tablicę, więc często łączy się go z operatorami tablicowymi.

## 8. Operator `$dateToString` – formatowanie dat

```javascript
db.zamowienia.aggregate([
  { $addFields: {
      dataTekst: {
        $dateToString: { format: "%Y-%m-%d", date: "$data", timezone: "Europe/Warsaw" }
      }
  } }
])
```

Zamienia pole typu data na sformatowany tekst według wzorca `format`. Opcjonalny `timezone` przelicza datę na wskazaną strefę przed sformatowaniem (bez niego daty są w UTC).

**Najczęstsze symbole formatu:**

```
%Y  rok (4 cyfry)      %H  godzina (00-23)
%m  miesiąc (01-12)    %M  minuty (00-59)
%d  dzień (01-31)      %S  sekundy (00-59)
```

<p class="callout info">**Screen:** dokument z polem `dataTekst` w formacie RRRR-MM-DD.</p>

## 9. Operatory na datach – składowe i obliczenia

```javascript
{ $year:       "$data" }   // sam rok
{ $month:      "$data" }   // sam miesiąc
{ $dayOfMonth: "$data" }   // sam dzień
```

Wyciągają pojedynczy składnik daty jako liczbę. Często używane w `$group` do grupowania np. po roku lub miesiącu.

**Różnica i przesunięcie dat:**

```javascript
{ $dateDiff: { startDate: "$data", endDate: "$$NOW", unit: "day" } }
{ $dateAdd:  { startDate: "$data", unit: "day", amount: 30 } }
```

`$dateDiff` liczy odstęp między dwiema datami w zadanej jednostce (zmienna `$$NOW` to bieżący czas serwera), a `$dateAdd` dodaje określony okres do daty.

## 10. Operatory tablicowe

```javascript
{ $size:        "$produkty" }                 // liczba elementów
{ $arrayElemAt: ["$produkty", 0] }            // element o indeksie
{ $first:       "$produkty" }                 // pierwszy element
{ $last:        "$produkty" }                 // ostatni element
```

Operują na polach będących tablicami. `$size` bywa przydatne np. po `$lookup`, by policzyć liczbę dopasowań.

**Przekształcanie i filtrowanie tablic:**

```javascript
// zostaw tylko produkty droższe niż 100
{ $filter: { input: "$produkty", as: "p", cond: { $gt: ["$$p.cena", 100] } } }

// wyciągnij samą nazwé z każdego produktu
{ $map: { input: "$produkty", as: "p", in: "$$p.nazwa" } }
```

`$filter` zwraca podzbiór tablicy spełniający warunek, a `$map` przekształca każdy element. W obu zmienna pętli (tu `$$p`) ma podwójny prefiks `$$`, bo odnosi się do zmiennej lokalnej, a nie do pola dokumentu.

## 11. Konwersje typów

```javascript
{ $toInt:    "$kwotaTekst" }   // tekst -> liczba całkowita
{ $toDouble: "$kwotaTekst" }   // tekst -> liczba zmiennoprzecinkowa
{ $toString: "$kwota" }        // liczba -> tekst
{ $toDate:   "$dataTekst" }    // tekst -> data
{ $type:     "$kwota" }        // zwraca nazwé typu pola
```

Zamieniają wartości między typami — niezbędne przy porządkowaniu danych zaimportowanych jako tekst. Bardziej elastyczny jest `$convert`, który pozwala wskazać typ docelowy i wartość zastępczą na wypadek błędu konwersji:

```javascript
{ $convert: { input: "$kwotaTekst", to: "double", onError: 0, onNull: 0 } }
```

## 12. Przykład złożony – operatory w akcji

```javascript
db.zamowienia.aggregate([
  { $addFields: {
      brutto: { $round: [ { $multiply: ["$kwota", 1.23] }, 2 ] },
      miesiac: { $dateToString: { format: "%Y-%m", date: "$data" } },
      etykieta: {
        $cond: [ { $gte: ["$kwota", 500] }, "VIP", "zwykly" ]
      },
      rabat: { $ifNull: ["$rabat", 0] }
  } }
])
```

Jeden etap `$addFields` liczy kwotę brutto (zaokrągloną), wyciąga miesiąc zamówienia jako tekst, nadaje etykietę na podstawie progu kwoty i uzupełnia brakujący rabat zerem. Pokazuje, jak operatory wyrażeń składa się ze sobą, by w jednym przebiegu przygotować dane do raportu.

<p class="callout info">**Screen:** wynik z czterema wyliczonymi polami.</p>

## Podsumowanie praktyczne

```javascript
// Najczęściej używane operatory wyrażeń
{ $cond:   [ { $gte: ["$kwota", 500] }, "duze", "male" ] }   // warunek
{ $ifNull: ["$rabat", 0] }                                    // wartość domyślna
{ $dateToString: { format: "%Y-%m-%d", date: "$data" } }      // formatowanie daty
{ $concat: ["$imie", " ", "$nazwisko"] }                      // sklejanie tekstu
{ $round:  [ { $multiply: ["$kwota", 1.23] }, 2 ] }           // arytmetyka
```