# Retrieve with ARX.SDK on MongoDB

## What a read does

| Rule | On MongoDB |
|---|---|
| One record | By id, or by a key or fields; not found is null; several matches are ARX2003 |
| Lookups | A declared lookup reads back as an `ArxEntityReference` with the referenced record's display name |
| Field names | Case-sensitive in MongoDB: a field the catalog declares is read and written with its declared spelling whatever the attribute's case; an undeclared field by its exact spelling (`Name` and `name` are two fields) |
| Functions and Execute | Not offered: MongoDB has no stored procedures or functions that return rows (ARX8001) |

## Reference

### Value types read back

| Stored as | Read back as |
|---|---|
| string | `string` |
| 32-bit integer | `int` |
| 64-bit integer | `long` |
| double | `double` |
| Decimal128 | `decimal` (ARX4001 past .NET's range) |
| boolean | `bool` |
| date | `DateTime`, UTC (a `DateOnly` is stored as midnight UTC: read it with `GetAttributeValue<DateOnly>`, which converts a midnight `DateTime` from 0.1.0-preview.5, or preload the field) |
| ObjectId | `string` (24 hexadecimal characters) |
| binary subtype 4 | `Guid` |
| other binary | `byte[]` |
| embedded document | `ArxJsonB` |
| array of one kind of value | a typed array (`string[]`, `int[]`, …); any other array as `ArxJsonB` |
| a declared lookup | `ArxEntityReference` |
| a preloaded field | the preloaded type (`DateOnly` from a date, `TimeOnly` from text, …) |

### Aggregates

| Aggregate | On MongoDB | Reads back as |
|---|---|---|
| Count, CountBig, CountDistinct, CountBigDistinct | `$sum` of 1 (or of the documents where the field is set), `$addToSet` then its size, as a 64-bit integer | `long` |
| Sum | `$sum`: of 32-bit integers, a 32-bit integer while it fits and a 64-bit one past it; of doubles, a double; of Decimal128, a Decimal128. A field no document has sums to 0 | `int`, `long`, `double` or `decimal` |
| Avg | `$avg`: a double (a Decimal128 for Decimal128 values) | `double` or `decimal` |
| Min, Max | `$min`, `$max` | the field's type |

## The samples

Every Retrieve sample, in reading order. Each scenario shows the code (verified against MongoDB) and the mongosh query that checks what it did. `service` is the application's `ArxMongoDbService`; values marked "In scope" come from earlier in the sample. What the samples' code refers to beyond the packages (SampleCatalog.cs, `dbContext`) is in the sample application file, arx-mongodb-sample-application.md.

### Read one record

Basics 1 of Retrieve.

Read one document, in three scenarios: by its id with every field, with chosen fields, and by a key; an id no document has reads null.

Good to know:

The id comes back under the declared column's name (`account_id`) and as `RecordId`.

Usings:

```csharp
using ARX.SDK.MongoDb.Services;
using ARX.SDK.Services;
```

Set up by the sample before its scenarios:

```csharp
var account = new ArxEntity("account");
account["name"] = "Consolidated Messenger";
account["account_number"] = "ACC-M951";
account["employee_count"] = 75;
var id = await service.CreateAsync(account);
```

#### 1. By id

By the id Create returned: every field.

In scope: `service` (ArxMongoDbService), `id` (ArxRecordId).

```csharp
ArxEntity? account = await service.RetrieveAsync("account", id);

Console.WriteLine($"{account?["name"]}, {account?["employee_count"]} employees, id {account?["account_id"]}");
```

Check it in the database:

```sql
db.account.find({ account_number: "ACC-M951" })
```

Expect: 1 document, Consolidated Messenger, employee_count 75.

#### 2. Chosen fields

Only the fields you name (the id always comes too).

In scope: `service` (ArxMongoDbService), `id` (ArxRecordId).

```csharp
ArxEntity? account = await service.RetrieveAsync("account", id, new ArxColumnSet("name"));

Console.WriteLine($"fields read: {string.Join(", ", account!.Attributes.Keys)}");
```

Check it in the database:

```sql
db.account.find({ account_number: "ACC-M951" }, { name: 1 })
```

Expect: 1 document, _id and name.

#### 3. By a key

By the account number (a declared key), and a number no account has: null.

In scope: `service` (ArxMongoDbService).

```csharp
var match = new ArxEntity("account");
match["account_number"] = "ACC-M951";
ArxEntity? found = await service.RetrieveAsync(match, new ArxRetrieveOptions { Match = ArxMatch.Key("account_number") });

match["account_number"] = "ACC-M959";
ArxEntity? missing = await service.RetrieveAsync(match, new ArxRetrieveOptions { Match = ArxMatch.Key("account_number") });

Console.WriteLine($"ACC-M951: {found?["name"]}; ACC-M959: {(missing == null ? "null" : "found")}");
```

Check it in the database:

```sql
db.account.countDocuments({ account_number: "ACC-M959" })
```

Expect: 0.

