mongo二维数组操作

本文详细介绍了MongoDB中查询与更新嵌套数组中特定子文档的方法,包括如何筛选、切片数组,使用$elemMatch进行模糊条件查询,以及如何在更新操作中精确指定目标子文档。并通过实例演示了查询comments中score大于5的记录、指定返回字段、查询特定子文档等技巧,并阐述了更新子文档、插入与删除子文档的操作。最后,文章指出在使用$where查询时需注意其性能损耗与对索引的限制。

有2个嵌套的数组:mongodb <wbr>子文档查询

如果我想查询comments里score大于5的记录:
testProvider.find({"comments.score":{"$gt":5}},{},function(err, result){
     console.log(result);
});
返回了查找到的记录,说明了查找有效果了:
mongodb <wbr>子文档查询
这样查找返回的数据都是把匹配的整个文档返回过来了。
 
如果我觉得其中有个字段是没必要返回给我的可以设置这个字段为0:
testProvider.find({"comments.score":{"$gt":5}},{'comments.others':0},function(err, result) {
    console.log(JSON.stringify(result));
});
可以看到返回的结果中已经没有others这个字段了
mongodb <wbr>子文档查询

如果我想查询comments数组里面的某个子文档中others数组里面name为a的记录:
testProvider.find({"comments.others.name":'a'},{},function(err, result) {
    console.log(JSON.stringify(result));
});
返回的结果中的一部分如下图:
mongodb <wbr>子文档查询
 
从上面的几个示例中可以看到我想要查找某个符合条件的子文档时可以直接一直用'.'号表示下去而不用管其中是否有数组,如:{"comments.others.name":'a'}
一些高级的查询语法看这一篇: http://blog.sina.com.cn/s/blog_abba9c1d0101eti7.html
 

1、使用"$size"可以查询指定长度的数组

查询数组长度为3的数组db.users.find({"emails":{"$size":3}})

常见的查询是数组长度范围的查询."$size"并不能与其他查询子句组合(如:"$gt"),但是这种查询可以通过

在文档中添加一个"size"键的方式来实现.这样每一次向指定数组添加元素的时候,同时增加"size"值.原来这样

的更新:  db.users.update({"$push":{"emails":"295240648@139.com"}})

变成这样的更新:  db.users.update({"$push":{"emails":"295240648@139.com"},"$inc":{"size":1}})

这样就可以这样查询了 db.users.find({"size":{"$gt":3}})

2、使用"$slice"查询

find的第二个参数是可选的,可以指定返回那些键,"$slice"返回数组的一个子集合

返回emails数组的前两个元素   db.users.find({"userName":"refactor"},{"emails":{"$slice":2}})

返回emails数组的后两个元素   db.users.find({"userName":"refactor"},{"emails":{"$slice":-2}})

返回emails数组的第2个和第11个元素.如果数组不够11个,则返回第2个后面的所有元素

db.users.find({"userName":"refactor"},{"emails":{"$slice":[1,10]}})

"$slice"默认将返回文档中的所有键. 

3、要正确指定一组条件,而不用指定每个键,要使用"$elemMatch".这种模糊的命名条件能用来部分指定匹配数组中

的单个内嵌文档的限定条件.正确写法应该是:

db.blog.find(
  {
    "comments":
    {
      "$elemMatch":
      {
        "author":"refactor",
        "score":{"$gte":5}
      }
    }
  }
)

"$elemMatch"将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到.

 

4."$where"查询

"$where"可以执行任意javascript作为查询的一部分.这使得查询能做(几乎)任何事情.

最典型的应用就是比较文档中的两个键的值是否相等.

如:db.blog.insert({"title":"refactor","content":"refactor"})

    db.blog.insert({"title":"refactor1","content":"refactor content"})

第一个文档title键和content键的值相同.要返回该文档.

 

