在 MongoDB 中搜索任何字段的值而不显式命名它

Posted

技术标签:

【中文标题】在 MongoDB 中搜索任何字段的值而不显式命名它【英文标题】:Searching for value of any field in MongoDB without explicitly naming it 【发布时间】:2011-10-11 01:46:25 【问题描述】:

我查看了 MongoDB 文档并在 Google 上搜索了这个问题,但找不到合适的答案。所以,这就是我要找的。 假设我有一个包含这样元素的集合:


   "foo" : "bar",
   "test" : "test",
   "key" : "value",

我想要实现的是通过在所有(可能除了有限多个 ;-) )字段中搜索来找到一个元素。换句话说:给定一个查询,我不知道应该在哪个字段中找到查询。

在我看来是这样的

db.things.find(_ANY_ : "bar") 

会给我示例元素。

感谢您的帮助。

【问题讨论】:

所以,您需要的是全文搜索,这在 mongodb 中没有原生实现,另请参阅:mongodb.org/display/DOCS/Full+Text+Search+in+Mongo 我不认为这是一个文本搜索问题。它是按值查询。他们都分享了这样一个事实,即在 mongo 中没有对它的原生支持;) @asaaki:与此同时,Mongo 已经实现了full-text search。让我们删除我们的 cmets。 【参考方案1】:

This answer 对类似问题有您的解决方案,为了完整起见,我将在此重复。您可以使用$where 运算符在 MongoDB 服务器上运行任意 javascript,但需要注意的是这将比几乎任何其他类型的查询慢得多。对于您的示例,它将是:

db.things.find($where: function() 
    for (var key in this) 
        if (this[key] === "bar") 
            return true;
        
    
    return false;
);

【讨论】:

return false; 应该是进一步向下的一个括号 "在 3.6 版中更改:$expr 运算符允许在查询语言中使用聚合表达式。$expr 比 $where 更快,因为它不执行 JavaScript,应尽可能首选。" - Source【参考方案2】:

遗憾的是,之前的答案都没有解决 mongo 可以在数组或嵌套对象中包含嵌套值的事实。

这是正确的查询:

$where: function() 
    var deepIterate = function  (obj, value) 
        for (var field in obj) 
            if (obj[field] == value)
                return true;
            
            var found = false;
            if ( typeof obj[field] === 'object') 
                found = deepIterate(obj[field], value)
                if (found)  return true; 
            
        
        return false;
    ;
    return deepIterate(this, "573c79aef4ef4b9a9523028f")

由于在数组或嵌套对象上调用 typeof 将返回“对象”,这意味着查询将迭代所有嵌套元素,并将遍历所有元素,直到找到具有值的键。

您可以使用嵌套值检查以前的答案,结果将远非预期。

对整个对象进行字符串化的性能要差得多,因为它必须逐个遍历所有内存扇区以尝试匹配它们。并在 ram 内存中创建对象的副本作为字符串(由于查询使用更多的 ram,效率低下,因为函数上下文已经加载了对象)

【讨论】:

我们可以重写 if (obj[field] == value) 比如 -- if(typeof obj[field] == 'string' && obj[field].contains(/value/))。它提供了更准确的搜索结果 @LokeshBoran 它给了我这个错误 TypeError: Invalid type: first can't be a Regular Expression :\ndeepIterate@:4:49\ndeepIterate@:9:25\n@:15:12 \n @prakharjain 你能把你的代码贴在一个粘贴箱里吗?那么我们可以看看吗?【参考方案3】:

使用$where 和做全表扫描一样,不能使用索引。我也无法让它工作,但我确实发现它有效(它也相当于全表扫描):

db.collection.find().forEach(function(doc)
for (var key in doc) 
    if ( /needle/.test(doc[key]) )
        printjson(doc);
    
);

其中/needle/ 是在doc[key] 的值中查找的正则表达式

【讨论】:

【参考方案4】:

要对所有字段进行文本搜索,首先必须在所有字段上创建文本索引。

