2012-06-25 12 views
27

W strukturze agregacji MongoDB miałem nadzieję użyć operatora $ unwind dla obiektu (np. Kolekcja JSON). Nie wygląda na to, że jest to possible, czy istnieje obejście? Czy są plany wdrożenia?

Weźmy na przykład kolekcję artykułów z agregacji documentation. Załóżmy, że istnieje dodatkowe pole "ocen", które jest mapą od użytkownika -> ocena. Czy możesz obliczyć średnią ocenę dla każdego użytkownika?

Poza tym jestem całkiem zadowolony ze struktury agregacji.

Aktualizacja: oto uproszczona wersja mojej kolekcji JSON na każde żądanie. Przechowuję dane genomowe. Nie mogę tak naprawdę uczynić genotypów tablicą, ponieważ najbardziej popularnym odnośnikiem jest uzyskanie genotypu dla przypadkowej osoby.

variants: [ 

    { 
     name: 'variant1', 
     genotypes: { 

      person1: 2, 
      person2: 5, 
      person3: 7, 

     } 
    }, 

    { 
     name: 'variant2', 
     genotypes: { 

      person1: 3, 
      person2: 3, 
      person3: 2, 

     } 
    } 

] 
+0

Zamieść js przy odbiorze. –

+0

Biorąc pod uwagę, że to twoja kolekcja (?), Jaka jest agregacja, którą chcesz wykonać, do której potrzebujesz $ unwind? –

+0

Czy to bezpieczne założenie, że liczba "osób" w podtypach genotypów jest zmienna? Nie jestem pewien, dlaczego nie można tworzyć genotypów jako tablicy, btw. –

Odpowiedz

28

Nie jest możliwe do zrobienia rodzaj obliczeń opisujesz z ramami agregacji - i to nie, ponieważ nie ma $unwind metoda tylko dla tablic. Nawet jeśli obiekty person: value były dokumentami w tablicy, $unwind nie pomogłoby.

Funkcja "grupuj według" (niezależnie od tego, czy w MongoDB, czy w dowolnej relacyjnej bazie danych) jest wykonywana na wartości pola lub kolumny. Grupujemy według wartości pola i sumy/średniej/etc na podstawie wartości innego pola.

Prosty przykład jest wariantem co proponujesz, oceny pola dodane do przykładowego artykułu kolekcji, ale nie jako mapa z użytkownikiem do rankingu, ale jako tablicę takich jak to:

{ title : title of article", ... 
    ratings: [ 
     { voter: "user1", score: 5 }, 
     { voter: "user2", score: 8 }, 
     { voter: "user3", score: 7 } 
    ] 
} 

Teraz można agregacji z tego:

[ {$unwind: "$ratings"}, 
    {$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } } 
] 

ale ten przykład strukturalnego, jak opisać to będzie wyglądać następująco:

{ title : title of article", ... 
    ratings: { 
     user1: 5, 
     user2: 8, 
     user3: 7 
    } 
} 

lub nawet poniżej:

{ title : title of article", ... 
    ratings: [ 
     { user1: 5 }, 
     { user2: 8 }, 
     { user3: 7 } 
    ] 
} 

Nawet jeśli można $unwind to nie ma nic do agregowania na tutaj. O ile nie znasz kompletnej listy wszystkich możliwych kluczy (użytkowników), nie możesz z tym wiele zrobić. [*]

Analogiczny relacyjnej DB schematu, co masz będzie:

CREATE TABLE T (
    user1: integer, 
    user2: integer, 
    user3: integer 
    ... 
); 

To nie jest to, co zrobić, zamiast tego byłoby to zrobić:

CREATE TABLE T (
    username: varchar(32), 
    score: integer 
); 

a teraz agregować za pomocą SQL:

select username, avg(score) from T group by username;

jest enhan cementowe żądanie dla MongoDB, które może pozwolić ci to zrobić w ramach struktury agregacji w przyszłości - możliwość wyświetlania wartości kluczom na odwrót. Tymczasem zawsze jest mapa/zmniejsz.