db.blog.find(
  {
    "$where":function()
          {
            for(var current in this)
            {
              for(var other in this)
              {
                if(current!=other&&this[current]==this[other])
                {
                  return true;
                }
              }
            }
            return false;
          }
  }
)
如果函数返回true,文档就作为结果的一部分被返回.

 

上面是用一个函数,也可以用一个字符串指定"$where"查询.下面两种方式是等价的:

db.blog.find({"$where":"this.x+this.y==10"})
db.blog.find({"$where":"function(){return this.x+this.y==10;}"})

不是非常必要时,应避免"$where"查询.它在速度上要比常规查询慢的多.每个文档都要从BSON转换成

javascript对象,然后通过"$where"的表达式来运行.它还不能利用索引.

将常规查询作为前置过滤,与"$where"组合使用才能不牺牲性能,如果有可能的话,用索引根据非"$where"

子句进行过滤,"$where"只用于对结果进行调优

 

上面的4个查询转自:http://www.cnblogs.com/refactor/archive/2012/07/30/2591344.html

                  http://www.cnblogs.com/refactor/archive/2012/07/31/2591544.html 

更新子文档:
在更新子文档时和查询时又有所不同(遇到数组时多加一个$符号):
testProvider.update({"comments.score":4},{'$set':{'comments.$.score':1}},{multi:true,w:1},function(err, numberUpdated) {
     console.log("numberUpdated: ", numberUpdated);
});
查询下可以发现结果已经改变了:
mongodb <wbr>子文档查询

如果我想往一个子文档的数组中插入一个子文档时可以使用push:
testProvider.update({"comments.score":1},{'$push':{'comments.$.others':{"name":"j","age":"24"}}},{multi:true,w: 1},function(err, numberUpdated) {
    console.log("numberUpdated: ", numberUpdated);
});
查询之后可以发现comments.others里多了一个子文档,表明已经插入进去了:
mongodb <wbr>子文档查询

如果我想把刚刚插入的子文档删除时,可以使用pull:
testProvider.update({"comments.score":1},{'$pull':{'comments.$.others':{"name":"j"}}},{multi:true,w: 1},function(err, numberUpdated) {
    console.log("numberUpdated: ", numberUpdated);
});
查询之后可以发现结果已经改变:
mongodb <wbr>子文档查询
注意:如下图所示pull值中的key对应的值必须为json格式的:
mongodb <wbr>子文档查询

mongodb <wbr>子文档查询
    我在使用的过程中还发现在遇到数组时使用'.$'符号时每个里面只能用一次;如上图的错误写法中就含有2个'.$'符号。所以如果子文档里嵌套有2个乃至更多数组时,很难准确的对嵌套的数组中的某一个元素进行更新。
