MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

Pourcentage de conditions OR satisfaites dans mongodb

Eh bien, votre solution devrait vraiment être spécifique à MongoDB, sinon vous finirez par faire vos calculs et une éventuelle correspondance côté client, et cela ne sera pas bon pour les performances.

Donc, bien sûr, ce que vous voulez vraiment, c'est un moyen d'avoir ce traitement côté serveur :

db.products.aggregate([

    // Match the documents that meet your conditions
    { "$match": {
        "$or": [
            { 
                "features": { 
                    "$elemMatch": {
                       "key": "Screen Format",
                       "value": "16:9"
                    }
                }
            },
            { 
                "features": { 
                    "$elemMatch": {
                       "key" : "Weight in kg",
                       "value" : { "$gt": "5", "$lt": "8" }
                    }
                }
            },
        ]
    }},

    // Keep the document and a copy of the features array
    { "$project": {
        "_id": {
            "_id": "$_id",
            "product_id": "$product_id",
            "ean": "$ean",
            "brand": "$brand",
            "model": "$model",
            "features": "$features"
        },
        "features": 1
    }},

    // Unwind the array
    { "$unwind": "$features" },

    // Find the actual elements that match the conditions
    { "$match": {
        "$or": [
            { 
               "features.key": "Screen Format",
               "features.value": "16:9"
            },
            { 
               "features.key" : "Weight in kg",
               "features.value" : { "$gt": "5", "$lt": "8" }
            },
        ]
    }},

    // Count those matched elements
    { "$group": {
        "_id": "$_id",
        "count": { "$sum": 1 }
    }},

    // Restore the document and divide the mated elements by the
    // number of elements in the "or" condition
    { "$project": {
        "_id": "$_id._id",
        "product_id": "$_id.product_id",
        "ean": "$_id.ean",
        "brand": "$_id.brand",
        "model": "$_id.model",
        "features": "$_id.features",
        "matched": { "$divide": [ "$count", 2 ] }
    }},

    // Sort by the matched percentage
    { "$sort": { "matched": -1 } }

])

Donc, comme vous connaissez la "longueur" du $or condition étant appliquée, il vous suffit de savoir combien d'éléments du tableau "features" correspondent à ces conditions. C'est donc de cela qu'il s'agit avec la deuxième $match du pipeline.

Une fois que vous avez ce nombre, vous divisez simplement par le nombre de conditions ce qui a été transmis comme votre $or . La beauté ici est que maintenant vous pouvez faire quelque chose d'utile avec ceci, comme trier par pertinence, puis même "pager" le côté serveur des résultats.

Bien sûr, si vous souhaitez une "catégorisation" supplémentaire, il vous suffit d'ajouter un autre $project étape jusqu'à la fin du pipeline :

    { "$project": {
        "product_id": 1
        "ean": 1
        "brand": 1
        "model": 1,
        "features": 1,
        "matched": 1,
        "category": { "$cond": [
            { "$eq": [ "$matched", 1 ] },
            "100",
            { "$cond": [ 
                { "$gte": [ "$matched", .7 ] },
                "70-99",
                { "$cond": [
                   "$gte": [ "$matched", .4 ] },
                   "40-69",
                   "under 40"
                ]} 
            ]}
        ]}
    }}

Ou comme quelque chose de similaire. Mais le $cond l'opérateur peut vous aider ici.

L'architecture devrait être correcte telle que vous l'avez car vous pouvez avoir un index composé sur la "clé" et la "valeur" pour les entrées de votre tableau de fonctionnalités et cela devrait bien s'adapter aux requêtes.

Bien sûr, si vous avez réellement besoin de quelque chose de plus que cela, comme la recherche à facettes et les résultats, vous pouvez envisager des solutions comme Solr ou la recherche élastique. Mais la mise en œuvre complète de cela serait un peu longue pour ici.