Mongodb源码分析--更新记录

mongodb源码分析(十)数据的插入 本文我们分析mongodb中数据的插入流程.插入的简单流程可以归结于如下: 1. 如果存在对应collection则从collection中分配空间,然后将数据保存到分配的空间中,不存在则先从database中分配对应的collection,若database不存在则分配database,建立xx.ns和xx.0 等文件. 2. 根据插入数据更新collection中的索引. 下面来看代码, 阅读详情

 在之前的一篇文章 中,介绍了assembleResponse函数(位于instance.cpp第224行),它会根据op操作枚举类型来调用相应的crud操作,枚举类型定义如下:
  
  1. enum  Operations {  
  2.     opReply  =   1 ,      /*  reply. responseTo is set.  */  
  3.     dbMsg  =   1000 ,     /*  generic msg command followed by a string  */  
  4.     dbUpdate  =   2001 ,  /*  更新对象  */  
  5.     dbInsert  =   2002 ,  
  6.      // dbGetByOID = 2003,  
  7.     dbQuery  =   2004 ,  
  8.     dbGetMore  =   2005 ,  
  9.     dbDelete  =   2006 ,  
  10.     dbKillCursors  =   2007  
  11. };  
 

    可以看到dbUpdate = 2001 为更新操作枚举值,下面我们看一下assembleResponse在确定是更新操作时调用的方法,如下:  
   
  1. // instance.cpp文件第224行  
  2.     assembleResponse( Message  & m, DbResponse  & dbresponse,  const  SockAddr  & client ) {  
  3.     .....  
  4.              try  {  
  5.                  if  ( op  ==  dbInsert ) {   // 添加记录操作  
  6.                     receivedInsert(m, currentOp);  
  7.                 }  
  8.                  else   if  ( op  ==  dbUpdate ) {  // 更新记录  
  9.                     receivedUpdate(m, currentOp);  
  10.                 }  
  11.                  else   if  ( op  ==  dbDelete ) {  // 删除记录  
  12.                     receivedDelete(m, currentOp);  
  13.                 }  
  14.                  else   if  ( op  ==  dbKillCursors ) {  // 删除Cursors(游标)对象  
  15.                     currentOp.ensureStarted();  
  16.                     logThreshold  =   10 ;  
  17.                     ss  <<   " killcursors  " ;  
  18.                     receivedKillCursors(m);  
  19.                 }  
  20.                  else  {  
  21.                     mongo::log()  <<   "     operation isn't supported:  "   <<  op  <<  endl;  
  22.                     currentOp.done();  
  23.                     log  =   true ;  
  24.                 }  
  25.             }  
  26.           .....  
  27.         }  
  28.     }  
 

    从上面代码可以看出,系统在确定dbUpdate操作时,调用了receivedUpdate()方法(位于instance.cpp文件第570行),下面是该方法的定义:


 

  1. void  receivedUpdate(Message &  m, CurOp &  op) {         
  2.         DbMessage d(m); // 初始化数据库格式的消息  
  3.          const   char   * ns  =  d.getns(); // 获取名空间,用于接下来insert数据  
  4.         assert( * ns);  
  5.          // 因为CUD操作在主库中操作,所以这里断言名空间包含的db信息中是不是主库,即"master"  
  6.         uassert(  10054  ,   " not master " , isMasterNs( ns ) );  
  7.         op.debug().str  <<  ns  <<   '   ' ;  
  8.          // 获取标志位信息(标识更新一条或多条等)关于消息结构体。有关消息结构参见我的这篇文章:  
  9.          // http://www.cnblogs.com/daizhj/archive/2011/04/02/2003335.html  
  10.          int  flags  =  d.pullInt();          
  11.          // 获取"更新消息"结构体中的selector(也就是要更新的数据条件,相关于where)  
  12.         BSONObj query  =  d.nextJsObj();  
  13.         assert( d.moreJSObjs() );  
  14.         assert( query.objsize()  <  m.header() -> dataLen() );  
  15.         BSONObj toupdate  =  d.nextJsObj(); // 要更新的记录  
  16.         uassert(  10055  ,  " update object too large " , toupdate.objsize()  <=  BSONObjMaxUserSize);  
  17.         assert( toupdate.objsize()  <  m.header() -> dataLen() );  
  18.         assert( query.objsize()  +  toupdate.objsize()  <  m.header() -> dataLen() );  
  19.          // 标识是否为upsert方式,即:如果存在就更新,如果不存在就插入  
  20.          bool  upsert  =  flags  &  UpdateOption_Upsert;  
  21.          // 是否更新所有满足条件(where)的记录  
  22.          bool  multi  =  flags  &  UpdateOption_Multi;  
  23.          // 是否更新所有节点(sharding状态)  
  24.          bool  broadcast  =  flags  &  UpdateOption_Broadcast;  
  25.         {  
  26.              string  s  =  query.toString();  
  27.              /*  todo: we shouldn't do all this ss stuff when we don't need it, it will slow us down. 
  28.                instead, let's just story the query BSON in the debug object, and it can toString() 
  29.                lazily 
  30.              */  
  31.             op.debug().str  <<   "  query:  "   <<  s;  
  32.             op.setQuery(query);  
  33.         }  
  34.         writelock lk;  
  35.          //  如果不更新所有节点(sharding)且当前物理结点是shard 状态时  
  36.          if  (  !  broadcast  &&  handlePossibleShardedMessage( m ,  0  ) )  
  37.              return ;  
  38.          // if this ever moves to outside of lock, need to adjust check Client::Context::_finishInit  
  39.         Client::Context ctx( ns );  
  40.         UpdateResult res  =  updateObjects(ns, toupdate, query, upsert, multi,  true , op.debug() ); // 更新对象  
  41.         lastError.getSafe() -> recordUpdate( res.existing , res.num , res.upserted );  //  for getlasterror  
  42.     }  

 

     上面的方法中,主要是对消息进行折包解析,找出要更新的数据记录及相应查询条件,以及更新方式(即upsert),然后再在“写锁”环境下执行更新数据操作。
   
     最终上面代码会调用 updateObjects()方法,该方法定义如下:

 
  1. // update.cpp 文件第1279行  
  2.  UpdateResult updateObjects( const   char   * ns,  const  BSONObj &  updateobj, BSONObj patternOrig,  bool  upsert,  bool  multi,  bool  logop , OpDebug &  debug ) {  
  3.        // 断言记录的ns是否在"保留的$集合"中  
  4.       uassert(  10155  ,  " cannot update reserved $ collection " , strchr(ns,  ' $ ' )  ==   0  );  
  5.        if  ( strstr(ns,  " .system. " ) ) {  
  6.         /*  dm: it's very important that system.indexes is never updated as IndexDetails has pointers into it  */  
  7.           uassert(  10156  , str::stream()  <<   " cannot update system collection:  "   <<  ns  <<   "  q:  "   <<  patternOrig  <<   "  u:  "   <<  updateobj , legalClientSystemNS( ns ,  true  ) );  
  8.       }  
  9.        return  _updateObjects( false , ns, updateobj, patternOrig, upsert, multi, logop, debug);  
  10.   }  



    上面方法对要更新的ns进行判断,以避免因更新保留的集合而对系统结构造成损坏,如果一切正常,则调用 _updateObjects方法,如下:

   
  1. // update.cpp 文件第1027行  
  2.   UpdateResult _updateObjects( bool  god,  const   char   * ns,  const  BSONObj &  updateobj, BSONObj patternOrig,  bool  upsert,  bool  multi,  bool  logop , OpDebug &  debug, RemoveSaver *  rs ) {  
  3.       DEBUGUPDATE(  " update:  "   <<  ns  <<   "  update:  "   <<  updateobj  <<   "  query:  "   <<  patternOrig  <<   "  upsert:  "   <<  upsert  <<   "  multi:  "   <<  multi );  
  4.       Client &  client  =  cc();  
  5.        int  profile  =  client.database() -> profile;  
  6.       StringBuilder &  ss  =  debug.str;  
  7.        if  ( logLevel  >   2  )  
  8.           ss  <<   "  update:  "   <<  updateobj.toString();  
  9.        /*  idea with these here it to make them loop invariant for multi updates, and thus be a bit faster for that case  */  
  10.        /*  NOTE: when yield() is added herein, these must be refreshed after each call to yield!  */  
  11.       NamespaceDetails  * d  =  nsdetails(ns);  //  can be null if an upsert...  
  12.       NamespaceDetailsTransient  * nsdt  =   & NamespaceDetailsTransient::get_w(ns);  
  13.        /*  end note  */  
  14.       auto_ptr < ModSet >  mods; // 定义存储修改信息操作(如$inc, $set, $push,)的集合实例  
  15.        bool  isOperatorUpdate  =  updateobj.firstElement().fieldName()[ 0 ]  ==   ' $ ' ;  
  16.        int  modsIsIndexed  =   false ;  //  really the # of indexes  
  17.        if  ( isOperatorUpdate ) {  
  18.            if ( d  &&  d -> indexBuildInProgress ) { // 如果正在构建索引  
  19.                set < string >  bgKeys;  
  20.             d -> inProgIdx().keyPattern().getFieldNames(bgKeys); // 获取当前对象的所有字段(field)信息  
  21.             mods.reset(  new  ModSet(updateobj, nsdt -> indexKeys(),  & bgKeys)); // 为mods绑定操作信息  
  22.           }  
  23.            else  {  
  24.               mods.reset(  new  ModSet(updateobj, nsdt -> indexKeys()) ); // 为mods绑定操作信息;  
  25.           }  
  26.           modsIsIndexed  =  mods -> isIndexed();  
  27.       }  
  28.        // upsert:如果存在就更新,如果不存在就插入  
  29.        if (  ! upsert  &&   ! multi  &&  isSimpleIdQuery(patternOrig)  &&  d  &&   ! modsIsIndexed ) {  
  30.            int  idxNo  =  d -> findIdIndex();  
  31.            if ( idxNo  >=   0  ) {  
  32.               ss  <<   "  byid  " ;  
  33.                // 根据id更新记录信息  
  34.                return  _updateById(isOperatorUpdate, idxNo, mods. get (), profile, d, nsdt, god, ns, updateobj, patternOrig, logop, debug);  
  35.           }  
  36.       }  
  37.        set < DiskLoc >  seenObjects;  
  38.        int  numModded  =   0 ;  
  39.        long   long  nscanned  =   0 ;  
  40.       MatchDetails details;  
  41.        // 构造“更新操作”实例对象并用其构造游标操作(符)实例  
  42.       shared_ptr <  MultiCursor::CursorOp  >  opPtr(  new  UpdateOp( mods. get ()  &&  mods -> hasDynamicArray() ) );  
  43.        // 构造MultiCursor查询游标(参见其构造方法中的 nextClause()语句)  
  44.       shared_ptr <  MultiCursor  >  c(  new  MultiCursor( ns, patternOrig, BSONObj(), opPtr,  true  ) );  
  45.       auto_ptr < ClientCursor >  cc;  
  46.        while  ( c -> ok() ) { // 遍历(下面的c->advance()调用)游标指向的记录信息  
  47.           nscanned ++ ;  
  48.            bool  atomic  =  c -> matcher() -> docMatcher().atomic();  
  49.            //  并将其与更新操作中的条件进行匹配  
  50.            if  (  !  c -> matcher() -> matches( c -> currKey(), c -> currLoc(),  & details ) ) {  
  51.               c -> advance(); // 将游标跳转到下一条记录  
  52.                if  ( nscanned  %   256   ==   0   &&   !  atomic ) {  
  53.                    if  ( cc. get ()  ==   0  ) {  
  54.                       shared_ptr <  Cursor  >  cPtr  =  c;  
  55.                       cc.reset(  new  ClientCursor( QueryOption_NoCursorTimeout , cPtr , ns ) );  
  56.                   }  
  57.                    if  (  !  cc -> yield () ) {  
  58.                       cc.release();  
  59.                        //  TODO should we assert or something?  
  60.                        break ;  
  61.                   }  
  62.                    if  (  ! c -> ok() ) {  
  63.                        break ;  
  64.                   }  
  65.               }  
  66.                continue ;  
  67.           }  
  68.           Record  * r  =  c -> _current(); // 游标当前所指向的记录  
  69.           DiskLoc loc  =  c -> currLoc(); // 游标当前所指向的记录所在地址  
  70.            //  TODO Maybe this is unnecessary since we have seenObjects  
  71.            if  ( c -> getsetdup( loc ) ) { // 判断当前记录是否是重复  
  72.               c -> advance();  
  73.                continue ;  
  74.           }  
  75.           BSONObj js(r);  
  76.           BSONObj pattern  =  patternOrig;  
  77.            if  ( logop ) { // 记录日志  
  78.               BSONObjBuilder idPattern;  
  79.               BSONElement id;  
  80.                //  NOTE: If the matching object lacks an id, we'll log  
  81.                //  with the original pattern.  This isn't replay-safe.  
  82.                //  It might make sense to suppress the log instead  
  83.                //  if there's no id.  
  84.                if  ( js.getObjectID( id ) ) {  
  85.                   idPattern.append( id );  
  86.                   pattern  =  idPattern.obj();  
  87.               }  
  88.                else  {  
  89.                   uassert(  10157  ,   " multi-update requires all modified objects to have an _id "  ,  !  multi );  
  90.               }  
  91.           }  
  92.            if  ( profile )  
  93.               ss  <<   "  nscanned: "   <<  nscanned;  
  94.           ......     
  95.           uassert(  10158  ,   " multi update only works with $ operators "  ,  !  multi );  
  96.       // 查看更新记录操作的时间戳,本人猜测这么做可能因为mongodb会采用最后更新时间戳解决分布式系统  
  97.       // 一致性的问题, 也就是通常使用的Last write wins准则,有关信息可参见这篇文章:  
  98.       // http://blog.mongodb.org/post/520888030/on-distributed-consistency-part-5-many-writer  
  99.           BSONElementManipulator::lookForTimestamps( updateobj );  
  100.           checkNoMods( updateobj );  
  101.            // 更新记录  
  102.           theDataFileMgr.updateRecord(ns, d, nsdt, r, loc , updateobj.objdata(), updateobj.objsize(), debug, god);  
  103.            if  ( logop ) { // 记录日志操作  
  104.               DEV  if ( god ) log()  <<   " REALLY?? "   <<  endl;  //  god doesn't get logged, this would be bad.  
  105.               logOp( " u " , ns, updateobj,  & pattern );  
  106.           }  
  107.            return  UpdateResult(  1  ,  0  ,  1  ); // 返回操作结果  
  108.       }  
  109.        if  ( numModded )  
  110.            return  UpdateResult(  1  ,  1  , numModded );  
  111.       ......  
  112.        return  UpdateResult(  0  ,  0  ,  0  );  
  113.   }  
 



     上面的代码主要执行构造更新消息中的查询条件(selector)游标,并将“游标指向”的记录遍历出来与查询条件进行匹配,如果匹配命中,则进行更 新。(有关游标的构造和继承实现体系,mongodb做的有些复杂,很难一句说清,我会在本系列后面另用篇幅进行说明)
    
    注意上面代码段中的这行代码:

  
  1. theDataFileMgr.updateRecord(ns, d, nsdt, r, loc , updateobj.objdata(), updateobj.objsize(), debug, god);  
 

    该方法会执行最终更新操作,其定义如下:

  1. // pdfile.cpp 文件934行  
  2.        const  DiskLoc DataFileMgr::updateRecord(  
  3.          const   char   * ns,  
  4.         NamespaceDetails  * d,  
  5.         NamespaceDetailsTransient  * nsdt,  
  6.         Record  * toupdate,  const  DiskLoc &  dl,  
  7.          const   char   * _buf,  int  _len, OpDebug &  debug,   bool  god) {  
  8.         StringBuilder &  ss  =  debug.str;  
  9.         dassert( toupdate  ==  dl.rec() );  
  10.         BSONObj objOld(toupdate);  
  11.         BSONObj objNew(_buf);  
  12.         DEV assert( objNew.objsize()  ==  _len );  
  13.         DEV assert( objNew.objdata()  ==  _buf );  
  14.          // 如果_buf中不包含_id,但要更新的记录(toupdate)有_id  
  15.          if (  ! objNew.hasElement( " _id " )  &&  objOld.hasElement( " _id " ) ) {  
  16.              /*  add back the old _id value if the update removes it.  Note this implementation is slow 
  17.                (copies entire object multiple times), but this shouldn't happen often, so going for simple 
  18.                code, not speed. 
  19.              */  
  20.             BSONObjBuilder b;  
  21.             BSONElement e;  
  22.             assert( objOld.getObjectID(e) ); // 获取对象objOld的ID并绑定到e  
  23.             b.append(e);  //  为了最好的性能,先放入_id  
  24.             b.appendElements(objNew);  
  25.             objNew  =  b.obj();  
  26.         }  
  27.          /* 重复key检查 */  
  28.         vector < IndexChanges >  changes;  
  29.          bool  changedId  =   false ;  
  30.          // 获取要修改的索引信息(包括要移除和添加的index key,并将结果返回给changes)  
  31.         getIndexChanges(changes,  * d, objNew, objOld, changedId);  
  32.          // 断言是否要修改_id索引  
  33.         uassert(  13596  , str::stream()  <<   " cannot change _id of a document old: "   <<  objOld  <<   "  new: "   <<  objNew ,  !  changedId );  
  34.         dupCheck(changes,  * d, dl); // 重复key检查,如果重复则通过断言终止当前程序  
  35.          // 如果要更新的记录比最终要插入的记录尺寸小  
  36.          if  ( toupdate -> netLength()  <  objNew.objsize() ) {  
  37.              //  如不合适,则重新分配  
  38.             uassert(  10003  ,  " failing update: objects in a capped ns cannot grow " ,  ! (d  &&  d -> capped));  
  39.             d -> paddingTooSmall();  
  40.              if  ( cc().database() -> profile )  
  41.                 ss  <<   "  moved  " ;  
  42.            // 删除指定的记录(record),删除操作详见我的这篇文章:  
  43.            // http://www.cnblogs.com/daizhj/archive/2011/04/06/mongodb_delete_recode_source_code.html  
  44.             deleteRecord(ns, toupdate, dl);  
  45.              // 插入新的BSONObj信息,插入操作详见我的这篇文章:  
  46.              // http://www.cnblogs.com/daizhj/archive/2011/03/30/1999699.html  
  47.              return  insert(ns, objNew.objdata(), objNew.objsize(), god);  
  48.         }  
  49.         nsdt -> notifyOfWriteOp();  
  50.         d -> paddingFits();  
  51.          /*  如果有要修改的索引  */  
  52.         {  
  53.             unsigned keyUpdates  =   0 ;  
  54.              int  z  =  d -> nIndexesBeingBuilt(); // 获取索引(包括正在构建)数  
  55.              for  (  int  x  =   0 ; x  <  z; x ++  ) {  
  56.                 IndexDetails &  idx  =  d -> idx(x);  
  57.                  // 遍历当前更新记录要修改(移除)的索引键信息  
  58.                  for  ( unsigned i  =   0 ; i  <  changes[x].removed.size(); i ++  ) {  
  59.                      try  {  
  60.                          // 移除当前记录在索引b树中相应信息(索引键)  
  61.                         idx.head.btree() -> unindex(idx.head, idx,  * changes[x].removed[i], dl);  
  62.                     }  
  63.                      catch  (AssertionException & ) {  
  64.                         ss  <<   "  exception update unindex  " ;  
  65.                         problem()  <<   "  caught assertion update unindex  "   <<  idx.indexNamespace()  <<  endl;  
  66.                     }  
  67.                 }  
  68.                 assert(  ! dl.isNull() );  
  69.                  // 获取指定名称(key)下的子对象  
  70.                 BSONObj idxKey  =  idx.info.obj().getObjectField( " key " );  
  71.                 Ordering ordering  =  Ordering::make(idxKey); // 生成排序方式  
  72.                 keyUpdates  +=  changes[x].added.size();  
  73.                   
  74.                  // 遍历当前更新记录要修改(插入)的索引键信息  
  75.                  for  ( unsigned i  =   0 ; i  <  changes[x].added.size(); i ++  ) {  
  76.                      try  {  
  77.                          // 之前做了dupCheck()操作,所以这里不用担心重复key的问题  
  78.                          // 在b树中添加索引键信息,有关该方法的定义参见我的这篇文章  
  79.                          // http://www.cnblogs.com/daizhj/archive/2011/03/30/1999699.html  
  80.                         idx.head.btree() -> bt_insert(  
  81.                             idx.head,  
  82.                             dl,  * changes[x].added[i], ordering,  /* dupsAllowed */ true , idx);  
  83.                     }  
  84.                      catch  (AssertionException &  e) {  
  85.                         ss  <<   "  exception update index  " ;  
  86.                         problem()  <<   "  caught assertion update index  "   <<  idx.indexNamespace()  <<   "   "   <<  e  <<  endl;  
  87.                     }  
  88.                 }  
  89.             }  
  90.              if ( keyUpdates  &&  cc().database() -> profile )  
  91.                 ss  <<   ' /n '   <<  keyUpdates  <<   "  key updates  " ;  
  92.         }  
  93.          //   update in place  
  94.          int  sz  =  objNew.objsize();  
  95.          // 将新修改的记录信息复制到旧记录(toupdate)所在位置  
  96.         memcpy(getDur().writingPtr(toupdate -> data, sz), objNew.objdata(), sz);  
  97.          return  dl;  
  98.     }   



    上面代码段主要先对B树索引进行修改(这里采用先移除再重建方式),之后直接更新旧记录在内存中的数据,最终完成了记录的更新操作。

    最后,用一张时序图回顾一下更新记录时mongodb服务端代码的执行流程:

     



    好了,今天的内容到这里就告一段落了,在接下来的文章中,将会介绍Mongodb的游标(cursor)设计体系和实现方式。



    参考链接:
    http://www.cnblogs.com/daizhj/archive/2011/03/30/1999699.html
    http://www.cnblogs.com/daizhj/archive/2011/04/06/mongodb_delete_recode_source_code.html
    http://www.cnblogs.com/daizhj/archive/2011/04/02/2003335.html
    http://blog.mongodb.org/post/520888030/on-distributed-consistency-part-5-many-writer


    原文链接: http://www.cnblogs.com/daizhj/archive/2011/04/08/mongodb_update_recode_source_code.html

    作者: daizhj, 代震军    
    微博: http://t.sina.com.cn/daizhj 
    Tags: mongodb,c++,source code

