索引
索引を適切に使用することは、優れたパフォーマンスを実現するために不可欠です。MongoDBのためのOracle Database APIを実行する場合は、MongoDB createIndexesコマンドを発行して索引を作成します。ほとんどのMongoDB索引タイプは、機能サポートにリストされている制限に従ってサポートされています。
索引を作成すると、MongoDBのためのOracle Database APIによって対応するSQL create json index文が生成されます。次に例を示します:
db.employees.insertMany([
{"_id": 1, "name" : "SMITH", "job" : "CLERK", "sal" : 800},
{"_id": 2, "name" : "ALLEN", "job" : "SALESMAN", "sal" : 1600},
{"_id": 3, "name" : "ALLEN", "job" : "CLERK", "sal" : 1250},
{"_id": 4, "name" : "WARD", "job" : "CLERK", "sal" : 1300}
]);
db.employees.createIndex({"job":1}, {"name":"job_index"});
この例では、4つのドキュメントを含むemployeesコレクションを作成し、jobフィールドに索引を追加します。SQLから同じ索引を作成するには、create json index文を使用します:
db.aggregate([{$sql:`
create json index job_index on employees e (f.data.job)
`}]);
findやaggregateなどのMongoDBコマンドおよびSQL問合せでは、これらの索引を使用できます。explainコマンドを使用して、特定のコマンドが索引を使用するかどうかを確認します。例を続けます:
db.employees.find({job:"SALESMAN"});
[ { _id: 2, name: 'ALLEN', job: 'SALESMAN', sal: 1600 } ]
scott> db.employees.find({job:"SALESMAN"}).explain();
{
queryPlanner: {
namespace: 'scott.employees',
parsedQuery: { job: 'SALESMAN' },
rewrittenQuery: { job: { '$stringOnly': 'SALESMAN' } },
generatedSql: `select "DATA",rawtohex("RESID"),"ETAG" from "SCOTT"."employees" where JSON_EXISTS("DATA",'$?(@.job.stringOnly() == $B0)' passing ? as "B0" type(strict))`,
winningPlan: ' Plan Hash Value : 3517912025 \n' +
'\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'| Id | Operation | Name | Rows | Bytes | Cost | Time |\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'| 0 | SELECT STATEMENT | | 1 | 24501 | 2 | 00:00:01 |\n' +
'| 1 | TABLE ACCESS BY INDEX ROWID BATCHED | employees | 1 | 24501 | 2 | 00:00:01 |\n' +
'| * 2 | INDEX RANGE SCAN (MULTI VALUE) | $ora:employees.job_index | 1 | | 1 | 00:00:01 |\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'\n' +
'Predicate Information (identified by operation id):\n' +
'------------------------------------------\n' +
`* 2 - access(JSON_QUERY("DATA" /*+ LOB_BY_VALUE */ FORMAT OSON , '$."job"[*]' RETURNING ANY ORA_RAWCOMPARE ASIS WITHOUT ARRAY WRAPPER ERROR ON ERROR PRESENT ON EMPTY NULL ON MISMATCH TYPE(LAX)\n` +
' MULTIVALUE)=SYS_CONS_ANY_SCALAR(:1, 4))\n' +
'\n' +
'\n' +
'Notes\n' +
'-----\n' +
'- Dynamic sampling used for this statement ( level = 2 )\n' +
'\n'
},
serverInfo: { host: 'localhost', port: 27017, version: '7.0.22' },
ok: 1
}
findコマンドは、jobフィールドがSALESMANと等しいドキュメントを取得します。explainコマンドは、生成されたSQLおよび問合せ計画を表示し、計画でjob索引($ora:employees.job_index)が使用されていることを確認します。
MongoDBコマンドを使用して作成された索引は、SQL問合せでも使用できます。例を続けます:
db.aggregate([{ $sql: `
select data
from employees e
where e.data.job = 'SALESMAN'
` }]);
[ { _id: 2, name: 'ALLEN', job: 'SALESMAN', sal: 1600 } ]
db.aggregate([{ $sql: `
select data
from employees e
where e.data.job = 'SALESMAN'
` }]).explain();
{
queryPlanner: {
namespace: 'scott.1',
parsedQuery: {},
generatedSql: "\n select data\n from employees e\n where e.data.job = 'SALESMAN'\n",
winningPlan: ' Plan Hash Value : 3517912025 \n' +
'\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'| Id | Operation | Name | Rows | Bytes | Cost | Time |\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'| 0 | SELECT STATEMENT | | 1 | 22489 | 1 | 00:00:01 |\n' +
'| * 1 | TABLE ACCESS BY INDEX ROWID BATCHED | employees | 1 | 22489 | 1 | 00:00:01 |\n' +
'| * 2 | INDEX RANGE SCAN (MULTI VALUE) | $ora:employees.job_index | 1 | | 1 | 00:00:01 |\n' +
'---------------------------------------------------------------------------------------------------------\n' +
'\n' +
'Predicate Information (identified by operation id):\n' +
'------------------------------------------\n' +
`* 1 - filter(JSON_VALUE("F"."DATA" /*+ LOB_BY_VALUE */ FORMAT OSON , '$.job' RETURNING VARCHAR2(4000) NULL ON ERROR TYPE(STRICT) )='SALESMAN')\n` +
`* 2 - access(JSON_QUERY("DATA" /*+ LOB_BY_VALUE */ FORMAT OSON , '$."job"[*]' RETURNING ANY ORA_RAWCOMPARE ASIS WITHOUT ARRAY WRAPPER ERROR ON ERROR PRESENT ON EMPTY NULL ON MISMATCH TYPE(LAX)\n` +
" MULTIVALUE)=SYS_CONS_ANY_SCALAR('SALESMAN', 4))\n" +
'\n' +
'\n' +
'Notes\n' +
'-----\n' +
'- Dynamic sampling used for this statement ( level = 2 )\n' +
'\n' +
'\n'
},
ok: 1
}
$sqlステージは、job属性がSALESMANと等しいドキュメントを返すSQL問合せを評価します。実行計画は、SQLがMongoDB createIndexesコマンドによって作成されたjob索引も使用することを示しています。MongoDBコマンドとSQL問合せは、同じ索引を共有します。
索引のヒント
MongoDBヒントを使用して、findおよびaggregationコマンドの索引選択に影響を与えます。索引名または索引仕様ドキュメントを指定します。次に例を示します:
db.employees.insertMany([
{"_id": 1, "name" : "SMITH", "job" : "CLERK", "sal" : 800},
{"_id": 2, "name" : "ALLEN", "job" : "SALESMAN", "sal" : 1600},
{"_id": 3, "name" : "ALLEN", "job" : "CLERK", "sal" : 1250},
{"_id": 4, "name" : "WARD", "job" : "CLERK", "sal" : 1300}
]);
db.employees.createIndex({"job":1});
db.employees.createIndex({"name":1});
db.employees.find({"job":"CLERK", "name":"ALLEN"});
[ { _id: 3, name: 'ALLEN', job: 'CLERK', sal: 1250 } ]
db.employees.find({"job":"CLERK", "name":"ALLEN"}).explain();
{
queryPlanner: {
winningPlan:
...
'| * 2 | INDEX RANGE SCAN (MULTI VALUE) | $ora:employees.job_1 '
...
}
}
db.employees.find({"job":"CLERK", "name":"ALLEN"}).hint("name_1").explain();
{
queryPlanner: {
winningPlan:
...
'| * 2 | INDEX RANGE SCAN (MULTI VALUE) | $ora:employees.name_1 '
...
}
}
db.employees.find({"job":"CLERK", "name":"ALLEN"}).hint({"name":1}).explain();
{
queryPlanner: {
winningPlan:
...
'| * 2 | INDEX RANGE SCAN (MULTI VALUE) | $ora:employees.name_1 '
...
}
}
この例では、4つのドキュメントを含むemployeesコレクションを作成し、jobおよびnameフィールドに索引を定義します。両方のフィールドをフィルタする問合せでは、デフォルトでjob索引が使用されます。この例では、最初に索引名"name_1"を持つヒントを適用してから、索引仕様ドキュメント{ "name": 1 }を使用して同じヒントを再度適用します。
SQLから索引を作成するか、createIndexesの使用時にカスタム名を割り当てる場合は、その正確な名前をヒントに指定します(たとえば、$ora:employees.job_index)。{ name: 1 }などの索引仕様ドキュメントは、デフォルト名を保持するMongoDB索引(name_1など)にのみ適用されます。
索引のネーミング
索引名の一意性ルールは、Oracle DatabaseとMongoDBで異なります。MongoDBでは、索引名はコレクション内で一意であるため、同じデータベース内の異なるコレクションで索引名を共有できます。Oracle Databaseでは、索引名はスキーマ内で一意である必要があるため、同じスキーマ内のコレクションで同じ索引名を再利用することはできません。競合を回避するために、MongoDBのためのOracle Database APIでは、生成された索引名に$ora:<collection name>を使用して接頭辞を付けます。listIndexesを実行すると、レスポンスにはローカル名と接頭辞付きの完全修飾名の両方が含まれます。例を続けます:
db.employees.getIndexes();
[
{
v: 2,
fullName: '$ora:employees.job_1',
name: 'job_1',
key: { job: 1 }
},
{
v: 2,
fullName: '$ora:employees.name_1',
name: 'name_1',
key: { name: 1 }
},
{ v: 2, key: { _id: 1 }, name: '_id_' }
]
employeesコレクションのlistIndexes出力には、生成された接頭辞を含むfullNameフィールドと、接頭辞を含まないnameフィールドが表示されます。