正如mongodb documentation 所指出的,“要允许对所有具有字符串内容的字段进行文本搜索,请使用通配符说明符 ($**) 来索引所有包含字符串内容的字段。”

如果您在 mongo shell 中工作(通过调用 'mongo' 从命令行执行),那么您可以使用此命令执行此操作,其中 'collection' 是您想要的 db 中集合的名称使用。

db.collection.createIndex( "$**": "text" , name: "TextIndex" )

第二个对象,即name:"TextIndex",是可选的...您实际上不需要为索引命名,因为每个集合只能有一个文本索引(一次...您可以如果需要,可以删除索引并创建新索引)。

在所有字段上创建文本索引后,您可以使用以下查询对象进行简单的文本搜索: $text : $search: <your string>

所以,如果您正在编写一个 javascript 函数,您可能会执行以下操作:

var cursor = db.collection(<collection_name>).find( $text: $search: <your string> );

有关控制搜索的各种方法的更多信息,请参阅有关文本搜索的 mongodb 文档here

【讨论】:

如果字符串已经转义了围绕一个单词的 \"s,那么它会将搜索变成一个 'and' 搜索而不是 'or' 搜索。 $text : $search: "\"jim \" \"habit\"" 在文本索引中查找所有带有 'jim' 和 'habit' 的记录。 @dave-adelson 如何使用正则表达式?我试图在其中使用正则表达式,但它给出了异常“errmsg”:“\”$search\”的类型错误。预期的字符串,找到了正则表达式”,查询:find($text:$search:/version/ )【参考方案5】:

要进行文本搜索,您必须为您的收藏创建文本索引。 有关更多信息,请查看 mongo 文档:indexes text

【讨论】:

【参考方案6】:

你可以用递归函数来做到这一点:

var recursiveSearch = function(query) 
    db.test_insert.find().forEach(function(items) 
        var i = 0;
        var recursiveFunc = function(itemsArray, itemKey) 
            var itemValue = itemsArray[itemKey];
            if(itemValue === query)  
                printjson(items);
                   

            if(typeof itemValue === "object") 
                Object.keys(itemValue).forEach(function(itemValueKey) 
                    recursiveFunc(itemValue, itemValueKey);
                );
            
        ;
        Object.keys(items).forEach(function(item)
            recursiveFunc(items, item);
        );
    );
;

recursiveSearch('your string');

【讨论】:

【参考方案7】:

如果不单独检查应用程序端的文档或通过服务器端的代码执行,这是不可能的。考虑将架构更改为:

params:[field:"foo", value:"bar", field:"test", value:"test", field:"key", value:"value"]

这显然有一些缺点(主要是性能和污染模式),但可以满足您的需求:

db.things.find('params.value':"bar")

【讨论】:

如何使用此架构按特定字段排序?我的意思是像 .sort(foo: 1) 在 OP 中的。 你没有。 find('params.field':"foo").sort('params.value':1) 不等价,因为数组排序的工作方式。 我们如何为 Spring Data mongo 查询编写存储库方法?请帮帮我..谢谢,Neha 全文搜索更简单。 这似乎与 dave adelson 的最高投票答案相矛盾。那是因为这个答案已经过时了吗?或者当您说“服务器端代码执行”时,您指的是创建索引吗?或者您是否建议该选项受到限制,因为它只搜索文本而不搜索其他类型?

以上是关于在 MongoDB 中搜索任何字段的值而不显式命名它的主要内容,如果未能解决你的问题,请参考以下文章

调用泛型方法而不显式指定类型参数

在 Sencha Touch 中设置 numberfield 的值而不触发“change”事件

在 SwiftUI 中捕获文本字段值而不按返回键 [重复]

如何在 django(DRF)中检索对象列表(包括 ForeignKey 字段数据)而不显着增加数据库调用时间

如何按显示的值而不是数据字段值对gridview列进行排序/过滤

Qt - 如何在不显式实现线程的情况下同时运行两个插槽