MongoDB原理与代码实例讲解 MongoDB, NoSQL, 数据库, 文档存储, 索引, 查询, 聚合, 性能优化 1. 背景介绍 随着互联网和移动互联网的蓬勃发展,数据量呈指数级增长,传统的关系型数据库在处理海量数据和复杂查询时面临着越来越多的挑战。为了应对这些挑战,NoSQL数据库应运而生,其中MongoD 阅读详情

相关推荐

mongodb 3.6.3 源码

mongodb 最新版本 3.6.3 源代码,由于防火墙的关系,国内很难下载下来,欢迎大家下载。

source code compiled install mongodb

为什么80%的码农都做不了架构师?>>> ...

weixin_34138139的博客 152

mongoDb源码和笔记

mongoDb详细笔记和源码,如果对mongoDb感兴趣的可以好好看看源码

mongoDB的c++接口的说明

说明: * IN表示输入参数; * OUT表示输出参数; (1)构造函数: DBClientConnection(bool auto_connect, 0, double so_timeout); auto_connect(IN):连接失败后自动重连so_timeout(IN):非连接超时,tcp的读写超时 (2)连接mongo: bool connect(string

把握自己。 3899

PowerBI参数化实战:3分钟搞定动态数据源切换(附常见错误排查)

本文详细介绍了PowerBI参数化实战技巧,教你3分钟实现动态数据源切换。通过创建参数、改造查询和测试切换三步,轻松应对多环境部署与团队协作中的路径问题,并附上常见错误排查指南,有效提升报表的可维护性和工作效率。

p1q2r的博客 585

Avellaneda & Stoikov模型实战解析:如何动态调整做市策略参数

本文深入解析Avellaneda & Stoikov模型在动态调整做市策略参数中的应用,重点探讨库存状态、剩余时间、风险系数和订单深度等核心参数的实战技巧。通过加密货币市场的实际案例,展示如何根据市场波动和流动性动态调整参数,提升做市策略的盈利能力和风险控制水平。

weixin_29070861的博客 202

MongoDB 3.7.3 发布,支持 Python 3

MongoDB 3.7.3 发布了,MongoDB是一个分布式文档存储数据库。更新内容:Support python 3Stricter systemd .service settingsOptimize $filter + $arrayElemAt to avoid scanning entire arrayAdd AArch64 Support for FTS fast byte vector...

lsxf_xin的专栏 1万+

手把手实战:快速构建合同智能审查Agent

配置“模型供应商”,选择 “OpenAI-API-compatible”,输入申请到的 “API Key” 和 “URL” 后点击“保存”,如下图示例完成 “AlayaNew/DeepSeek-R1。至此,我们完成了合同智能审核Agent的构建,我们也可以在Dify中把这个智能体发布为API服务,这样企业其它应用如OA就可以通过API集成,在原合同流程中实现智能审核工作。该节点“输入变量”中,配置从“开始”节点中获取上传的文件,输出变量就是合同的内容,配置如下图。接下来我们开始创建智能体。

weixin_46880696的博客 4403

MongoDB源码研究系列文章(转)

[url]http://blog.nosqlfan.com/html/1743.html[/url] 本文推荐给大家的是代震军博客中的一系列MongoDB源码研究文章。文章对MongoDB各个部分的源码做了研究并进行了流程机制方面的描述,其中不乏对源码的直接注释与说明。对 MongoDB感兴趣的同学,可以通过这一系列文章对感兴趣的机制进行深入的了解。下面是文章列表: [url=...

wen66 129

QT6与QT5安装全攻略:从下载到环境配置一站式指南

本文提供了一份详尽的QT6与QT5安装教程,涵盖从版本选择、在线安装器使用到环境变量配置的全过程。针对新手常见痛点,重点对比了QT6与QT5的安装差异,并给出了MinGW与MSVC编译器的选择建议,旨在帮助开发者快速搭建稳定可用的QT开发环境,避免常见错误。

weixin_29216353的博客 311

【AI黑话日日新】什么是AI云原生?

云原生赋能AI:利用容器、微服务、Serverless、DevOps等云原生技术,解决AI应用(尤其是大模型)在部署、扩展、运维、成本控制方面的痛点,让AI服务具备高可用、高性能、易运维、易扩展的特性AI赋能云原生:将AI技术(如智能调度、异常检测、自动优化)融入云基础设施管理,使云平台具备自我感知、自我优化、自我修复的能力,从"被动响应"升级为"主动适配"其本质是实现从""(AI运行在云上)到""(为AI量身打造的云)的根本性转变。

专注:太空算力、AI infra、异构调度、大模型成本工程 627

Mongodb源码分析--删除记录

在之前的一篇文章中,介绍了assembleResponse函数(位于instance.cpp第224行),它会根据op操作枚举类型来调用相应的crud操作,枚举类型定义如下...可以看到dbDelete = 2002 为删除操作枚举值。当客户端将要删除的记录(或条件的document)发到服务端之后,mongodb通过消息封装方式将数据包中的字节流解析转成 message类型,并进一步转换成dbmessage之后,mongodb就会根据消息类型进行判断,以决定接下来执行的操作),下面我们看一下 assemb

