代码之家  ›  专栏  ›  技术社区  ›  Jeremy Kaffrey

具有完全匹配优先级的Mongodb聚合匹配查询

  •  2
  • Jeremy Kaffrey  · 技术社区  · 6 年前

    我正在尝试对字段执行mongodb正则表达式查询。我希望查询在找到一个完整匹配项后优先排序,然后是部分匹配项。

    例如,如果我有一个包含以下条目的数据库。

    {
       "username": "patrick"
    },
    {
       "username": "robert"
    },
    {
       "username": "patrice"
    },
    {
       "username": "pat"
    },
    {
       "username": "patter"
    },
    {
       "username": "john_patrick"
    }
    

    我查询用户名‘pat’,我想先得到直接匹配的结果,然后是部分匹配。所以结果将被排序为[“pat”,“patrick”,“patrice”,“patter”,“john_patrick']。

    是否可以仅使用mongo查询来完成此操作?如果可以的话,有人能给我指一个资源,详细说明如何做到这一点吗?

    下面是我试图用来执行此操作的查询。

    db.accounts.aggregate({ $match : 
    { 
        $or : [ 
            { "usernameLowercase" : "pat" },
            { "usernameLowercase" : { $regex : "pat" } }
        ] 
    } })
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   dnickless    6 年前

    根据您的精确示例,这可以通过以下方式完成-如果您的真实世界场景稍微复杂一点,您可能会遇到问题,但是:

    db.accounts.aggregate([{
        $match: {
            "username": /pat/i // find all documents that somehow match "pat" in a case-insensitive fashion
        }
    }, {
        $addFields: {
            "exact": { 
                $eq: [ "$username", "pat" ] // add a field that indicates if a document matches exactly
            },
            "startswith": { 
                $eq: [ { $substr: [ "$username", 0, 3 ] }, "pat" ] // add a field that indicates if a document matches at the start
            }
    
        }
    }, {
        $sort: {
            "exact": -1, // sort by our primary temporary field
            "startswith": -1 // sort by our seconday temporary
        }
    }, {
        $project: {
            "exact": 0, // get rid of the "exact" field,
            "startswith": 0 // same for "startswith"
        }
    }])
    

    另一种方法是 $facet 这可能会通过启用更复杂的场景而变得更强大,但速度会慢一些(不过,这里有几个人会因为这个提议而恨我):

    db.accounts.aggregate([{
        $facet: { // run two pipelines against all documents
            "exact": [{ // this one will capture all exact matches
                $match: {
                    "username": "pat"
                }
            }],
            "others": [{ // this one will capture all others
                $match: {
                    "username": { $ne: "pat", $regex: /pat/i }
                }
            }]
        }
    }, {
        $project: {
            "result": { // merge the two arrays
                $concatArrays: [ "$exact", "$others" ]
            }
        }
    }, {
        $unwind: "$result" // flatten the resulting array into separate documents
    }, {
        $replaceRoot: { // restore the original document structure
            "newRoot": "$result"
        }
    }])