找了一晚上,终于找到个合适的方法,都准备睡觉了,结构试了这个方法,确实可以,帮了我一个大忙啊
<think>嗯,用户这次的问题很明确:想用Kettle实现MongoDB到Oracle的数据同步。结合用户上次的问题历史,ta应该是在做异构数据库之间的数据迁移或实时同步,而且提到了业务系统对数据量和时效性的要求。从用户引用的三篇资料来看,关键点已经呼之欲出了:-引用[1]提到OracleCDC的异步模式,但用户这次是从MongoDB出发-引用[2]详细描述了Oracle到MongoDB的反向同步配置,正好可以借鉴连接配置部分-引用[3]则突出大数据量迁移的痛点用户可能遇到的实际难点应该是:1数据结构转换(文档数据库到关系型)2增量同步机制(ObjectIdvs时间戳)3大数据量下的性能优化注意到引用[3]提到"10T数据迁移"和"业务系统不能中断",所以方案必须包含:-全量+增量组合策略-避免锁表的写入方式-调度机制的健壮性上次我给出的方案里可能遗漏了两个细节:1)Oracle字段类型自动映射的风险2)kettle资源库的必要性。这次要特别强调字段类型的显式配置,毕竟MongoDB的嵌套文档转Oracle二维表很容易出现类型冲突。突然想到个隐患:如果源文档有嵌套数组,Kettle的MongoDB输入步骤会自动平铺成多行,可能导致目标表出现重复记录。这点必须在"转换设计"环节预警。用户应该需要看到具体的组件截图,但文字描述只能强调几个关键配置位置:连接字符串格式、字段选择器的使用、INSERT/UPDATE的合并策略。最后补上调度配置的说明比较稳妥——毕竟很多新手会忘记把转换保存成作业。</think> 基于Kettle的MongoDB到Oracle数据同步方案(含详细步骤) --- ### 一、同步流程设计 1. **连接配置** - **MongoDB连接**:在`数据库连接`中选择MongoDB类型,填写: ``` 主机名:localhost 端口:27017 数据库名:your_db 认证方式:用户名/密码(如需) ``` - **Oracle连接**:选择Oracle类型,填写JDBC信息: ``` jdbc:oracle:thin:@//host:port/service_name 用户名/密码 ``` 2. **数据抽取转换** ```mermaid graph TD A[MongoDB输入] --> B(字段类型转换) B --> C(Oracle表输出) C --> D[日志记录] ``` - **输入组件**:使用`MongoDB Input` - 配置集合名称 - 使用`JSON Path`提取字段(如`$.user_id`) - **转换组件**: - `Select values`调整字段顺序和命名 - `Convert data type`处理类型差异(如MongoDB的ObjectId转Oracle的VARCHAR2) 3. **写入策略**(核心配置) | 场景 | 配置方式 | Oracle执行模式 | |------------|-----------------------------|-----------------| | 全量覆盖 | 使用`Truncate table` | SQL直接执行 | | 增量同步 | `Insert/Update` + 时间戳过滤 | 批量提交 | | 数据合并 | 配置唯一索引字段 | MERGE语句 | 4. **调度配置** - 创建作业(Job)定时触发转换 - 添加成功/失败通知机制(邮件或日志) --- ### 二、关键代码示例 ```vbnet // MongoDB Input配置片段 MongoDB Query: {} Fields: { "name":"user_id", "path":"$_id", "type":"String" } { "name":"order_date", "path":"$date", "type":"Date" } // Oracle表输出SQL INSERT /*+ APPEND */ INTO orders (user_id, order_date) VALUES (?, ?) ``` --- ### 三、避坑指南 1. **类型映射陷阱** - MongoDB的`Decimal128` → Oracle的`NUMBER(38,10)` - 嵌套文档需用`JSON Extract`组件展开 2. **性能优化** - 启用Oracle的`Direct Path Load`(减少REDO日志) - 设置MongoDB读取批次大小(默认1000行) ```vbnet // 在MongoDB输入高级选项中 batch.size=5000 ``` 3. **增量同步方案** ```sql -- 在转换中使用SQL过滤 WHERE last_modified > '${LAST_SYNC_TIME}' ``` --- ### 四、实时同步进阶方案 若需秒级延迟: 1. 使用**Change Stream Capture**(MongoDB 3.6+) ```javascript // Kettle需调用MongoDB API db.collection.watch([{ $match: { operationType: "insert" } }]) ``` 2. 结合**Oracle MERGE语句** ```sql MERGE INTO target_table t USING source_data s ON (t.id = s.id) WHEN MATCHED THEN UPDATE SET t.value=s.value WHEN NOT MATCHED THEN INSERT VALUES(s.id, s.value) ``` --- **相关优化建议**: - 大数据量场景优先使用`Partitioning`插件并行处理 - 对MongoDB的`_id`字段建立投影减少网络传输 - Oracle侧建议启用`NOLOGGING`模式提升写入速度 > **操作警示**:生产环境同步前务必在测试库验证数据类型兼容性,特别是时空数据类型(如MongoDB的GeoJSON转Oracle的SDO_GEOMETRY)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值