daizhj的专栏 4133

金融监管压力测试场景生成

压力测试是一种评估金融资产或机构在极端市场条件下的表现和风险承受能力的方法。它通过模拟各种不利的市场情景,测试金融机构在这些情景下的财务状况和运营能力。压力测试的作用在于帮助金融机构识别潜在风险,评估应对策略,并制定应急预案。总结压力测试场景生成系统的开发过程和实现效果,强调其在金融监管中的重要性和应用价值。随着金融市场的发展,实时压力测试将越来越重要。未来,压力测试需要更加注重实时性和动态性,及时反映市场的变化。本文详细介绍了金融监管压力测试的重要性、核心概念、算法原理、系统架构以及实战案例。

AI天才研究院 1208

Mongodb源码分析--插入记录及索引B树构建

http://www.cnblogs.com/daizhj/archive/2011/03/30/1999699.html Mongodb源码分析--插入记录及索引B树构建      在之前的一篇文章中,介绍了assembleResponse函数(位于instance.cpp第224行),它会根据op操作枚举类型来调用相应的crud操作,枚举类型定义如下:      enum

hanxinis的专栏 1032

ModuleNotFoundError: No module named‘langchain.schema.output parserlangchain.schema is not a package

电脑里现有的langchain库版本与import所需的不同,卸载原有langchain,重新下载最新的langchain。在步骤1后,若仍不可运行。在确保已重新下载新langchain后,重启python后,可正常运行。在版本号中填入所需的langchain版本号,可在requirements.txt查看。运行如下代码时,出现error。

