0

私はnode.jsとmongodbを学習中です。多くのチュートリアルの推奨により、mongoose を利用して mongo とのやり取りを支援しています。問題を複雑にするために、私は重要な RDMS のバックグラウンドを持っており、SQL レンズを通して mongodb を見たいという私の心の欲求と戦うために最善を尽くしています。

現在、サブドキュメントのクエリの概念に苦労しています。サブドキュメントのプロパティに基づいて親ドキュメントをクエリする方法はわかりましたが、子ドキュメントを直接クエリしてすべての親ドキュメント (タイプに関係なく) をクエリする方法がわかりません。説明するために、次の不自然なスキーマの例があります。

// subdocument
var CategorySchema = new Schema({
    name: { type: String, required: true }
});

var IpSchema = new Schema({
  ip_address: { type: String, required: true, index: true }
  ,categories: [CategorySchema]
});

var DomainSchema = new Schema({
  domain_name: { type: String, required: true, index: true }
  ,categories: [CategorySchema]
});

var ip = mongoose.model('Ip', IpSchema);
var domain = mongoose.model('Domain', DomainSchema);
var category = mongoose.model('Category', CategorySchema);

上記のスキーマは、保存された各ドメインと IP ドキュメントにカテゴリのサブドキュメント配列を埋め込みます。カテゴリ名に基づいてドメインと IP を個別に取得することは簡単ですが、特定のカテゴリに関連付けられたすべてのドメインと IP を一度に取得することは困難です。以下のコードは、私がこれを信じる理由の概要を示しています。

category.find(function (err, tcs) {
    console.log(tcs); // contains an empty set because no categories stored here
});

ip.find({ 'categories.name' : req.params.category }, function(err, ips) {
    console.log(ips); // contains all parent documents w/ subdocument name
});

domain.find({ 'categories.name' : req.params.category }, function(err, ips) {
    console.log(ips); // contains all parent documents w/ subdocument name
});

これで、上記のクエリの結果を組み合わせることができましたが、これは脆弱な可能性があるように思われます (ますます多くのドキュメントでカテゴリを再利用すると仮定すると)。カテゴリを保存してから、カテゴリ ID を介して参照を埋め込む必要がありますか? 読み取りを最適化するために書き込みを行うと、チャーンが増加するようです。残念ながら、私の Googlefu では、タグ付けスキームのチュートリアルやベスト プラクティスを見つけることができませんでした。また、私が物事を過度に複雑にしている可能性もあります。

共有サブドキュメントに基づいて異なる親ドキュメントを取得する最良の方法は何ですか?

4

1 に答える 1

2

私の知る限り、mongo クエリは 1 つのコレクションに対して実行する必要があります。これはマングースの事実ではなく、mongodb 自体の事実です。この事実を考えると、試すことができるいくつかの可能な設計があります。それぞれに異なるトレードオフがあるため、アプリケーションにとって重要なクエリを理解し、それに応じて選択する必要があります。

1) IP とドメインの両方を 1 つのコレクションに格納しますが、各ドキュメントにはtypeプロパティと対応するプロパティがあります。

Mongoose は、この使用パターンを容易にするようには設定されていません。コレクションの大部分が同種のドキュメントを保持している場合、Mongoose は最適に機能します。これはmongodb自体にも当てはまりますが、それほどではありません。推奨されませんが、使用パターンが本当にこれを必要とする場合は問題外ではありません。

2)複数のコレクションに対して同じクエリを並行して実行します。以下にこれを行うコードがあります。の内部へのかなり厄介なハックですMongoose.Queryが、機能します。

.

var _                 = require('underscore');
var async             = require('async');
function multiModelFind(query, models, outerCallback) {
  var queries = _.map(models, function (Model) {
    var otheModelQuery = new Query();
    var state = _.pick(query,
      '_conditions',
      '_fields',
      '_updateArg',
      'op',
      'options',
      'safe'
   );
    state.model = Model;
    _.extend(otheModelQuery, state);
    return otheModelQuery;
  });
  async.map(queries, function (query, callback) {
    query.exec(callback);
  }, function (error, models) {
    outerCallback(error, _.flatten(models));
  });
}

使用例:

var query = IP.find({"categories.name": "foo");
multiModelfind(query, [IP, Domain], function (error, ipsAndDomains) {/*...*/});

これは少数のコレクションでは実行可能だと思いますが、少数を超える場合は、おそらくオプション 3 に移動する必要があります。

3)Categorizedコレクションごとに 1 つの名前付きプロパティを持つスキーマを使用してコレクションを 作成しました。これは、マングースを持つ ObjectId であり、「結合された」レコードをロードするためにref使用されます。.populate()これは、リレーショナル データベースの結合テーブルとほとんど同じです。

{
    category: {type: ObjectId, ref: 'Category'},
    ip: {type: ObjectId, ref 'IP'},
    domain: {type: ObjectId, ref 'Domain'},
}

これらのプロパティのうち 2 つのみのレコードごとに、Categorized実際には非 null になり、.populate('ip').populate('domain')各クエリに対して実行します。コレクションに対して 1 つのクエリがあり、一致したドキュメントごとCategorizedに 1 つのインデックス クエリがあります。_idカテゴリの名前が単なるキーワード タグである場合は、カテゴリの名前を直接保存することもできます。その場合、最初にカテゴリの ObjectId を名前で検索する必要はありません。

于 2012-12-10T03:42:13.087 に答える