La recherche facettée (faceted search) permet aux utilisateurs de filtrer une collection de données selon un ou plusieurs critères (les facettes). C’est exactement ce qui vous permet de trouver Batman parmi les super-héros en fonction de son univers (DC), sa ville (Gotham) et son symbole (chauve-souris). Une recherche Google renvoit directement à un article d’aout 2013. La version 3.4 - sortie le 29 novembre 2016 - de MongoDB apporte de nouvelles solutions.

Méthode 1 : La Brute

Le document contient des données en double car il faut dupliquer les données des facettes. Chaque document devient donc plus lourd. De plus, l’index est plus lourd qu’avec les autres méthodes.

// stockage
{
	_id: 123,
	name: "Batman",
	universe: ["DC"],
	city : [ "Gotham", "Arkham" ],
	symbol : ["bat","cape"]
	facets : [
		"universe:DC",
		"city:Gotham",
		"city:Arkham",
		"symbol:bat",
		"symbol:cape"
	]
}

// creation index
db.superheros.ensureIndex({"facets" : 1})

// obtenir les facettes
db.superheros.aggregate([{ "$unwind" : "$facets" },
                          { "$group" : { "name" : "$facets", count : { "$sum" : 1 } } },
                          { "$sort" : { "name" : 1 } }
                         ])

Méthode 2 : Le Truand

Le document reste simple et devient même plus concis. Il faut créer un index par facette, pourtant le poids des index est inférieur à celui de l’index de la méthode 1. Par contre il est nécessaire de faire une aggrégation par facette.

réécrire en explosant les facettes et tester. ?

// stockage						 
{
	_id: 123,
	name: "Batman",
	facets : {
	        universe: ["DC"],
		city : [ "Gotham", "Arkham" ],
		symbol : ["bat","cape"]
	}
}

// creation index	
db.superheros.ensureIndex({"facets.universe" : 1})
db.superheros.ensureIndex({"facets.city" : 1})
db.superheros.ensureIndex({"facets.symbol" : 1})

// obtenir les facettes
db.superheros.aggregate([{ "$unwind" : "$facets.universe" },
	{ "$group" : { "name" : "$facets.universe", count : { "$sum" : 1 } } },
	{ "$sort" : { "name" : 1 } }
])
db.superheros.aggregate([{ "$unwind" : "$facets.city" },
	{ "$group" : { "name" : "$facets.city", count : { "$sum" : 1 } } },
	{ "$sort" : { "name" : 1 } }
])
db.superheros.aggregate([{ "$unwind" : "$facets.symbol" },
	{ "$group" : { "name" : "$facets.symbol", count : { "$sum" : 1 } } },
	{ "$sort" : { "name" : 1 } }
])

Méthode 3 : Le Bon

Parmi les nouveautés de la version 3.4 de MongoDB, vous trouverez $facet et $bucket qui facilite la gestion des facettes. Le document n’est pas modifié pour les facettes. Aucun index spécifique n’est requis pour fonctionner.

// stockage						 
{
	_id: 123,
	name: "Batman",
	universe: ["DC"],
	city : [ "Gotham", "Arkham" ],
	item : ["bat", "cape"]
}

// obtenir les facettes
db.superheros.aggregate( [
  {
    $facet: {
      "categorizedByUniverse": [
        { $unwind: "$universe" },
        { $sortByCount: "$universe" }
      ],
      "categorizedByCities": [
        { $unwind: "$city" },
        { $sortByCount: "$city" }
      ],
      "categorizedByItems": [
        { $unwind: "$item" },
        { $sortByCount: "$item" }
      ]
     }
   }
])

Astuce

Pour connaitre les versions de MongoDB, voici deux commandes à exécuter dans le shell mongo : version() : connaitre la version du client MongoDB db.version() : connaitre la version de MongoDB

Références

Faceted Search with MongoDB

$facet dans la documentation MongoDB

$bucket dans la documentation MongoDB