Поиск граней с использованием MongoDB
Я собираюсь использовать MongoDB для своего следующего проекта. Одним из основных требований для этого приложения является предоставление фасетного поиска. Кто-нибудь пытался использовать MongoDB для достижения поиска фасета?
У меня есть модель продукта с различными атрибутами, такими как размер, цвет, марка и т.д. При поиске продукта это приложение Rails должно отображать фасетные фильтры на боковой панели. Фазовые фильтры будут выглядеть примерно так:
Size:
XXS (34)
XS (22)
S (23)
M (37)
L (19)
XL (29)
Color:
Black (32)
Blue (87)
Green (14)
Red (21)
White (43)
Brand:
Brand 1 (43)
Brand 2 (27)
Ответы
Ответ 1
Я думаю, что с помощью Apache Solr или ElasticSearch вы получаете больше гибкости и производительности, но это поддерживается с помощью Aggregation Framework.
Основная проблема с использованием MongoDB заключается в том, что вы должны запросить его N Times: First для получения результатов сопоставления, а затем один раз для каждой группы; при использовании полнотекстовой поисковой системы вы получаете все в одном запросе.
пример
//'tags' filter simulates the search
//this query gets the products
db.products.find({tags: {$all: ["tag1", "tag2"]}})
//this query gets the size facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$size"}, count: {$sum:1}},
{$sort: {count:-1}}
)
//this query gets the color facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$color"}, count: {$sum:1}},
{$sort: {count:-1}}
)
//this query gets the brand facet
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}},
{$group: {_id: "$brand"}, count: {$sum:1}},
{$sort: {count:-1}}
)
После того, как пользователь будет фильтровать поиск с использованием фасетов, вы должны добавить этот фильтр для запроса предиката и предиката соответствия следующим образом.
//user clicks on "Brand 1" facet
db.products.find({tags: {$all: ["tag1", "tag2"]}, brand: "Brand 1"})
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$size"}, count: {$sum:1}},
{$sort: {count:-1}}
)
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$color"}, count: {$sum:1}},
{$sort: {count:-1}}
)
db.products.aggregate(
{$match: {tags: {$all: ["tag1", "tag2"]}}, brand: "Brand 1"},
{$group: {_id: "$brand"}, count: {$sum:1}},
{$sort: {count:-1}}
)
Ответ 2
Mongodb 3.4 представляет фасетный поиск
Этап $ facet позволяет создавать многогранные агрегации, которые характеризуют данные в разных измерениях или фасетках на одном этапе агрегации. Многогранные агрегаты предоставляют множество фильтров и категорий для управления просмотром и анализом данных.
Входные документы передаются на этап $ facet только один раз.
Теперь вам не нужно запрашивать N раз для получения скоплений по N группам.
$ facet позволяет использовать различные агрегаты в одном и том же наборе входных документов, не требуя многократно извлекать входные документы.
Образец запроса для варианта использования ОП был бы чем-то вроде
db.products.aggregate( [
{
$facet: {
"categorizedByColor": [
{ $match: { color: { $exists: 1 } } },
{
$bucket: {
groupBy: "$color",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
],
"categorizedBySize": [
{ $match: { size: { $exists: 1 } } },
{
$bucket: {
groupBy: "$size",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
],
"categorizedByBrand": [
{ $match: { brand: { $exists: 1 } } },
{
$bucket: {
groupBy: "$brand",
default: "Other",
output: {
"count": { $sum: 1 }
}
}
}
]
}
}
])
Ответ 3
Популярным вариантом для более продвинутого поиска в MongoDB является использование ElasticSearch в сочетании с поддерживаемым сообществом плагином MongoDB River. Плагин MongoDB River подает поток документов от MongoDB в ElasticSearch для индексирования.
ElasticSearch - это распределенная поисковая система, основанная на Apache Lucene, и имеет интерфейс RESTful JSON через http. Существует API поиска Facet и ряд других дополнительных функций, таких как Percolate и "Больше похоже на это".
Ответ 4
Вы можете выполнить запрос, вопрос будет быстрым или нет. т.е. что-то вроде:
find( { size:'S', color:'Blue', Brand:{$in:[...]} } )
вопрос заключается в том, как производительность. Специального объекта для факсимильного поиска в продукте пока нет. Вдоль дороги могут быть какие-то сетки, похожие на пересечения, которые хороши, но это tbd/future.
-
Если ваши свойства являются предопределенным набором, и вы знаете, каковы они, вы можете создать индекс для каждого из них. Только один из индексов будет использоваться в текущей реализации, поэтому это поможет, но только достанется вам: если набор данных имеет средний плюс по размеру, это может быть хорошо.
-
Вы можете использовать составные индексы, которые, возможно, объединяют два или более свойств. Если у вас небольшое количество свойств, это может работать очень хорошо. Индекс не должен использовать все запросы переменных, но в одном выше составной индекс для любых двух из трех, вероятно, будет лучше, чем индекс для одного элемента.
-
Если у вас не будет слишком много скотов, то скин будет работать; например, если вы 1MM-скины, сканирование таблицы в ram может быть достаточно быстрым. в этом случае я бы сделал таблицу с только значениями фасет и сделаю ее как можно меньше и сохранит полный файл sku в отдельной коллекции. например:
facets_collection: {sz: 1, бренд: 123, clr: 'b', _ id:}...
если # размеров фаз не слишком высок, вы можете вместо этого сделать очень сложный индекс размерных размеров, и вы получите эквивалент выше, без дополнительной работы.
если вы создадите quit несколько индексов, вероятно, лучше не создавать так много, что они больше не вписываются в ram.
учитывая, что выполняется запрос, и это вопрос производительности, который может быть просто с монго, и если он не достаточно быстрый, а затем болт на solr.
Ответ 5
Граничное решение (основанное на счетах) зависит от вашего дизайна приложения.
db.product.insert(
{
tags :[ 'color:green','size:M']
}
)
Однако, если вы можете подавать данные в указанном выше формате, где грани и их значения объединяются вместе, чтобы сформировать согласованный тег, то используя приведенный ниже запрос
db.productcolon.aggregate(
[
{ $unwind : "$tags" },
{
$group : {
_id : '$tags',
count: { $sum: 1 }
}
}
]
)
См. Результат результата ниже
{
"_id" : "color:green",
"count" : NumberInt(1)
}
{
"_id" : "color:red",
"count" : NumberInt(1)
}
{
"_id" : "size:M",
"count" : NumberInt(3)
}
{
"_id" : "color:yellow",
"count" : NumberInt(1)
}
{
"_id" : "height:5",
"count" : NumberInt(1)
}
Помимо этого шага сервер приложений может выполнять группировку цветов и размеров перед отправкой обратно клиенту.
Примечание. Подход к объединению фасета и его значений дает вам все значения фасетов, которые могут быть объединены, и вы можете избежать этого. "Основная проблема с использованием MongoDB заключается в том, что вы должны запросить его N Times: сначала для получения результатов сопоставления, а затем один раз для каждой группы; полнотекстовый поисковый движок вы получите все в одном запросе ". см. ответ Гарсии