如何连接所有的值,并在Mongodb中找到特定的子字符串



我有一个json文档,像这样:

{
  "A": [
    {
      "C": "abc",
      "D": "de"
    },
    {
      "C": "fg",
      "D": "hi"
    }
  ]
}
我将检查"A"是否包含字符串ef。首先连接所有值abcdefghi,然后搜索ef

在XML中,XPATH应该是这样的://A[contains(., 'ef')]

在Mongodb中是否有类似的查询?

对于这种类型的搜索,所有选项都非常糟糕,但是您可以采用一些方法。请注意,虽然这里的结束情况可能是最好的解决方案,但我提出的选项是为了说明问题。

如果数组"A"中的键是一致定义的,并且总是包含数组,那么您将像这样搜索:

db.collection.aggregate([
    // Filter the documents containing your parts
    { "$match": {
        "$and": [
            { "$or": [
                { "A.C": /e/ },
                { "A.D": /e/ } 
            ]},
            {"$or": [
                { "A.C": /f/ },
                { "A.D": /f/ }
            ]}
        ]
    }},
    // Keep the original form and a copy of the array
    { "$project": { 
        "_id": { 
            "_id": "$_id", 
            "A": "$A" 
        },
        "A": 1 
    }},
    // Unwind the array
    { "$unwind": "$A" },
    // Join the two fields and push to a single array
    { "$group": {
         "_id": "$_id",
         "joined": { "$push": {
             "$concat": [ "$A.C", "$A.D" ]
         }}
    }},
    // Copy the array
    { "$project": {
        "C": "$joined",
        "D": "$joined"
    }},
    // Unwind both arrays
    { "$unwind": "$C" },
    { "$unwind": "$D" },
    // Join the copies and test if they are the same
    { "$project": {
        "joined": { "$concat": [ "$C", "$D" ] },
        "same": { "$eq": [ "$C", "$D" ] },
    }},
    // Discard the "same" elements and search for the required string
    { "$match": {
        "same": false,
        "joined": { "$regex": "ef" }
    }},
    // Project the origial form of the matching documents
    { "$project": {
        "_id": "$_id._id",
        "A": "$_id.A"
    }}
])

因此,除了可怕的 $regex 匹配之外,为了使字段"连接"以便再次按顺序搜索字符串,还有一些需要经过的环节。还要注意这里可能的反向连接可能产生假阳性。目前还没有简单的方法来避免反向连接或过滤它,因此需要考虑这个问题。

另一种方法基本上是通过任意JavaScript运行所有内容。mapReduce方法可以成为您实现这一点的工具。在这里,您可以稍微放宽可以包含在" a "和try中的数据类型,将更多的条件匹配与try结合起来,以减少您正在处理的文档集:

db.collection.mapReduce(
    function () {
      var joined = "";
      if ( Object.prototype.toString.call( this.A ) === '[object Array]' ) {
        this.A.forEach(function(doc) {
          for ( var k in doc ) {
            joined += doc[k];
          }
        });
      } else {
        joined = this.A;  // presuming this is just a string
      }
      var id = this._id;
      delete this["_id"];
      if ( joined.match(/ef/) )
        emit( id, this  );
    },
    function(){},    // will not reduce
    { 
        "query": {
            "$or": [
                { "A": /ef/ },
                { "$and": [
                    { "$or": [
                        { "A.C": /e/ },
                        { "A.D": /e/ } 
                    ]},
                    {"$or": [
                        { "A.C": /f/ },
                        { "A.D": /f/ }
                    ]}
                ] }
            ]
        },
        "out": { "inline": 1 }
    }
);

所以你可以使用任意逻辑来搜索包含的对象。这个只是区分了"数组"和假设是字符串,允许查询的附加部分首先搜索匹配的"string"元素,这是一个"短路"计算。

但是在一天结束的时候,最好的方法是简单地将数据呈现在文档中,并且在更新文档内容时必须自己维护它:

{
  "A": [
    {
      "C": "abc",
      "D": "de"
    },
    {
      "C": "fg",
      "D": "hi"
    }
  ],
  "search": "abcdefghi"
}

因此,这仍然会调用 $regex 类型查询的可怕用法,但至少这避免了(或者更确切地说,转移到编写文档)"连接"元素的开销,以影响搜索所需的字符串。

这最终导致的是一个"完全成熟"的文本搜索解决方案,这意味着一个外部的一个在这个时候,而不是MongoDB的文本搜索设施,可能会是你最好的性能选择。

在创建"连接"字段时使用"预存储"方法,或者在支持的情况下(Solr是可以做到这一点的一种解决方案),在索引文档内容时创建文本索引中的"计算字段"。

无论如何,这些都是解决问题的方法和要点。这不是XPath搜索,也不是从这个意义上看整个集合的某种"类似XPath"的视图,因此您最适合按照能够提供最佳性能的方法构建数据。

说了这么多,这里的示例是一个相当人为的示例,如果您有一个"像"这样的东西的实际用例,那么实际用例可能会产生一个非常有趣的问题。实际案例的解决方案通常与虚构案例不同。但是现在你有事情要考虑了。

相关内容

  • 没有找到相关文章

最新更新