2

出会い系アプリに例えることができるアプリを構築しています。

次のような構造のドキュメントがいくつかあります。

$ db.profiles.find().pretty()

[
  {
    "_id": 1,
    "firstName": "John",
    "lastName": "Smith",
    "fieldValues": [
      "favouriteColour|red",
      "food|pizza",
      "food|chinese"
    ]
  },
  {
    "_id": 2,
    "firstName": "Sarah",
    "lastName": "Jane",
    "fieldValues": [
      "favouriteColour|blue",
      "food|pizza",
      "food|mexican",
      "pets|yes"
    ]
  },
  {
    "_id": 3,
    "firstName": "Rachel",
    "lastName": "Jones",
    "fieldValues": [
      "food|pizza"
    ]
  }
]

私がしようとしているのは、1 つ以上の で互いに一致するプロファイルを特定することですfieldValues

したがって、上記の例では、私の理想的な結果は次のようになります。

<some query>

result:
[
  {
    "_id": "507f1f77bcf86cd799439011",
    "dateCreated": "2013-12-01",
    "profiles": [
      {
        "_id": 1,
        "firstName": "John",
        "lastName": "Smith",
        "fieldValues": [
          "favouriteColour|red",
          "food|pizza",
          "food|chinese"
        ]
      },
      {
        "_id": 2,
        "firstName": "Sarah",
        "lastName": "Jane",
        "fieldValues": [
          "favouriteColour|blue",
          "food|pizza",
          "food|mexican",
          "pets|yes"
        ]
      },

    ]
  },
  {
    "_id": "356g1dgk5cf86cd737858595",
    "dateCreated": "2013-12-02",
    "profiles": [
      {
        "_id": 1,
        "firstName": "John",
        "lastName": "Smith",
        "fieldValues": [
          "favouriteColour|red",
          "food|pizza",
          "food|chinese"
        ]
      },
      {
        "_id": 3,
        "firstName": "Rachel",
        "lastName": "Jones",
        "fieldValues": [
          "food|pizza"
        ]
      }
    ]
  }
]

これを map reduce として、または集約フレームワークで行うことを考えました。

いずれにせよ、「結果」はコレクションに永続化されます (上記の「結果」に従って)

私の質問は、どちらがより適しているでしょうか? そして、どこからこれを実装し始めますか?

編集

一言で言えば、モデルは簡単に変更できません。
これは、従来の意味での「プロファイル」とは異なります。

私が基本的に(疑似コードで)やろうとしていることは、次の行に沿っています。

foreach profile in db.profiles.find()
  foreach otherProfile in db.profiles.find("_id": {$ne: profile._id})
    if profile.fieldValues matches any otherProfie.fieldValues
      //it's a match!

明らかに、その種の操作は非常に遅いです。

また、このデータは表示されないことにも言及する価値があるかもしれません。これは文字通り、「照合」に使用される単なる文字列値です。

4

2 に答える 2

10

MapReduce は JavaScript を別のスレッドで実行し、指定したコードを使用してドキュメントの一部を出力および縮小し、特定のフィールドで集計します。この演習は、各「fieldValue」を集計するものとして見ることができます。集約フレームワークでもこれを行うことができますが、別の JavaScript スレッドではなく C++ のサーバーで集約が実行されるため、はるかに高速になります。ただし、集計フレームワークは 16 MB を超えるデータを返す場合があります。その場合、データ セットのより複雑なパーティショニングを行う必要があります。

しかし、問題はこれよりもはるかに単純なようです。プロファイルごとに、特定の属性を共有する他のプロファイルを見つけたいだけです。データセットのサイズとパフォーマンス要件を知らなくても、fieldValues にインデックスがあると仮定して、クエリを実行すると効率的です。その上で、次の単純なループで必要な結果を取得できます。

> db.profiles.find().forEach( function(p) { 
       print("Matching profiles for "+tojson(p));
       printjson(
            db.profiles.find(
               {"fieldValues": {"$in" : p.fieldValues},  
                                "_id" : {$gt:p._id}}
            ).toArray()
       ); 
 }  );

出力:

Matching profiles for {
    "_id" : 1,
    "firstName" : "John",
    "lastName" : "Smith",
    "fieldValues" : [
        "favouriteColour|red",
        "food|pizza",
        "food|chinese"
    ]
}
[
    {
        "_id" : 2,
        "firstName" : "Sarah",
        "lastName" : "Jane",
        "fieldValues" : [
            "favouriteColour|blue",
            "food|pizza",
            "food|mexican",
            "pets|yes"
        ]
    },
    {
        "_id" : 3,
        "firstName" : "Rachel",
        "lastName" : "Jones",
        "fieldValues" : [
            "food|pizza"
        ]
    }
]
Matching profiles for {
    "_id" : 2,
    "firstName" : "Sarah",
    "lastName" : "Jane",
    "fieldValues" : [
        "favouriteColour|blue",
        "food|pizza",
        "food|mexican",
        "pets|yes"
    ]
}
[
    {
        "_id" : 3,
        "firstName" : "Rachel",
        "lastName" : "Jones",
        "fieldValues" : [
            "food|pizza"
        ]
    }
]
Matching profiles for {
    "_id" : 3,
    "firstName" : "Rachel",
    "lastName" : "Jones",
    "fieldValues" : [
        "food|pizza"
    ]
}
[ ]

明らかに、クエリを微調整して、既に一致したプロファイルを除外しないようにすることができます (に変更すること{$gt:p._id}により{$ne:{p._id}}) (表示する出力の形式から判断) 出力形式の要件が柔軟な場合は、ビルトインの集計オプションのいずれかを使用することもできます。

個々の fieldValues を集約するとどうなるかを確認しましたが、悪くはありません。出力がこれと一致する場合に役立つ可能性があります。

> db.profiles.aggregate({$unwind:"$fieldValues"}, 
      {$group:{_id:"$fieldValues", 
              matchedProfiles : {$push:
               {  id:"$_id", 
                  name:{$concat:["$firstName"," ", "$lastName"]}}},
                  num:{$sum:1}
               }}, 
      {$match:{num:{$gt:1}}});
{
    "result" : [
        {
            "_id" : "food|pizza",
            "matchedProfiles" : [
                {
                    "id" : 1,
                    "name" : "John Smith"
                },
                {
                    "id" : 2,
                    "name" : "Sarah Jane"
                },
                {
                    "id" : 3,
                    "name" : "Rachel Jones"
                }
            ],
            "num" : 3
        }
    ],
    "ok" : 1
}

これは基本的に、「各 fieldValue ($unwind) について、一致するプロファイル _id と名前の配列を fieldValue ごとにグループ化し、各 fieldValue が累積する一致数 ($group) を数えてから、それに一致するプロファイルが 1 つしかないものを除外します。

于 2013-05-07T22:51:36.653 に答える