2017-10-16 218 views
0

數據結構 - 在一個大集合一個文檔MongoDB的屬性字符串以查詢和設置組ID

{ 
    OPERATINGSYSTEM: "Android 6.0" 
} 

問題:OperatingSystem的可以等於例如「Android 5.0」,「Android 6.0」,「Windows Phone」,「Windows Phone 8.1」

沒有隻包含操作系統類型的屬性,例如只有Android


我需要得到Windows手機和Android手機的數量。

我的臨時解決方案:

db.getCollection('RB').find(
    {OPERATINGSYSTEM: {$regex: "^Android"}} 
).count(); 

我做了查詢通過的Windows Phone等,這些更換"^Android"需要花費很多時間,並且需要並行工作要做。

使用聚合框架不過,我覺得這一點:

db.RB.aggregate(
{$group: {_id: {OPERATINGSYSTEM:"$OPERATINGSYSTEM"}}},) 

但是使用這個我得到每個OperatingSystem的版本Android 5.0Android 6.0等條目...

的解決方案我在尋找宜以這種格式返回數據:

{ 
    "Android": 50, 
    "Windows Phone": 100 
} 

這怎麼能在單個查詢中完成?

回答

0

您可以使用map reduce,並在地圖功能中應用您的邏輯。

var map = function(){ 
    var name = this.op.includes("android") ? "Android" : ""; // could be a regexp 

    if(name === ""){ 
     name = this.op.includes("windows") ? "Windows" : ""; 
    } 

    emit(name, 1); 
} 

var reduce = function(key, values){ 
    return Array.sum(values) 
} 

db.operating.mapReduce(map, reduce, {out: "total"}) 

https://docs.mongodb.com/manual/tutorial/map-reduce-examples/

+0

你是對的,在這種情況下,灌溉框架不是最好的解決方案。 –

2

前提是你的字符串至少持續有數字版本字符串中的最後一件事,那麼你可以使用$split與聚合框架,使從「空格分隔的」數組內容,然後重建之前刪除該數組中的最後一個元素:

鑑於數據等:

{ "name" : "Android 6.0" } 
{ "name" : "Android 7.0" } 
{ "name" : "Windows Phone 10" } 

你可以試試:

db.getCollection('phones').aggregate([ 
    { "$group": { 
    "_id": { 
     "$let": { 
     "vars": { "split": { "$split": [ "$name", " " ] } }, 
     "in": { 
      "$reduce": { 
      "input": { "$slice": [ "$$split", 0, { "$subtract": [ { "$size": "$$split" }, 1 ] } ] }, 
      "initialValue": "", 
      "in": { 
       "$cond": { 
       "if": { "$eq": [ "$$value", "" ] }, 
       "then": "$$this", 
       "else": { "$concat": [ "$$value", " ", "$$this" ] } 
       } 
      } 
      } 
     } 
     } 
    }, 
    "count": { "$sum": 1 } 
    }}, 
    { "$replaceRoot": { 
    "newRoot": { 
     "$arrayToObject": [[{ "k": "$_id", "v": "$count" }]] 
    } 
    }} 
]) 

這是所有可能的,如果你的MongoDB是至少MongoDB的3.4,同時支持$split$reduce$replaceRoot實際上是關於命名密鑰,而不是真正需要的。

或者您可以使用mapReduce

db.getCollection('phones').mapReduce(
    function() { 
    var re = /\d+/g; 
    emit(this.name.substr(0,this.name.search(re)-1),1); 
    }, 
    function(key,values) { return Array.sum(values) }, 
    { "out": { "inline": 1 } } 
) 

在哪裏更容易通過,其中一個數值發生指數打破字符串。無論哪種情況,您都不需要對任何內容進行「硬編碼」,並且這些鍵的值完全依賴於上下文中的字符串。

請記住,除非有非常多的可能值,否則運行並行.count()操作應該是最快的處理方式,因爲返回的遊標數比實際計算聚合條目要快得多。