[*] Jest to skomplikowany sposób, aby to zrobić, jeśli znasz wszystkie unikalne klucze (możesz znaleźć wszystkie unikalne klucze za pomocą metody podobnej do this), ale jeśli znasz wszystkie klucze, możesz po prostu uruchomić sekwencję zapytań o formularz db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1}) dla każdego userX, który zwróci wszystkie swoje oceny i możesz je zsumować i uśrednić po prostu, zamiast wykonywać bardzo złożoną projekcję wymaganą przez strukturę agregacji.

+0

Niestety, myślę, że to prawda ... Jestem w podobnej sytuacji i powodem, dla którego taka struktura jest podobna do prostych przyrostów gdzie indziej. Mimo że struktura agregacji nie może tego obsłużyć, MongoDB może nadal grupować i agregować te pola. Po prostu musisz to zrobić z MongoCode w grupie $.Zasadniczo zapętlisz się i powiesz, że klucz nie istnieje, ustaw go na aktualną wartość. Jeśli tak, dodaj do wartości. Dość proste. Szkoda, że ​​struktura agregacji nie radzi sobie z czymś tak prostym. W rzeczywistości użycie struktury agregacji prowadzi do większej konserwacji kodu. – Tom

+0

Możesz głosować na ten https://jira.mongodb.org/browse/SERVER-5947, aby uzyskać funkcję mongo, która może to umożliwić. Powinieneś również zmienić swój schemat, aby użyć "name": "username", "value": num elementy tablicy, a to * nie * uniemożliwi korzystanie z operatora $ inc, ponieważ możesz dopasować element tablicy i zaktualizować go za pomocą $ "operator pozycyjny" –

+0

Napisz to jako pytanie - komentarze służą wyłącznie do omówienia tego pytania i odpowiedzi. –

0

To jest stare pytanie, ale natknąłem się na ciekawostki związane z próbami i błędami, które ludzie mogą uznać za przydatne.

To rzeczywiście można odpocząć na wartości manekina przez oszukiwanie parser ten sposób:

db.Opportunity.aggregate(
    { $project: { 
     Field1: 1, Field2: 1, Field3: 1, 
     DummyUnwindField: { $ifNull: [null, [1.0]] } 
    } 
    }, 
    { $unwind: "$DummyUnwindField" } 
); 

To będzie produkować 1 wiersz na dokumencie, niezależnie od tego, czy wartość istnieje. Być może uda ci się to ulepszyć, aby wygenerować pożądane rezultaty. Miałem nadzieję, że połączy to z wieloma $ unwindami na (trochę jak emit() na mapie/zmniejszeniu), ale niestety, ostatnie wygrane $ unwind lub łączą się jako skrzyżowanie zamiast unii, co uniemożliwia osiągnięcie wyników I szukałem. Jestem rozczarowany zagregowaną funkcjonalnością ramek, ponieważ nie pasuje ona do jednego przypadku użycia, do którego miałem nadzieję (i wydaje się dziwnie, jak wiele pytań na temat StackOverflow w tym obszarze pyta) - porządkowanie wyników w oparciu o dopasowanie oceniać. Udoskonalenie słabej mapy zmniejsza wydajność, dlatego cała ta funkcja jest niepotrzebna.

0

This is what I found & extended.

Pozwala tworzyć bazę eksperymentalną w Mongo

db.copyDatabase('livedb' , 'experimentdb') 

teraz używać experimentdb & convert Array sprzeciwu w experimentcollection

db.getCollection('experimentcollection').find({}).forEach(function(e){ 
    if(e.store){ 
     e.ratings = [e.ratings]; //Objects name to be converted to array eg:ratings 
     db.experimentcollection.save(e); 
    } 
}) 

Niektóre nerdy js kod przekonwertować json do płaskiego obiektu

var flatArray = []; 

var data = db.experimentcollection.find().toArray(); 

for (var index = 0; index < data.length; index++) { 

    var flatObject = {}; 

    for (var prop in data[index]) { 

    var value = data[index][prop]; 

    if (Array.isArray(value) && prop === 'ratings') { 
     for (var i = 0; i < value.length; i++) { 
     for (var inProp in value[i]) { 
      flatObject[inProp] = value[i][inProp]; 
     } 
     } 
    }else{ 
     flatObject[prop] = value; 
    } 
    } 
    flatArray.push(flatObject); 
} 

printjson(flatArray); 
Powiązane problemy