• MongoDB对Javascript的支持


    在项目中MongoDB的Map-Reduce功能做了许多统计任务,在重构代码的时候修改了_id对象里面的属性字段名称,当用db.collection.update({$rename:{"_id.a":"_id.b"}})的时候提示mongodb $rename affecting _id not allowed错误消息。于是只能通过写bash shell脚本来进行处理,之前学习MongoDB Manual时候里面有提到服务器端Javascript的特性但一直没去详细了解,今天正好有这个需求就查了下资料。

    自MongoDB 2.4以后的版本采用V8引擎执行所有Javascript代码,允许同时运行多个Javascript操作,在2.4版以前在执行Javascript的时候要求先获取锁,所以一次只能执行一个Javascript操作;MongoDB提供了对Javascript的完整支持,如在Node.js、MongoDB服务器端和mongo shell里面都提供非常好的支持。

    MongoDB对下列服务器端操作支持执行Javascript代码:

    1)mapReduce或者相对应的mongo shell方法db.collection.mapReduce();

    2)eval命令或者相对应的mongo shell方法db.eval()

    3)$where操作符

    4)在服务器端通过mongo shell实例运行.js文件

     

    下面重点说说$where操作符以及在服务器端运行.js文件:

    $where操作符能够传递Javascript表达式字符串或者完整的Javascript函数到MongDB的查询系统。$where操作符为我们在做复杂查询时提供极大的灵活性,但是要求数据库对于Collection中的每条记录都执行一次$where提供的Javascript处理操作,所以会对查询性能有明显的影响。因此只有当我们不能用标准的MongoDB查询操作符完成查询操作时才推荐使用。

    使用$where也应该注意以下几点:

    1.使用$where操作不能够利用数据库索引。

    2.不能在$where Javascript表达式或函数里面执行数据库写操作。

    3.为了避免对整个Collection文档进行扫描并在每条记录上执行Javascript代码,应至少包含一个其他标准的查询操作符如($gt,$in)对结果集先进行必要的过滤减少性能损耗。

    那么什么情况下必须使用$where操作符呢?

    当我们有需要比较同一记录里面不同字段的关系如this.credits==this.debits时,这种查询操作标准操作符没有提供支持,所以必须通过$where来实现;再比如当有一个字段是一个数组时,当我们想要通过数组的大小来过滤查询时,标准操作符$size只能接受固定大小的值如{cards:{ $size:3}},不能实现 {cards:{$size:{$gt:3}}} 这种查询,然而通过$where可以轻松实现这个需求。

    看下面的Example Code:

    db.myCollection.find( { $where: "this.credits == this.debits" } );
    db.myCollection.find( { $where: "obj.credits == obj.debits" } );
    
    db.myCollection.find( { $where: function() { return (this.credits == this.debits) } } );
    db.myCollection.find( { $where: function() { return obj.credits == obj.debits; } } );
    db.myCollection.find( { $where: function() { return this.cards.length>3; } } );

    注意:这里this或者obj对象代表的是当前正在操作的记录。

    另外如果查询仅仅由$where组成,这可以直接采用下面的用法:

    db.myCollection.find( "this.credits == this.debits || this.credits > this.debits" );
    
    db.myCollection.find( function() { return (this.credits == this.debits || this.credits > this.debits ) } );

    标准的查询操作符和$where一起使用:

    db.myCollection.find( { active: true, $where: "this.credits - this.debits < 0" } );
    db.myCollection.find( { active: true, $where: function() { return obj.credits - obj.debits < 0; } } );

    注意:为了改善查询性能MongoDB在$where执行前先执行所有其他非$where过滤条件来过滤结果集。在MongoDB 2.4及以后的版本的map-reduce、group和$where里面禁止访问某些全局变量,如db等。

     在Javascript文件里面写mongo shell脚本:

    我们能够在Javascript文件里面写mongo shell脚本来操纵在MongoDB里面的数据或执行管理操作。

    从mongo shell或者Javascript文件里面实例化数据库连接:

    conn = new Mongo("[host][:port]"); 
    db = conn.getDB("myDatabase");
    //or
    db = connect("localhost:27020/myDatabase");

    同时MongoDB在Javascript里面提供了和在mongo shell里面的命令相对应的:如在mongo shell里面的show dbs,show databases,在Javascript里面可以通过db.adminCommand("listDatabases");获得同样的效果,又如在Javascript文件里面可以通过db=db.getSiblingDB("dbName");代替use dbName操作等等,详细列表查看:http://docs.mongodb.org/manual/tutorial/write-scripts-for-the-mongo-shell/

     

    执行服务器端Javascript的几种方式:

    1.在mongo shell里面通过load("myjstest.js")加载Javascript文件,然后在后面的命令里面可以调用该文件里面定义的函数了,就像普通的Javascript代码一样。在该js文件里面可以访问此次mongo shell会话里面的全局变量,如db等。

    Example Code:

    function rename_id_field(taskId,mrId){
         print("taskId is "+taskId+",mapReduceId is "+mrId);
         var collectionName = "test_collection";
         var backupName = "test_collection_bak"
         var count = db[collectionName].count({"value.taskId":taskId,"value.mapReduceId":mrId});
         print("record size:"+count);
         var pageSize=10000;
         var pages = (count-1)/pageSize+1;
         for(var p=1;p<=pages;p++){
            var start = (p-1)*pageSize;
            print("Page:"+p+",start record:"+start);         
            var cursor = db[cName].find({"value.taskId":taskId,"value.mapReduceId":mrId}).skip(start).limit(pageSize);
            while(cursor.hasNext()){
               var rd = cursor.next();
               rd._id.key=rd._id.name1;
               rd._id.tid=rd.value.taskId;
               delete rd._id.name1;delete rd.value.taskId;
               db[backupName].save(rd);
            }
         }
    
    }

    将该文件保存在/opt/script/test.js,然后通过Linux命令行或者bash shell脚本的方式登陆到mongo shell,然后如下图所示:

     

    2.在mongo shell里面使用文本编辑器编辑Javascript代码,首先需要在Linux系统里面指定环境变量如EDITOR=vim,然后如下所示:

    MongoDB shell version: 2.2.0
    > function f() {}
    > edit f
    function f() {
        print("this really works");
    }
    > f()
    this really works

     

    3.通过db.eval()方式在mongo shell里面执行Javascript代码,如下所示:

    db.eval( function(name, incAmount) {
                var doc = db.myCollection.findOne( { name : name } );
    
                doc = doc || { name : name , num : 0 , total : 0 , avg : 0 };
    
                doc.num++;
                doc.total += incAmount;
                doc.avg = doc.total / doc.num;
    
                db.myCollection.save( doc );
                return doc;
             },
             "eliot", 5 );

     

    通过MongoDB提供的这几种Javascript服务器端支持方式足以满足我们对MongoDB数据库进行复杂日常运行维护的管理工作。

  • 相关阅读:
    类型初始值设定项引发异常的解决方法
    sql修改排序规则,区分大小
    SQLServer查询所有子节点
    Cannot resolve the collation conflict between "Chinese_PRC_CI_AS" and "SQL_L及由于排序规则不同导致查询结果为空的问题
    SQLServer跨库查询--分布式查询
    DataTable对象的操作问题
    .Net插入大批量数据
    SQL修改字段类型
    数据抓包分析
    Qss 皮肤
  • 原文地址:https://www.cnblogs.com/javaminer/p/3684695.html
Copyright © 2020-2023  润新知