1. 概要

デフォルトでは、 MongoDB エンジンは、抽出されたデータを並べ替えるときに大文字と小文字を考慮します。 集計または照合を指定することにより、大文字と小文字を区別しない並べ替えクエリを実行できます。

この短いチュートリアルでは、MongoDBシェルとJavaの両方を使用した2つのソリューションを見ていきます。

2. 環境の設定

まず、MongoDBサーバーを実行する必要があります。 Dockerイメージを使用してみましょう:

$ docker run -d -p 27017:27017 --name example-mongo mongo:latest

これにより、ポート27017を公開する「example-mongo」という名前の新しい一時Dockerコンテナが作成されます。 次に、ソリューションをテストするために必要なデータを使用して、基本的なMongoデータベースを作成する必要があります。

まず、コンテナ内のMongoShellを開きます。

$ docker exec -it example-mongo mongosh

シェルに入ったら、コンテキストを切り替えて、「sorting」という名前のデータベースに入ります。

> use sorting

最後に、並べ替え操作を試すためのデータを挿入しましょう。

> db.users.insertMany([
  {name: "ben", surname: "ThisField" },
  {name: "aen", surname: "Does" },
  {name: "Aen", surname: "Not" },
  {name: "Ben", surname: "Matter" },
])

一部のドキュメントのnameフィールドに同様の値を挿入しました。 唯一の違いは、最初の文字の場合です。 この時点で、データベースが作成され、データが適切に挿入されたので、アクションの準備が整いました。

3. デフォルトの並べ替え

カスタマイズせずに標準クエリを実行してみましょう。

> db.getCollection('users').find({}).sort({name:1})

返されるデータは、ケースを考慮して順序付けられます。 これは、たとえば、大文字の「B」が小文字の「a」の前に考慮されることを意味します。

[
  {
    _id: ..., name: 'Aen', surname: 'Not'
  },
  {
    _id: ..., name: 'Ben', surname: 'Matter'
  },
  {
    _id: ..., name: 'aen', surname: 'Does'
  },
  {
    _id: ..., name: 'ben', surname: 'ThisField'
  }
]

次に、Benbenが一緒に表示されるように、大文字と小文字を区別しないソートを作成する方法を見てみましょう。

4. Mongoシェルでの大文字と小文字を区別しない並べ替え

4.1. 照合を使用した並べ替え

MongoDBCollationを使ってみましょう。 MongoDB 3.4以降のバージョンでのみ使用可能であり、文字列比較のための言語固有のルールを有効にします。

Collation ICUロケールパラメータは、データベースの並べ替え方法を決定します。 “ en” (英語) locale:を使用してみましょう。

> db.getCollection('users').find({}).collation({locale: "en"}).sort({name:1})

これにより、名前が文字でクラスター化された出力が生成されます。

[
  {
    _id: ..., name: 'aen', surname: 'Does'
  },
  {
    _id: ..., name: 'Aen', surname: 'Not'
  },
  {
    _id: ..., name: 'ben', surname: 'ThisField'
  },
  {
    _id: ..., name: 'Ben', surname: 'Matter'
  }
]

4.2. 集計を使用した並べ替え

Aggregation関数を使用してみましょう。

> db.getCollection('users').aggregate([{
        "$project": {
            "name": 1,
            "surname": 1,
            "lowerName": {
                "$toLower": "$name"
            }
        }
    },
    {
        "$sort": {
            "lowerName": 1
        }
    }
])

$ project 機能を使用して、nameフィールドの小文字バージョンとしてlowerNameフィールドを追加します。 これにより、そのフィールドを使用して並べ替えることができます。 必要な並べ替え順序で、追加のフィールドを持つ結果オブジェクトが表示されます。

[
  {
    _id: ..., name: 'aen', surname: 'Does', lowerName: 'aen'
  },
  {
    _id: ..., name: 'Aen', surname: 'Not', lowerName: 'aen'
  },
  {
    _id: ..., name: 'ben', surname: 'ThisField', lowerName: 'ben'
  },
  {
    _id: ..., name: 'Ben', surname: 'Matter', lowerName: 'ben'
  }
]

5. Javaで大文字と小文字を区別しない並べ替え

Javaで同じメソッドを実装してみましょう。

5.1. 構成ボイラープレートコード

まず、 mongo- java-driver依存関係を追加しましょう。

<dependency>
    <groupId>org.mongodb</groupId>
    <artifactId>mongo-java-driver</artifactId>
    <version>3.12.10</version>
</dependency>

次に、MongoClientを使用して接続しましょう。

MongoClient mongoClient = new MongoClient();
MongoDatabase db = mongoClient.getDatabase("sorting");
MongoCollection<Document> collection = db.getCollection("users");

5.2. Javaで照合を使用して並べ替える

「照合」ソリューションをJavaに実装する方法を見てみましょう。

FindIterable<Document> nameDoc = collection.find().sort(ascending("name"))
  .collation(Collation.builder().locale("en").build());

ここでは、“ en”ロケールを使用して照合を構築しました。 次に、作成したCollationオブジェクトをFindIterableオブジェクトのcollationメソッドに渡しました。

次に、MongoCursorを使用して結果を1つずつ読みましょう。

MongoCursor cursor = nameDoc.cursor();
List expectedNamesOrdering = Arrays.asList("aen", "Aen", "ben", "Ben", "cen", "Cen");
List actualNamesOrdering = new ArrayList<>();
while (cursor.hasNext()) {
    Document document = cursor.next();
    actualNamesOrdering.add(document.get("name").toString());
}
assertEquals(expectedNamesOrdering, actualNamesOrdering);

5.3. Javaでの集約を使用したソート

Aggregationを使用してコレクションを並べ替えることもできます。 JavaAPIを使用してコマンドラインバージョンを再作成しましょう。

まず、 project メソッドを使用して、Bsonオブジェクトを作成します。 このオブジェクトには、Projectionsクラスを使用して名前のすべての文字を小文字に変換することによって計算されるlowerNameフィールドも含まれます。

Bson projectBson = project(
  Projections.fields(
    Projections.include("name","surname"),
    Projections.computed("lowerName", Projections.computed("$toLower", "$name"))));

次に、前のスニペットのBsonsortメソッドを含むリストをaggregateメソッドにフィードします。

AggregateIterable<Document> nameDoc = collection.aggregate(
  Arrays.asList(projectBson,
  sort(Sorts.ascending("lowerName"))));

この場合、前の例と同様に、MongoCursorを使用して結果を簡単に読み取ることができます。

6. 結論

この記事では、MongoDBコレクションの大文字と小文字を区別しない単純な並べ替えを実行する方法を説明しました。

MongoDBシェルでAggregationメソッドとCollationメソッドを使用しました。 最後に、これらのクエリを翻訳し、mongo-java-driverライブラリを使用して単純なJava実装を提供しました。

いつものように、記事の完全なソースコードは、GitHubから入手できます。