Поиск граней с использованием 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: сначала для получения результатов сопоставления, а затем один раз для каждой группы; полнотекстовый поисковый движок вы получите все в одном запросе ". см. ответ Гарсии