2011-02-17 5 views
1

Пожалуйста, не стесняйтесь, я довольно новичок во всем материале CouchDb.Выбор n элементов, принадлежащих пользователю в MapReduce для CouchDB

ДБ выглядит следующим образом:

** item ** count ** user ** 
    A  20  bob 
    B  30  bob 
    C  10  bob 
    D  15  john 

Я хочу написать MapReduce, который выбирает все элементы, принадлежащие боб и только вернуть верхнюю 2, отсортирован. поэтому он должен вернуться [{item:"B",count:"30"},{item:"A",count:"20}]

Я не уверен, как это можно сделать? Похоже, я должен испускать (doc.item, doc.count), но как узнать, владеет ли пользователь документом? Как запустить другой MapReduce, чтобы выбрать верхние элементы?

ответ

2

Одно решение было бы написать ваш взгляд использовать сложный ключ, например:

function (doc) { 
    emit([doc.user, doc.count], doc.item); 
} 

Если добавить descending=true в строку запроса, что даст вам результат вида, как:

{"total_rows":4,"offset":0,"rows":[ 
    {"id":"53f359b7cd360da296dd9aab3d0029bd","key":["john",15],"value":"D"}, 
    {"id":"53f359b7cd360da296dd9aab3d001a0e","key":["bob",30],"value":"B"}, 
    {"id":"53f359b7cd360da296dd9aab3d000fec","key":["bob",20],"value":"A"}, 
    {"id":"53f359b7cd360da296dd9aab3d002668","key":["bob",10],"value":"C"} 
]} 

Сортировка по умолчанию пользователя, затем подсчет. (с типом предмета в качестве значения)

Затем вы можете использовать _list function, чтобы сделать все остальное. В приведенном ниже коде в основном просматривается представление, и возвращается два лучших результата для каждого пользователя. Если вы укажете user=bob в строке запроса, вы получите результаты только для bob.

function (head, req) { 
    // specify that we're sending JSON as our response 
    provides('json', function() { 
     var results = [], 
      result, user, count, row; 

     while (row = getRow()) { 
      // if the user doesn't match the last iteration, reset our counter 
      if (user != row.key[0]) { 
       user = row.key[0]; 
       count = 0; 
      } 

      // we only need the top 2 
      if (count++ >= 2) { 
       continue; 
      } 

      // start building a result object 
      result = { 
       item: row.value, 
       count: row.key[1] 
      }; 

      // if we provide user=? 
      if (req.query.user) { 
       // check to see if it matches the current user 
       if (req.query.user === user) { 
        // if so, add it to the results 
        results.push(result); 
       } 
      // by default, we'll return the top 2 for every user 
      } else { 
       // add the user key to the result object 
       result.user = row.key[0]; 
       // and add it to the result set 
       results.push(result); 
      } 
     } 

     // send outside the loop, since it needs to be sent as valid JSON 
     send(JSON.stringify(results)); 
    }); 
} 
+0

+1 отличный ответ. Возможно, вы захотите подчеркнуть, что функция '_list' * необязательна *, она добавляет удобство/согласованность для клиента, однако нет фундаментального * изменения или прямого запроса. Это может показать, что ответ довольно прост! Затем есть бонусное упражнение, чтобы сделать его идеальным. – JasonSmith

2

Если вы положили user и count в ключе зрения, вы можете использовать startkey=["bob",""] и endkey=["bob"] для выбора пользователя, а descending=true и limit=2, чтобы получить два верхних элементов.

Я попробовал следующую функцию карты:

function(doc) { 
    if(doc.user && doc.count && doc.item) { 
    emit([doc.user, doc.count], doc); 
    } 
} 

со строкой запроса ?startkey=["bob",""]&endkey=["bob"]&descending=true&limit=2 он возвращает:

{"total_rows":4,"offset":1,"rows":[ 
{"id":"item_B_bob","key":["bob",30],"value":{"_id":"item_B_bob","_rev":"1-b23bd22fb719c7d59b045bce0932df8c","item":"B","count":30,"user":"bob"}}, 
{"id":"item_A_bob","key":["bob",20],"value":{"_id":"item_A_bob","_rev":"2-515bca46eab383cfeaaa2a101d180291","item":"A","count":20,"user":"bob"}} 
]} 

Обратите внимание:

Смежные вопросы