2013-04-12 2 views
3

Хорошо, здесь есть несколько вещей. У меня есть две коллекции: test и test1. Документы в обеих коллекциях имеют поле массива (теги и теги1 соответственно), который содержит некоторые теги. Мне нужно найти пересечение этих тегов. и также извлекают весь документ из коллекции test1, если совпадает хотя бы один тег.Пересечение массива в MongoDB

> db.test.find(); 
{ 
    "_id" : ObjectId("5166c19b32d001b79b32c72a"), 
    "tags" : [ 
      "a", 
      "b", 
      "c" 
    ] 
}   
> db.test1.find(); 
{ 
    "_id" : ObjectId("5166c1c532d001b79b32c72b"), 
    "tags1" : [ 
      "a", 
      "b", 
      "x", 
      "y" 
    ] 
} 
> db.test.find().forEach(function(doc){db.test1.find({tags1:{$in:doc.tags}})}); 

Удивительно, но это ничего не возвращает. Однако, когда я пытаюсь использовать один документ, он работает:

> var doc = db.test.findOne(); 
> db.test1.find({tags1:{$in:doc.tags}}); 
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a", "b", "x", "y" ] } 

Но это часть того, что мне нужно. Мне также нужно пересечение. Так что я попытался это:

> db.test1.find({tags1:{$in:doc.tags}},{"tags1.$":1}); 
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a" ] } 

Но он вернулся только «а», тогда как «а» и «б» оба были в tags1. Возвращает ли позиционный оператор только первое совпадение? Кроме того, использование $in не даст мне перекрестка. Как я могу получить пересечение (должен возвращать «a» и «b»), независимо от того, какой массив сравнивается с другим.

Теперь говорят, что есть оператор, который может это сделать ..

> db.test1.find({tags1:{$intersection:doc.tags}},{"tags1.$":1}); 
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1" : [ "a", "b" ] } 

Мое требование, мне нужно весь массив tags1 PLUS это пересечение, в том же запросе, как это:

> db.test1.find({tags1:{$intersection:doc.tags}},{"tags1":1, "tags1.$":1}); 
{ "_id" : ObjectId("5166c1c532d001b79b32c72b"), "tags1": [ "a", "b", "x", "y" ], 
"tags1" : [ "a", "b" ] } 

Но это недопустимый json. Возможно ли переименование ключа, или это возможно только через структуру агрегации (и через разные коллекции?)? Я попробовал вышеуказанный запрос с $in. Но он вел себя так, как будто он полностью проигнорировал проекцию "tags:1".

PS: Я собираюсь иметь как минимум 10 тыс. Документов в test1 и очень мало (< 10) в тесте. И этот запрос находится в режиме реального времени, поэтому я хочу избежать mapreduce :)

Спасибо за любую помощь!

+2

Чтобы прокомментировать ваш первый вопрос, «db.test1.find ({tags1: {$ in: doc.tags}})« вернет курсор. Чтобы напечатать на экране, вы должны добавить что-то вроде следующего для второго find: .forEach (function (doc) {print (tojson (doc));}) –

+0

Спасибо @JamesWahlin! Я знал, что это что-то глупое: P Ваш быстрый комментарий позволил мне хотя бы продолжить поиск решения. –

ответ

0

Если вы хотите иметь это в режиме реального времени, вы должны рассмотреть, чтобы отойти от ServerSide Javascript, который работают только с одним потоком и должен быть довольно медленным (однопоточный) (это уже не относится к v2.4, http://docs.mongodb.org/manual/core/server-side-javascript/).

Оператор позиционирования возвращает только первое совпадающее/текущее значение. Не зная внутренней реализации, с точки зрения производительности даже нет смысла искать дополнительные критерии соответствия, если документ уже был оценен как совпадение. Поэтому я сомневаюсь, что вы можете пойти на это.

Я не знаю, нужен ли вам декартовой продукт для вашего поиска, но я бы рассмотрел возможность присоединения к нескольким тестам одного документа в одном, а затем несколько $ искать его на test1, возвращая все соответствующие документы. На вашей локальной машине вы можете иметь несколько потоков, которые генерируют пересечение для вашего документа.

В зависимости от того, как часто меняется ваш тест1 и коллекция тестов, вы выполняете этот запрос, вы можете предварительно рассчитать эту информацию.Это позволит легко выполнить запрос в поле, которое содержит информацию о пересечении.

документ является недействительным, потому что у вас есть два поля имен tags1

+0

Почему серверный JS имеет значение? он не делает серверные JS в своих попытках - оболочка запускает js локально на клиентской машине. –

+0

Да, вы правы, javascript выполняется локально. Здесь я ошибался. – philnate

0

Монго не имеет присущую способность извлекать перекрестков массива. Если вам действительно нужно использовать ad-hoc-запрос, получите перекресток на стороне клиента.

С другой стороны, рассмотрите возможность использования Map-Reduce и сохранения его в виде коллекции. Вы можете увеличить возвращаемые объекты в разделе finalize, чтобы добавить пересекающиеся теги. Cron MR запускается каждые несколько секунд. Вы получаете выгоду от постоянной коллекции, которую вы можете запросить на стороне клиента.