MongoDBでデータの検索について試してみました

「[MongoDBをDocker上に構築してみる](https://qiita.com/masato-sato/items/9f32e390a0b9b0f5e814)」で MongDBの環境を作成したので、データの検索について試してみたいと思います。
## 初期データ
上記で初期データとして投入したものでは少し物足りないので、初期データを下記のものに入れ替えました。自分がRDBの考え方になっているので、リレーション風のデータの形で、投入してみます。
```java:init_db.js
db = db.getSiblingDB('db_employee_info');

db.employee.insertMany([
    {
        employeeID: 'emp001',
        divisionID: 'ka001',
        roleID: 'yaku001',
        employeeName: '田村',
        age: 30,
        salary: 400000,
        created_date: new Date(),
        updated_date: new Date()
    },
    {
        employeeID: 'emp002',
        divisionID: 'ka001',
        roleID: 'yaku002',
        employeeName: '山田',
        age: 31,
        salary: 350000,
        created_date: new Date(),
        updated_date: new Date()
    },
    {
        employeeID: 'emp003',
        divisionID: 'ka002',
        roleID: 'yaku003',
        employeeName: '中山',
        age: 37,
        salary: 300000,
        created_date: new Date(),
        updated_date: new Date()
    },
    {
        employeeID: 'emp004',
        divisionID: 'ka002',
        roleID: 'yaku004',
        employeeName: '佐藤',
        age: 23,
        salary: 250000,
        created_date: new Date(),
        updated_date: new Date()
    },
    {
        employeeID: 'emp005',
        divisionID: 'ka003',
        roleID: 'yaku002',
        employeeName: '佐々木',
        age: 33,
        salary: 350000,
        created_date: new Date(),
        updated_date: new Date()
    },
    {
        employeeID: 'emp006',
        divisionID: 'ka004',
        roleID: 'yaku002',
        employeeName: '吉田',
        age: 35,
        salary: 350000,
        created_date: new Date(),
        updated_date: new Date()
    }
]);
```
MongoDBが起動されたら、NoSQL Booster で確認したところ、初期データが登録されていることが確認できました。
![nosqlbooster_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763635/6722e912-b467-b18d-79d9-d6d55b439cc8.png)

## データベース用語の違い
MongoDBはスキーマレスであれことから、データベースで使用される用語が違います。MongoDBとRBDの違いを記しておきます。
| RDB | MongoDB |
| ---- | ---- |
| テーブル | コレクション |
| 行 | ドキュメント |
| 列 | フィールド |
| プライマリキー | ObjectID |
| インデックス | インデックス |

## 検索
```java:検索：説明
> db.collection.find(
  { <検索条件> },
  { key1: 0, key2: 1, ・・・}
)
```
最初の｛ ｝は検索条件を指定します。検索条件はJSONのような形式で記述します。
その後に、指定したフィールドだけを取得します。
key: 0　フィールドを非表示にする
key: 1　フィールドを表示する

```java:検索：サンプル
> db_employee_info> db.employee.find({age:31})
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54f'),
    employeeID: 'emp002',
    divisionID: 'ka001',
    roleID: 'yaku002',
    employeeName: '山田',
    age: 31,
    salary: 350000,
    created_date: ISODate('2024-07-08T06:31:21.820Z'),
    updated_date: ISODate('2024-07-08T06:31:21.820Z')
  }
]

> db_employee_info> db.employee.find({age:31},{_id:0, age:0})
[
  {
    employeeID: 'emp002',
    divisionID: 'ka001',
    roleID: 'yaku002',
    employeeName: '山田',
    salary: 350000,
    created_date: ISODate('2024-07-08T06:31:21.820Z'),
    updated_date: ISODate('2024-07-08T06:31:21.820Z')
  }
]

> db_employee_info> db.employee.find({age:31},{employeeName:1})
[
  { 
    _id: ObjectId('668b87b9e98d52fe42f3f54f'),
    employeeName: '山田'
  }
]
```
『_id』は、データを登録したときに自動で付与される主キーになります。

## 配列データ
MongoDBでは、配列のデータを保持することができます。
```java: データ投入
db.employee.insertOne({
        employeeID: 'emp008',
        divisionID: 'ka001',
        roleID: 'yaku004',
        employeeName: '大田',
        age: 36,
        salary: 320000,
        skill: [ { name: "Oracle", level: 3},{ name: "MySQL", level: 2},{ name: "Redis", level: 1},{ name: "MongoDB", level: 5}],
        created_date: new Date(),
        updated_date: new Date()
        })
```
![nosqlbooster_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763635/83c47347-b39d-1a37-5ab7-b2eae9159520.png)

```java:配列データの検索
> db.employee.find({"skill.level":3},{_id:0})
[
  {
    employeeID: 'emp008',
    divisionID: 'ka001',
    roleID: 'yaku004',
    employeeName: '大田',
    age: 36,
    salary: 320000,
    skill: [
      { name: 'Oracle', level: 3 },
      { name: 'MySQL', level: 2 },
      { name: 'Redis', level: 1 },
      { name: 'MongoDB', level: 5 }
    ],
    created_date: ISODate('2024-07-08T09:09:51.607Z'),
    updated_date: ISODate('2024-07-08T09:09:51.607Z')
  }
]
```
配列の中のオブジェクトを検索条件に指定する場合は『"配列.オブジェクトのプロパティ名"』を指定します。この場合、""（ダブルコーテーション）で括る必要があります。上記では、"skill.level" としています。

## 検索条件（完全一致／部分一致／前方一致／後方一致）

```java:検索条件：完全一致
> db_employee_info> db.employee.find( { employeeName: '山田' },{ _id:0 } )
[
  {
    employeeID: 'emp002',
    divisionID: 'ka001',
    roleID: 'yaku002',
    employeeName: '山田',
    age: 31,
    salary: 350000,
    created_date: ISODate('2024-07-08T06:31:21.820Z'),
    updated_date: ISODate('2024-07-08T06:31:21.820Z')
  }
]
```
```java:検索条件：部分一致
> db_employee_info> db.employee.find( { employeeName: /田/ },{ employeeName:1 } )
[
  { _id: ObjectId('668b87b9e98d52fe42f3f54e'), employeeName: '田村' },
  { _id: ObjectId('668b87b9e98d52fe42f3f54f'), employeeName: '山田' },
  { _id: ObjectId('668b87b9e98d52fe42f3f553'), employeeName: '吉田' },
  { _id: ObjectId('668bacdf05dbdfe7c7f3f54f'), employeeName: '大田' }
]
```
```java:検索条件：前方一致
> db_employee_info> db.employee.find( { employeeName: /^田/ },{ employeeName:1 } )
[ { _id: ObjectId('668b87b9e98d52fe42f3f54e'), employeeName: '田村' } ]
```
```java:検索条件：後方一致
> db_employee_info> db.employee.find( { employeeName: /田$/ },{ employeeName:1 } )
[
  { _id: ObjectId('668b87b9e98d52fe42f3f54f'), employeeName: '山田' },
  { _id: ObjectId('668b87b9e98d52fe42f3f553'), employeeName: '吉田' },
  { _id: ObjectId('668bacdf05dbdfe7c7f3f54f'), employeeName: '大田' }
]
```
## 検索条件（範囲指定）

```java:検索条件：以下
> db_employee_info> db.employee.find({age: { $lte: 30 } },{ employeeName:1, age:1 } )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54e'),
    employeeName: '田村',
    age: 30
  },
  {
    _id: ObjectId('668b87b9e98d52fe42f3f551'),
    employeeName: '佐藤',
    age: 23
  }
]
```
```java:検索条件：未満
> db_employee_info> db.employee.find({age: { $lt: 30 } },{ employeeName:1, age:1 } )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f551'),
    employeeName: '佐藤',
    age: 23
  }
]
```
```java:検索条件：以上
> db_employee_info> db.employee.find( { age: { $gte: 36 } },{ employeeName:1, age:1 } )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f550'),
    employeeName: '中山',
    age: 37
  },
  {
    _id: ObjectId('668babfb05dbdfe7c7f3f54e'),
    employeeName: '上野',
    age: 36
  },
  {
    _id: ObjectId('668bacdf05dbdfe7c7f3f54f'),
    employeeName: '大田',
    age: 36
  }
]
```
```java:検索条件：より大きい
> db_employee_info> db.employee.find( { age: { $gt: 36 } },{ employeeName:1, age:1 } )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f550'),
    employeeName: '中山',
    age: 37
  }
]
```
```java:検索条件：範囲指定
> db_employee_info> db.employee.find(
                        { age: { $gte: 30, $lte: 32 } },
                        { employeeName:1, age:1 }
                    )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54e'),
    employeeName: '田村',
    age: 30
  },
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54f'),
    employeeName: '山田',
    age: 31
  }
]
```
```java:検索条件：and
db_employee_info> db.employee.find(
                            { $and: [ 
                                { employeeName: /田/ },
                                {age: {$lte: 32 } } 
                                ] },
                            { employeeName:1, age:1 }
                        )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54e'),
    employeeName: '田村',
    age: 30
  },
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54f'),
    employeeName: '山田',
    age: 31
  }
]
```
```java:検索条件：or
> db_employee_info> db.employee.find(
                            { $or: [
                                { employeeName: /田村/ },
                                { age: { $gte: 37 } } 
                                ] },
                            { employeeName:1, age:1 }
                        )
[
  {
    _id: ObjectId('668b87b9e98d52fe42f3f54e'),
    employeeName: '田村',
    age: 30
  },
  {
    _id: ObjectId('668b87b9e98d52fe42f3f550'),
    employeeName: '中山',
    age: 37
  }
]
```
```java:検索条件：「skill -> name」のフィールドを持っているデータ
> db_employee_info> db.employee.find({"skill.name": {$exists: true}},{employeeName:1, skill:1})
[
  {
    _id: ObjectId('668bacdf05dbdfe7c7f3f54f'),
    employeeName: '大田',
    skill: [
      { name: 'Oracle', level: 3 },
      { name: 'MySQL', level: 2 },
      { name: 'Redis', level: 1 },
      { name: 'MongoDB', level: 5 }
    ]
  }
]
```
```java:検索条件：指定した要素数をもつドキュメントを検索する
> db_employee_info> db.employee.find({skill: {$size: 4}},{employeeName:1, skill:1})
[
  {
    _id: ObjectId('668babfb05dbdfe7c7f3f54e'),
    employeeName: '上野',
    skill: [ 'Oracle', 'MySQL', 'Redis', 'MongoDB' ]
  },
  {
    _id: ObjectId('668bacdf05dbdfe7c7f3f54f'),
    employeeName: '大田',
    skill: [
      { name: 'Oracle', level: 3 },
      { name: 'MySQL', level: 2 },
      { name: 'Redis', level: 1 },
      { name: 'MongoDB', level: 5 }
    ]
  }
]
```
```java:検索結果をソートする
> db_employee_info> db.employee.find(
                            {},
                            { _id: 0, employeeName: 1, age: 1 }
                        ).sort(
                            { age: 1 }
                        )
[
  { employeeName: '佐藤', age: 23 },
  { employeeName: '田村', age: 30 },
  { employeeName: '山田', age: 31 },
  { employeeName: '佐々木', age: 33 },
  { employeeName: '吉田', age: 35 },
  { employeeName: '上野', age: 36 },
  { employeeName: '大田', age: 36 },
  { employeeName: '中山', age: 37 }
]
```
```java:検索結果の件数を制限する
> db_employee_info> db.employee.find(
                            {},
                            { _id: 0, employeeName: 1, age: 1 }
                        ).sort(
                            { age: 1 }
                        ).limit(2)
[
  { employeeName: '佐藤', age: 23 },
  { employeeName: '田村', age: 30 }
]
```
```java:検索結果で取得する開始位置を指定する　　※検索結果は0番目から始まる
> db_employee_info> db.employee.find(
                            {},
                            { _id: 0, employeeName: 1, age: 1 }
                        ).sort(
                            { age: 1 }
                        ).limit(2).skip(2)
[
  { employeeName: '山田', age: 31 },
  { employeeName: '佐々木', age: 33 }
]
```
# 最後に
今回はここまでとします。RDBのSQLと違い、JSON形式のデータを検索する機能（要素数による検索、フィールド名による検索）が用意されているため、RDBとはまた違った活用方法があると感じました。
他にも色々な検索条件があるようなので、便利な検索条件があればコメントいただけるとうれしいです。