MongoDB
MongoDBでドキュメントをクエリおよびフィルタリングする方法
はじめに
ドキュメントのクエリは、MongoDB内で様々な操作を行うために必要な必須スキルです。必要なドキュメントを効果的に取得し、データベース内の既存情報を更新し、ドキュメント間の共通点と相違点を理解するために、クエリを実行できる必要があります。
このガイドでは、MongoDBでクエリを構成して要件に応じてドキュメントを取得する方法の基本を説明します。クエリが一般的なレベルでどのように機能するかを示し、その後、条件を評価して結果を絞り込むのに役立つMongoDBが提供するさまざまな演算子について詳しく説明します。
MongoDBを使用している場合は、PrismaのMongoDBコネクタをチェックしてください!Prisma Clientを使用して、本番MongoDBデータベースを安心して管理できます。
MongoDBとPrismaの作業を開始するには、ゼロからの入門ガイド、または既存のプロジェクトに追加する方法をチェックしてください。
例のコレクションを作成する
この記事全体を通して、school
というデータベース内に保存されているstudents
というコレクションとteachers
というコレクションを使用します。
次のコマンドを使用して、例のデータベースを作成し、コレクションにデータを投入できます。
use schooldb.students.insertMany([{first_name: "Ashley",last_name: "Jenkins",dob: new Date("January 08, 2003"),grade_level: 8},{first_name: "Brian",last_name: "McMantis",dob: new Date("September 18, 2010"),grade_level: 2},{first_name: "Leah",last_name: "Drake",dob: new Date("October 03, 2009")},{first_name: "Naomi",last_name: "Pyani"},{first_name: "Jasmine",last_name: "Took",dob: new Date("April 11, 2011")},{first_name: "Michael",last_name: "Rodgers",dob: new Date("February 25, 2008"),grade_level: 6},{first_name: "Toni",last_name: "Fowler"}])db.teachers.insertMany([{first_name: "Nancy",last_name: "Smith",subjects: ["vocabulary","pronunciation"]},{first_name: "Ronald",last_name: "Taft",subjects: ["literature","grammar","composition"]},{first_name: "Casey",last_name: "Meyers",subjects: ["literature","composition","grammar"]},{first_name: "Rebecca",last_name: "Carrie",subjects: ["grammar","literature"]},{first_name: "Sophie",last_name: "Daggs",subjects: ["literature","composition","grammar","vocabulary","pronunciation"]}])
基本的なクエリ構文
ドキュメントを含む2つのコレクションができたので、個々のドキュメントまたはドキュメントのグループを取得する方法を試すことができます。MongoDBからドキュメントをフェッチする主な方法は、対象のコレクションでfind()
メソッドを呼び出すことです。
たとえば、students
コレクションからすべてのドキュメントを収集するには、引数なしでfind()
を呼び出します。
db.students.find()
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
出力をより読みやすくするために、find()
の後にpretty()
メソッドを連鎖させることもできます。
db.<collection>.find().pretty()
{"_id" : ObjectId("60e8743b4655cbf49ff7cb83"),"first_name" : "Ashley","last_name" : "Jenkins","dob" : ISODate("2003-01-08T00:00:00Z"),"grade_level" : 8}{"_id" : ObjectId("60e875d54655cbf49ff7cb84"),"first_name" : "Brian","last_name" : "McMantis","dob" : ISODate("2010-09-18T00:00:00Z"),"grade_level" : 2}{"_id" : ObjectId("60e875d54655cbf49ff7cb85"),"first_name" : "Leah","last_name" : "Drake","dob" : ISODate("2009-10-03T00:00:00Z")}{"_id" : ObjectId("60e877914655cbf49ff7cb86"),"first_name" : "Naomi","last_name" : "Pyani"}{"_id" : ObjectId("60e8792d4655cbf49ff7cb87"),"first_name" : "Jasmine","last_name" : "Took","dob" : ISODate("2011-04-11T00:00:00Z")}{"_id" : ObjectId("60e8792d4655cbf49ff7cb88"),"first_name" : "Michael","last_name" : "Rodgers","dob" : ISODate("2008-02-25T00:00:00Z"),"grade_level" : 6}{"_id" : ObjectId("60e8792d4655cbf49ff7cb89"),"first_name" : "Toni","last_name" : "Fowler"}
各ドキュメントに_id
フィールドが追加されていることがわかります。MongoDBでは、コレクション内の各ドキュメントに一意の_id
が必要です。オブジェクト作成時に提供しない場合、自動的に追加されます。このIDを使用して、単一のオブジェクトを確実に取得できます。
db.student.find({_id : ObjectId("60e8792d4655cbf49ff7cb89")})
{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
等価性で結果をフィルタリングする
フィールドと値のペアを指定するオブジェクトを提供することで、等価性をチェックして結果をフィルタリングできます。
たとえば、「Brian」という名前の生徒のリストは、次のクエリで取得できます。
db.students.find({first_name: "Brian"})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
フィールドと値の表記を使用して指定するすべての条件は、等価性クエリとして解釈されます。複数のフィールドを指定した場合、ドキュメントが一致するにはすべての値が等しい必要があります。
たとえば、以前と同じ等価一致を実行しても、grade_level
を3と含めると、ドキュメントは返されません。
db.students.find({first_name: "Brian", grade_level: 3})
比較演算子を使用したフィルタリング
単純な等価フィルタリングは便利ですが、表現できる範囲はかなり限られています。他の種類の比較のために、MongoDBは様々な比較演算子を提供しており、他の方法でクエリを実行できます。
利用可能な比較演算子の基本的な機能は、他のプログラミング言語を使用していれば、おそらくかなり馴染み深いでしょう。ほとんどの演算子は、フィールド名に演算子と比較したい値を含むオブジェクトを渡すことで機能します。以下のようにです。
<field_name>: { <operator>: <value_to_compare_against> }
等しい
$eq
演算子は、提供された値とドキュメント内のフィールド値との間の等価性をチェックします。ほとんどの場合、これは前述の等価比較と同じ機能を持っています。
例えば、「Brian」という名前の生徒に対する同じクエリを以下のように表現できます。
db.students.find({first_name: { $eq: "Brian" }})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }
等しくない
提供された値と**等しくない**ドキュメントをクエリすることもできます。この演算子は$ne
です。
例えば、grade_level
が設定されているすべての生徒を見つける一つの方法は、そのフィールドがnull
に設定されていないエントリを検索することです。
db.students.find({grade_level: { $ne: null }})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
より大きい
$gt
演算子を使用すると、フィールド値が提供された参照番号よりも大きいドキュメントをクエリできます。
例えば、6年生より上の学年の生徒のすべての記録を見つけるには、次のように入力できます。
db.students.find({grade_level: { $gt: 6 }})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }
以上
$gte
演算子は、提供された値と同じかそれよりも大きい値をクエリとして表現します。
上記のクエリと同じものを実行できますが、6年生の生徒も含むには、次のように入力します。
db.students.find({grade_level: { $gte: 6 }})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
未満
$lt
演算子を使用して、指定された値より小さい値を見つけます。
例えば、2010年1月1日以前の生年月日を表示するには、次のように入力します。
db.students.find({dob: { $lt: new Date("January 1, 2010") }})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
以下
$lte
演算子は、提供された参照値以下である値をチェックします。
例えば、6年生以下の生徒を見つけるには、次のように入力します。
db.students.find({grade_level: { $lte: 6 }})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
値のグループのいずれかに一致する
$in
演算子は$eq
等価演算子のように機能しますが、配列で複数の可能な値を提供できます。たとえば、フィールド値が8に等しいかどうかをチェックする代わりに、値が[8, 9, 10, 11]
のいずれかであるかどうかをチェックできます。
$in
演算子は正規表現でも機能します。たとえば、名が「i」または「e」で終わるすべての生徒を次のように入力して見つけることができます。
db.students.find({first_name: {$in: [/i$/,/e$/]}})
{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
値のグループのいずれにも一致しない
上記の逆の手順は、指定された配列に値が含まれていないすべてのドキュメントを見つけることです。その演算子は$nin
です。
たとえば、名が「i」または「e」で**終わらない**すべての生徒を次のように入力して見つけることができます。
db.students.find({first_name: {$nin: [/i$/,/e$/]}})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
論理演算子を使用したフィルタリング
より複雑なクエリを作成するには、論理演算子を使用して複数の条件を構成できます。論理演算子は、式を含むオブジェクト、または複数の式のオブジェクトを含む配列を渡すことで機能します。
論理AND演算子
$and
演算子は、渡されたすべての式を満たす結果を返します。$and
式内のすべての式がtrueと評価される場合にのみ、結果が返されます。
たとえば、$and
を使用して、生年月日と学年の両方が設定されている生徒をクエリできます。
db.students.find({$and: [{ dob: { $ne: null } },{ grade_level: { $ne: null } }]})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
論理OR演算子
$or
演算子は論理OR計算を実行します。渡された式の**いずれか**が真であれば、句全体が満たされたと見なされます。
例えば、上記でクエリしたフィールドのいずれかが欠けている生徒をクエリするためにこれを使用できます。
db.students.find({$or: [{ dob: { $eq: null } },{ grade_level: { $eq: null } }]})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
論理NOT演算子
$not
演算子は、渡された式の値を否定します。$not
は単項演算子であるため、式の配列に対してではなく、演算子式を直接定義する単一の式に対して機能します。
これにより、以前の演算子とは少し異なる構文になります。完全なフィールドと値の式をラップする代わりに、$not
をフィールドマッチの値の一部として使用し、完全な式ではなく**演算子式**のみを引数として受け取ります(フィールド名は$not
式の内部ではなく外部にあります)。
例えば、2010年より前の誕生日を持たない全ての生徒を次のように入力して見つけることができます。これは、dob
エントリが2010年より小さいかどうかをチェックするのとは異なり、そのフィールドが全く設定されていないドキュメントも返します。
db.students.find({dob: {$not: {$lt: new Date("January 1, 2010")}}})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
論理NOR演算子
$nor
演算子はオブジェクトの配列を受け取り、それらのオブジェクトで指定された条件の**いずれにも**一致しないドキュメントを返します。すべての条件に失敗したドキュメントのみが返されます。
たとえば、6年生ではなく、姓が「s」で終わらない生徒のドキュメントを取得したい場合は、次のように入力できます。
db.students.find({$nor: [{ grade_level: 6 },{ last_name: /s$/ }]})
{ "_id" : ObjectId("60e875d54655cbf49ff7cb85"), "first_name" : "Leah", "last_name" : "Drake", "dob" : ISODate("2009-10-03T00:00:00Z") }{ "_id" : ObjectId("60e877914655cbf49ff7cb86"), "first_name" : "Naomi", "last_name" : "Pyani" }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb87"), "first_name" : "Jasmine", "last_name" : "Took", "dob" : ISODate("2011-04-11T00:00:00Z") }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb89"), "first_name" : "Toni", "last_name" : "Fowler" }
存在によるフィルタリング
その他のテスト方法としては、フィールドまたは値の状態に基づいています。
例えば、$exists
フィルターは、ドキュメント内のフィールドの存在をチェックします。$exists
をtrue
またはfalse
に設定することで、取得するドキュメントを決定できます。
例えば、学年が設定されている生徒のドキュメントを見つけたい場合は、次のように入力します。
db.students.find({grade_level: { $exists: true }})
{ "_id" : ObjectId("60e8743b4655cbf49ff7cb83"), "first_name" : "Ashley", "last_name" : "Jenkins", "dob" : ISODate("2003-01-08T00:00:00Z"), "grade_level" : 8 }{ "_id" : ObjectId("60e875d54655cbf49ff7cb84"), "first_name" : "Brian", "last_name" : "McMantis", "dob" : ISODate("2010-09-18T00:00:00Z"), "grade_level" : 2 }{ "_id" : ObjectId("60e8792d4655cbf49ff7cb88"), "first_name" : "Michael", "last_name" : "Rodgers", "dob" : ISODate("2008-02-25T00:00:00Z"), "grade_level" : 6 }
配列の特性に基づいたフィルタリング
ドキュメントを、それが持つ配列を通じてクエリすることもできます。配列要素やその他の特性に基づいて一致させるために使用できる多くの演算子があります。
必須要素の指定
$all
演算子は、指定された要素を**すべて**含む配列を持つドキュメントを返します。
例えば、作文と文法の両方を教える教師だけを取得したい場合、次のように入力できます。
db.teachers.find({subjects: {$all: [ "composition", "grammar" ]}})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }
1つの要素に対する複数の要件
$elemMatch
演算子は、テストされている配列が、提供されたすべての条件を満たす要素を少なくとも1つ含んでいる場合に、ドキュメントを返します。
あまり役立たない例ですが、「literature」と「vocabulary」の間でアルファベット順に並ぶ科目を教える教師のドキュメントを返すには、次のように入力します。
db.teachers.find({subjects: {$elemMatch: {$gt: "literature",$lt: "vocabulary"}}})
{ "_id" : ObjectId("60eddca65eb74f5c676f3baa"), "first_name" : "Nancy", "last_name" : "Smith", "subjects" : [ "vocabulary", "pronunciation" ] }{ "_id" : ObjectId("60eddca65eb74f5c676f3bae"), "first_name" : "Sophie", "last_name" : "Daggs", "subjects" : [ "literature", "composition", "grammar", "vocabulary", "pronunciation" ] }
「pronunciation」を教える両方の教師がここにリストされています。それが両方の条件を満たす唯一の要素だからです。
配列サイズによるクエリ
最後に、$size
演算子を使用して、特定のサイズのドキュメントをクエリできます。たとえば、3つの科目を教えているすべての教師を見つけるには、次のように入力します。
db.teachers.find({subjects: { $size: 3 }})
{ "_id" : ObjectId("60eddca65eb74f5c676f3bab"), "first_name" : "Ronald", "last_name" : "Taft", "subjects" : [ "literature", "grammar", "composition" ] }{ "_id" : ObjectId("60eddca65eb74f5c676f3bac"), "first_name" : "Casey", "last_name" : "Meyers", "subjects" : [ "literature", "composition", "grammar" ] }
結論
このガイドでは、MongoDBデータベースでドキュメントをクエリする方法について説明しました。find()
メソッドの基本的な動作と、その出力をより読みやすくする方法を説明しました。その後、MongoDBが提供する多くの演算子を見て、関心のあるドキュメントの正確なパラメーターを指定する方法を学びました。
結果を絞り込み、仕様に一致するドキュメントを選択するためにクエリを構成する方法を理解することは、データの読み取りと更新の両方で重要です。演算子をさまざまな方法で連結する方法に慣れることで、さまざまな種類のドキュメントに一致する複雑な要件を表現できます。
MongoDBを使用している場合は、PrismaのMongoDBコネクタをチェックしてください!Prisma Clientを使用して、本番MongoDBデータベースを安心して管理できます。
MongoDBとPrismaの作業を開始するには、ゼロからの入門ガイド、または既存のプロジェクトに追加する方法をチェックしてください。
FAQ
$gt
演算子をfindステートメント内で使用して、特定の日付より大きい日付フィールドを持つドキュメントを見つけることができます。
基本的な構文は次のようになります。
db.collection.find( { <Field Name>: { $gt:ISODate('Date here') } } )
MongoDBデータベースクエリプロファイラは、実行中のmongod
インスタンスに対して実行されたデータベースコマンドに関する詳細情報を収集するツールです。
これには、CRUD操作だけでなく、構成コマンドや管理コマンドも含まれます。これは、遅い操作をソートしようとするときに特に役立ちます。
基本的な構文は次のようになります。
{ $strLenCP: "Hello World!" }
この特定の文字列は、12
という値を返します。
より大きなコレクション内でフィールドのユニークな値のみをクエリするには、distinct()
メソッドを使用できます。
基本的な構文は次のようになります。
db.collection.distinct("<Field_Name>")
これは、特定のフィールドに対してコレクション内のすべてのユニークな値を重複なく返します。
mongoexport
コマンドラインツールを使用して、データベースの内容をJSONにエクスポートできます。これはmongo
シェル内ではなく、コマンドラインで実行する必要があることに注意してください。
基本的な構文は次のようになります。ここでは、コレクションのエクスポートの出力をjson
に指定しています。
mongoexport --collection=events --db=reporting --out=events.json