2012-10-14 3 views
8

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

Я хотел получить записи, что значение (целое) массива находится в запросе. Например:

Запись 1:

{"name" : "Mango Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}]} 

запись 2:

{"name" : "Mango Banana Shake", 
"ingredients" : [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "banana"}]} 

запись 3:

{"name" : "Milk Shake", 
"ingredients" : [{"type" : "milk", "name" : "soy milk"}]} 

, тогда у меня бы появился запрос примерно

{"ingredients" : {$all : [{"type" : "fruit", "name" : "mango"}, 
          {"type" : "milk", "name" : "soy milk"}, 
          {"type" : "fruit", "name" : "strawberry"}]}} 

потому что у меня есть манго, соевое молоко и клубника. Поэтому я хотел знать, какие тряски я могу сделать. По-видимому, это ничего не возвращает, потому что запрос не может иметь лишний материал. Если я использую $in, тогда все вернется, но я не смогу сделать манго-банановый коктейль, потому что у меня нет банана.

Так что мне нужен только первый и последний. Есть идеи? Цените это :)

+0

Итак, вы хотите сформировать запрос из тех ингредиентов, которые у вас есть под рукой, тем самым возвращая набор результатов, который включает только те тряски, которые вы могли бы сделать? – chb

+0

да точно .. спасибо chb –

+0

у вас есть какие-либо требования, как будто это должен быть только один запрос? могу ли я просто вернуть имя/id? –

ответ

0

Я не мог придумать способ сделать это с помощью поиска. Но вы можете сделать это с помощью mapReduce.

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

Когда ключи уникальны, функция уменьшения никогда не должна вызываться (функция уменьшения используется для определения того, что происходит, когда несколько значений испускаются для одного и того же ключа). Я не думаю, что вы можете просто опустить функцию сокращения, поэтому вы должны написать тот, который просто возвращает первый элемент массива значений.

0

Карта снижение будет работать, если вы хотите, или вы можете просто использовать $ и с одним условием для каждого из ваших ингредиентов:

http://docs.mongodb.org/manual/reference/operator/and/

я использовал $ и раньше для подобных нужд запросов. К сожалению, в обоих случаях это необходимо будет проверять каждый документ каждого рецепта при каждом запросе, который будет иметь серьезные последствия для производительности, если ваша коллекция станет большой.

Это может быть лучше обработано реляционной базой данных. По крайней мере, вы должны рассмотреть возможность хранения отдельного запрашиваемого индекса ингредиентов, которые имеют карту для идентификаторов рецептов, которые их используют. Затем вы можете получить только те ингредиенты, которые у вас есть, и взять перекресток рецептов в коде. Опять же, поскольку это фактически то, что может сделать для вас реляционная база данных, это может быть просто лучшим выбором здесь.

0

Вы можете использовать dot notation для доступа к определенным элементам массива и убедитесь, что каждый элемент $in ваш список доступных ингредиентов и вы могли бы построить $and запрос, чтобы сделать это для каждого элемента в документе, который запрашивается НО конечно , что будет не работает, так как вы не знаете, сколько элементов содержится в списке ингредиентов каждого рецепта.

Одним из решений этой проблемы является добавление массива к каждому документу, который является фиксированным размером (возможно, 3 элемента), в который вы помещаете n-наименее общие ингредиенты из рецепта, заполнение (с повторами, если необходимо, чтобы заполнить массив). Теперь вы можете сделать запрос, используя $and и $in против каждого элемента этого массива. Это найдет все рецепты, которые могут быть «возможны», и поскольку вы храните наименее распространенные ингредиенты, он должен хорошо отлаживать рецепты, которые вы не можете сделать. Затем вы можете сделать окончательный фильтр на стороне клиента.

Иногда добавление другого представления данных может быть хорошим способом решения сложной проблемы с запросами, подобной этой, и иногда может потребоваться комбинация фильтрации на сервере и на стороне клиента.

0

В качестве параметра поиска вы можете использовать функцию javascript. Тем не менее, я думаю, что MapReduce будет намного более элегантным решением в целом благодаря масштабируемости и общей неудобствам, которые возникают с введением ввода пользователя в выполняемый код.

Вот пример запроса, который будет построен. В основном, он фильтрует ингредиенты этого пункта и проверяет, что отфильтрованный список не имеет ту же длину, что и исходный список ингредиентов:

db.shakes.find(function() { 

    // Implant your query JSON object here 
    var query_obj = [{"type" : "fruit", "name" : "mango"}, 
        {"type" : "milk", "name" : "soy milk"}, 
        {"type" : "fruit", "name" : "strawberry"}]; 
    return this.ingredients.filter(function(ingredient) { 
    var has_ingredients = false; 
    query_obj.forEach(function(query) { 
     has_ingredients |= (query.type == ingredient.type && query.name == ingredient.name); 
    }); 
    return has_ingredients; 
    }).length === this.ingredients.length; 
}) 
0

Там нет прямой способ сделать это. вы можете создать простую функцию javascript для решения вашего запроса, просмотрев каждый документ, и проверка ингредиентов массива содержит все три компонента.

db.shakes.find().forEach(

function (shakes) { 
    for (i = 0; i < shakes.ingredients.length; i++) { 
     switch (shakes.ingredients[i].name) { 
     case "banana": 
      var count = 1; 
     case "soy milk": 
      count++; 
     case "mango": 
      count++; 
     } 
     if (count == 3) { 
      print(shakes.name); 
     } 
    } 
} 
); 
0

Чтобы принести новую идею, в случае, если у вас уже есть набор всех ингредиентов. Например возможные ингредиенты только 5:

["mango","soy milk", "strawberry", "banana", "pineapple"] 

И вы не»не имеют ни банан, ни ананас

Одним из вариантов является для запроса ингредиентов у вас нет:

{"ingredients" : {$nin : [{"type" : "fruit", "name" : "banana"}, 
          {"type" : "fruit", "name" : "pineapple"}]}} 

Это возвращает все документы, у которых нет ингредиентов, которых у вас нет, и, таким образом, вы получаете готовые ингредиенты.

0

Предполагая, что вы в порядке с caveats of using the $where operator, вы можете использовать его в сочетании с Array.prototype.some и Array.prototype.every, чтобы получить желаемые результаты.

Это похоже на javascript-специфическое решение, но это не так. Javascript вводится непосредственно в Mongo (V8). Все языковые драйверы должны его поддерживать. Я знаю, по крайней мере, что PHP does.

Вы должны вставить этот код прямо в Mongo REPL и получить ожидаемые результаты (постучите по дереву).

Если ваша база данных молочного коктейля имеет потенциал для масштабирования за пределами того, что может дать $where, Aggregation Framework - твой друг.

var query = { 
    "$where": function() { 
     var myIngredients = [ 
      {"type" : "fruit", "name" : "mango"}, 
      {"type" : "milk", "name" : "soy milk"}, 
      {"type" : "fruit", "name" : "strawberry"} 
     ]; 
     return obj.ingredients.every(function(milkShakeIngredient){ 
      return myIngredients.some(function(myIngredient){ 
       return (milkShakeIngredient.name === myIngredient.name && milkShakeIngredient.type === myIngredient.type); 
      }); 
     }); 
    } 
}; 

db.milkShakes.find(query);