weixin_45751925的博客 1713

生产环境中redis的SCAN命令如何替代KEYS命令?

本文介绍了使用SCAN命令替代KEYS的完整方案。SCAN通过游标分批迭代,避免KEYS命令遍历整个键空间导致的阻塞风险。文章详细说明了SCAN的基础语法、迭代流程和Java实现示例,强调必须关闭Cursor避免连接泄漏。针对生产环境,提供了COUNT参数调优、客户端去重、集群模式适配等优化建议,并列举了典型错误场景。SCAN通过分批次迭代和游标管理,可安全实现高性能键空间遍历。全文150字。

DN金猿的博客 179

MongoDB Java Driver 源码分析(3):com.mongodb.DBCollection

DBCollection 是表示数据集合的抽象类,它的实现可以简单地分为两类:   一类是抽象方法,由子类(DBApiLayer.MyCollection)实现;   另一类委托给类型为 "DB" 的属性 _db,_db 实际上是 DBApiLayer 类的实例(DBApiLayer 继承抽象类 DB);   因此,DBCollection 类是实现细节与 DBApiLayer 关系密切

流年、你奈我何 1327

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

上一篇: Mongodb源码分析--更新记录
下一篇: Mongodb源码分析--游标Cursor
jackfirst86
博客等级 码龄18年 28粉丝 27原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值