MongoDB
MongoDBでドキュメントをクエリおよびフィルタリングする方法
はじめに
ドキュメントのMongoDBでのクエリは、さまざまな操作を行うために不可欠なスキルです。必要なドキュメントを効果的に取得し、データベース内の既存の情報を更新し、ドキュメント間の共通点と相違点を理解するためには、クエリを実行できる必要があります。
このガイドでは、MongoDBのクエリを作成して、要件に応じてドキュメントを取得する方法の基本について説明します。クエリが一般的なレベルでどのように機能するかを示し、MongoDBが条件を評価して結果を絞り込むために提供するさまざまな演算子について説明します。
MongoDBを使用している場合は、PrismaのMongoDBコネクタをチェックしてください!Prisma Clientを使用すると、本番環境のMongoDBデータベースを自信を持って管理できます。
MongoDBとPrismaの使用を開始するには、ゼロから始めるガイド、または既存のプロジェクトに追加する方法を確認してください。
コレクション例の作成
この記事全体を通して、students
というコレクションと、teachers
というコレクションを使用します。どちらもschool
というデータベース内に保存されています。
次のコマンドを使用して、データベース例を作成し、コレクションにデータを投入できます。
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
を指定しない場合、MongoDBが追加します。この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
が設定されているすべての学生を見つける1つの方法は、フィールドが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計算を実行します。渡された式のいずれかがtrueの場合、句全体が満たされたと見なされます。
たとえば、これを使用して、上記でクエリしたフィールドのいずれかが欠落している学生をクエリできます。
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年より前の誕生日がないすべての学生を見つけるには、次のように入力します。これは、2010年より小さいdob
エントリをチェックするのとは異なり、フィールドがまったく設定されていないドキュメントも返します。
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つ含まれている場合にドキュメントを返します。
あまり役に立たない例として、「文学」と「語彙」の間にある科目を教える教師のドキュメントを返すには、次のように入力できます。
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" ] }
「発音」を教えている教師の両方がここにリストされています。これは、両方の条件を満たす唯一の要素であるためです。
配列サイズによるクエリ
最後に、$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
findステートメント内で$gt
演算子を使用して、日付フィールドが特定の日付よりも大きいドキュメントを見つけることができます。
基本的な構文は次のようになります。
db.collection.find( { <Field Name>: { $gt:ISODate('Date here') } } )
MongoDBデータベースクエリプロファイラーは、実行中のmongod
インスタンスに対して実行されたデータベースコマンドに関する詳細情報を収集するツールです。
これには、CRUD操作だけでなく、構成コマンドや管理コマンドも含まれます。これは、低速な操作をソートしようとするときに特に役立ちます。
基本的な構文は次のようになります。
{ $strLenCP: "Hello World!" }
この特定の文字列は、12
の値を返します。
より大きなコレクション内のフィールドの一意の値のみをクエリするには、distinct()
メソッドを使用できます。
基本的な構文は次のようになります。
db.collection.distinct("<Field_Name>")
これにより、特定のフィールドのコレクション内の一意の値がすべて重複なしで返されます。
データベースの内容をJSONにエクスポートするには、mongoexport
コマンドラインツールを使用できます。これはmongo
シェル内ではなく、コマンドラインで実行する必要があることに注意することが重要です。
基本的な構文は次のようになります。コレクションのエクスポートの出力をjson
に指定します。
mongoexport --collection=events --db=reporting --out=events.json