在MongoDB中搜索任何字段的值,而无需显式命名


70

我浏览了MongoDB文档,并用谷歌搜索了此问题,但找不到真正合适的答案。所以,这就是我想要的。假设我有一个包含以下元素的集合:

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

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

我的想法是这样的

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

会给我示例元素。

感谢您的帮助。


因此,您需要的是全文搜索之类的东西,而mongodb本身并未实现该搜索,另请参阅:mongodb.org/display/DOCS/Full+Text+Search+in+Mongo
asaaki 2011年

我认为这不是文本搜索问题。它是按值查询的。他们俩都有一个事实,即在mongo中没有本地支持;)
Remon van Vliet 2011年

1
@asaaki:与此同时,Mongo已实施了全文搜索。让我们删除我们的评论。
Dan Dascalescu 2014年

Answers:


42

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

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

如果您在mongo shell(在命令行中通过调用“ mongo”执行)内部工作,则可以使用此命令执行操作,其中“ collection”是您要使用的数据库中的集合名称。

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文档。


1
如果字符串已绕过\“周围的单词,则它将搜索转换为'and'搜索,而不是'or'搜索。{$ text:{$ search:” \“ jim \” \“ habit \”“ }}在文本索引中找到所有带有'jim'和'habit'的记录
Keith John Hutchison

1
@ dave-adelson如何使用正则表达式?我尝试在其中使用正则表达式,但给出了异常“ errmsg”:“ \” $ search \”具有错误的类型。预期的字符串,找到了RegEx”,查询:find({$ text:{$ search:/ version /}} )
维维克·辛格

31

这个类似问题的答案可以解决您的问题,为完整性起见,我将在此重复。您可以使用$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,因此应首选使用。-来源
CodeFinity

26

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

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

这显然有一些缺点(主要是性能和受污染的架构),但可以通过以下方式满足您的需求:

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

如何使用此架构按特定字段排序?我的意思是在OP的内容中类似.sort({foo:1})。
runTarm 2012年

1
你不知道 由于对数组进行排序的方式,find({'params.field':“ foo”})。sort({'params.value':1})是不等效的。
Remon van Vliet

我们如何为Spring Data mongo查询编写存储库方法?请,请帮助我。.谢谢,Neha

全文搜索更简单。
基思·约翰·哈奇森

这似乎与达夫·阿德尔森的最高票数答案相矛盾。那是因为这个答案已经过时了吗?或者,当您说“服务器端代码执行”时,您是指创建索引吗?还是您建议该选项是有限的,因为它仅搜索文本而不搜索其他类型?
德鲁·纳特

20

遗憾的是,先前的答案都没有解决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将返回'object',这意味着查询将在所有嵌套元素上进行迭代,并在所有过程中进行迭代,直到找到具有值的键。

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

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


3
我们可以重写if(obj [field] == value)就像-if(typeof obj [field] =='string'&& obj [field] .contains(/ value /))。它提供了更加准确的搜索结果
Lokesh Boran

@LokeshBoran它给了我这个错误TypeError:无效类型:首先不能是正则表达式:\ ndeepIterate @:4:49 \ ndeepIterate @:9:25 \ n @:15:12 \ n
prakharjain

@prakharjain您可以将代码发布到粘贴容器中吗?那么我们可以看看吗?
twboc

4

您可以使用递归函数:

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');

4

使用$ where与执行全表扫描相同,并且不能使用索引。我也无法使其正常工作,但是我确实找到了这个工作(它也等效于全表扫描):

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

在其中/needle/的值中找到的正则表达式在哪里doc[key]



-11
{
   "foo" : "bar",
   "test" : "test",
   "key" : "value",
}

db.collection_name.find({'foo':"bar"})

这不是OP所要求的。问题是如何在不指定字段名称的情况下查找项目。
user2223059
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.