Това, което искате, е резултат от „фасетирано търсене“, където съхранявате статистическите данни за съвпадащите термини в текущия набор от резултати. Впоследствие, въпреки че има продукти, които „изглеждат“ да вършат цялата работа с един отговор, трябва да имате предвид, че повечето генерични машини за съхранение ще се нуждаят от множество операции.
С MongoDB можете да използвате две заявки, за да получите самите резултати, и друга, за да получите информация за аспектите. Това би дало резултати, подобни на фасетираните резултати, налични от продукти за специализирани търсачки като Solr или ElasticSearch.
Но за да направите това ефективно, искате да включите това във вашия документ по начин, по който може да се използва ефективно. Много ефективен формуляр за това, което искате, е използването на масив от токенизирани данни:
{
"otherData": "something",
"facets": [
"country:UK",
"city:London-UK",
"genre:Student"
]
}
Така че „фактите“ е едно поле във вашия документ, а не на няколко места. Това прави много лесно индексирането и заявката. След това можете ефективно да обобщите резултатите си и да получите общите суми за всеки аспект:
User.aggregate(
[
{ "$unwind": "$facets" },
{ "$group": {
"_id": "$facets",
"count": { "$sum": 1 }
}}
],
function(err,results) {
}
);
Или по-идеално с някои критерии в $match
:
User.aggregate(
[
{ "$match": { "facets": { "$in": ["genre:student"] } } },
{ "$unwind": "$facets" },
{ "$group": {
"_id": "$facets",
"count": { "$sum": 1 }
}}
],
function(err,results) {
}
);
В крайна сметка дава отговор като:
{ "_id": "country:FR", "count": 50 },
{ "_id": "country:UK", "count": 300 },
{ "_id": "city:London-UK", "count": 150 },
{ "_id": "genre:Student": "count": 500 }
Такава структура е лесна за преминаване и проверка за неща като отделната „държава“ и „града“, който принадлежи към „държава“, тъй като тези данни просто се разделят последователно с тире „-“.
Опитът за смесване на документи в масиви е лоша идея. Има ограничение за размера на BSON от 16MB, което също трябва да се спазва, от което резултатът от смесването (особено ако се опитвате да запазите съдържанието на документа) със сигурност ще бъде превишен в отговора.
За нещо толкова просто, като получаването на „общия брой“ на резултатите от такава заявка, просто обобщете елементите от конкретен тип аспект. Или просто издайте същите аргументи на заявката си към .count()
операция:
User.count({ "facets": { "$in": ["genre:Student"] } },function(err,count) {
});
Както е казано тук, особено при прилагане на „пейджинг“ на резултатите, тогава ролите за получаване на „Брой на резултатите“, „Брои на аспекти“ и действителната „Страница с резултати“ са делегирани на „разделни“ заявки към сървъра.
Няма нищо лошо в това да изпратите всяка от тези заявки до сървъра паралелно и след това да комбинирате структура, която да захранва вашия шаблон или приложение, много приличаща на фасетирания резултат от търсене от един от продуктите на търсачките, които предлагат този вид отговор.
Заключение
Така че поставете нещо в документа си, за да маркирате аспектите на едно място. Масив от токенизирани низове работи добре за тази цел. Също така работи добре с формуляри за заявки като $in
и $all
за условия "или" или "и" за комбинации за избор на аспекти.
Не се опитвайте да смесвате резултатите или да влагате добавки само за да съответствате на някаква възприета йерархична структура, а по-скоро преминете през получените резултати и използвайте прости шаблони в токените. Много е лесно да се
Изпълнявайте страници със заявки за съдържанието като отделни заявки или към аспекти, или към общия брой. Няма смисъл да се опитвате да избутате цялото съдържание в масиви и след това да ограничите само за получаване на броя. Същото важи и за RDBMS решение, за да се направи същото нещо, където резултатите от пейджинг се броят и текущата страница са отделни операции на заявка.
Има повече информация, написана в блога на MongoDB за Faceted Search с MongoDB, която също обяснява някои други опции. Има и статии за интеграция с външни решения за търсене, използващи mongoconnector или други подходи.