<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Mustang Blog</title>
  
  <subtitle>天行健，君子以自强不息；地势坤，君子以厚德载物</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yuenshui.com/"/>
  <updated>2017-10-08T03:18:11.000Z</updated>
  <id>http://yuenshui.com/</id>
  
  <author>
    <name>于恩水(Mustang Yu)</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>NW.js填坑之window.open</title>
    <link href="http://yuenshui.com/2017/10/08/NW-js%E5%A1%AB%E5%9D%91%E4%B9%8Bwindow-open/"/>
    <id>http://yuenshui.com/2017/10/08/NW-js填坑之window-open/</id>
    <published>2017-10-08T02:25:12.000Z</published>
    <updated>2017-10-08T03:18:11.000Z</updated>
    
    <content type="html"><![CDATA[<p>江湖传闻NW的坑多，多得像繁星闪闪，很多坑没人填。<br>那我们就遇到一个填一个吧，总是会填一个少一个。<br>NV.js v0.13 官网文档：</p><hr><p><img src="/img/Snip20171008_5.png"></p><hr><p>上面是NW官网对Windows.open的解释。第一个参数URL，必选；第二个参数选项，可选；第三个参数回调，可选；<br>从文档看，回调函数可以获取窗口句柄，然后我们可以通过句柄进行子窗口的控制和通讯。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"#Button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">winHandler.close();</span><br><span class="line">&#125; <span class="keyword">catch</span> (e)&#123;</span><br><span class="line"><span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">require</span>(<span class="string">'nw.gui'</span>).Window.open(<span class="string">'view.html'</span>, &#123;</span><br><span class="line">new_instance: <span class="literal">true</span></span><br><span class="line">&#125;,<span class="function"><span class="keyword">function</span>(<span class="params">new_win</span>) </span>&#123;</span><br><span class="line">winHandler = new_win</span><br><span class="line">&#125;);</span><br><span class="line">&#125;);</span><br><span class="line">这段代码是无法实现每次新开窗口前关闭上次打开的窗口，此时回调函数的参数new_win是<span class="literal">null</span>。</span><br></pre></td></tr></table></figure></p><p>如果你有此想法，就不要用选项参数了，实测，如果使用第二个选项参数，回调函数获取不到窗口句柄。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">$(<span class="string">"#Button"</span>).click(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">winHandler.close();</span><br><span class="line">&#125; <span class="keyword">catch</span> (e)&#123;</span><br><span class="line"><span class="built_in">console</span>.log(e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">require</span>(<span class="string">'nw.gui'</span>).Window.open(<span class="string">'view.html'</span>, <span class="function"><span class="keyword">function</span>(<span class="params">new_win</span>) </span>&#123;</span><br><span class="line">winHandler = new_win</span><br><span class="line">&#125;);</span><br><span class="line">&#125;);</span><br><span class="line">这样，就能获取窗口句柄，实现控制和通信了。</span><br></pre></td></tr></table></figure></p><pre><code>———— 官网文档害死人啊^_^</code></pre>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;江湖传闻NW的坑多，多得像繁星闪闪，很多坑没人填。&lt;br&gt;那我们就遇到一个填一个吧，总是会填一个少一个。&lt;br&gt;NV.js v0.13 官网文档：&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;/img/Snip20171008_5.png&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;上面是
      
    
    </summary>
    
    
      <category term="NW.js" scheme="http://yuenshui.com/tags/NW-js/"/>
    
      <category term="window.open" scheme="http://yuenshui.com/tags/window-open/"/>
    
  </entry>
  
  <entry>
    <title>数据库NeDB文档翻译</title>
    <link href="http://yuenshui.com/2017/09/15/%E6%95%B0%E6%8D%AE%E5%BA%93NeDB%E6%96%87%E6%A1%A3%E7%BF%BB%E8%AF%91/"/>
    <id>http://yuenshui.com/2017/09/15/数据库NeDB文档翻译/</id>
    <published>2017-09-14T16:00:00.000Z</published>
    <updated>2017-10-08T07:54:00.000Z</updated>
    
    <content type="html"><![CDATA[<p>因为使用这个项目，自己的翻译分享出来，分享给大家，如果错误和遗漏，希望大家在Issues提醒指正。🙏<br>原文档地址：<a href="https://github.com/louischatriot/nedb" target="_blank" rel="noopener">https://github.com/louischatriot/nedb</a><br>如果翻译有误请指正，提交地址：<a href="https://github.com/yuenshui/NeDBdoc/issues" target="_blank" rel="noopener">https://github.com/yuenshui/NeDBdoc/issues</a>  </p><p><img src="http://i.imgur.com/9O1xHFb.png" style="width: 25%; height: 25%; float: left;"></p><h2 id="The-JavaScript-Database"><a href="#The-JavaScript-Database" class="headerlink" title="The JavaScript Database"></a>The JavaScript Database</h2><p><strong>Embedded persistent or in memory database for Node.js, nw.js, Electron and browsers, 100% JavaScript, no binary dependency</strong>. API is a subset of MongoDB’s and it’s <a href="#speed">plenty fast</a>.</p><p>为Node.js、nw.js、Electron、浏览器设计，100%的Javascript程序可使用的嵌入式或内存数据库，不依赖二进制库。数据库API是MongoDB的一个子集，运行速度非常快。</p><p><strong>IMPORTANT NOTE</strong>: Please don’t submit issues for questions regarding your code. Only actual bugs or feature requests will be answered, all others will be closed without comment. Also, please follow the <a href="#bug-reporting-guidelines">bug reporting guidelines</a> and check the <a href="https://github.com/louischatriot/nedb/wiki/Change-log" target="_blank">change log</a> before submitting an already fixed bug :)</p><p>重要提示：不要提交使用过程中的代码问题，只有数据库Bug或希望增加的功能才会回复。所有其他的评论都将关闭。另外提交bug前先看已经提交过的bug，然后再提交。</p><h2 id="Support-NeDB-development（赞助NeDB开发）"><a href="#Support-NeDB-development（赞助NeDB开发）" class="headerlink" title="Support NeDB development（赞助NeDB开发）"></a>Support NeDB development（赞助NeDB开发）</h2><p><img src="http://i.imgur.com/mpwi4lf.jpg"></p><p>No time to <a href="#pull-requests">help out</a>? You can support NeDB development by sending money or bitcoins!</p><p>Money: <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&amp;business=louis%2echatriot%40gmail%2ecom&amp;lc=US&amp;currency_code=EUR&amp;bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHostedGuest" target="_blank" rel="noopener"><img src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" alt="Donate to author"></a></p><p>Bitcoin address: 1dDZLnWpBbodPiN8sizzYrgaz5iahFyb1</p><h2 id="Installation-tests"><a href="#Installation-tests" class="headerlink" title="Installation, tests"></a>Installation, tests</h2><p>Module name on npm and bower is <code>nedb</code>.</p><p>在npm上的模块名字是nedb</p><figure class="highlight cmake"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm <span class="keyword">install</span> nedb --save    <span class="comment"># Put latest version in your package.json</span></span><br><span class="line">npm <span class="keyword">test</span>                   <span class="comment"># You'll need the dev dependencies to launch tests</span></span><br><span class="line">bower <span class="keyword">install</span> nedb         <span class="comment"># For the browser versions, which will be in browser-version/out</span></span><br></pre></td></tr></table></figure><h2 id="API"><a href="#API" class="headerlink" title="API"></a>API</h2><p>It is a subset of MongoDB’s API (the most used operations).</p><p>这是MongoDB的API子集，最常用的一部分操作</p><ul><li><a href="#creatingloading-a-database">Creating/loading a database</a></li><li><a href="#persistence">Persistence</a></li><li><a href="#inserting-documents">Inserting documents</a></li><li><a href="#finding-documents">Finding documents</a><ul><li><a href="#basic-querying">Basic Querying</a></li><li><a href="#operators-lt-lte-gt-gte-in-nin-ne-exists-regex">Operators ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex)</a></li><li><a href="#array-fields">Array fields</a></li><li><a href="#logical-operators-or-and-not-where">Logical operators $or, $and, $not, $where</a></li><li><a href="#sorting-and-paginating">Sorting and paginating</a></li><li><a href="#projections">Projections</a></li></ul></li><li><a href="#counting-documents">Counting documents</a></li><li><a href="#updating-documents">Updating documents</a></li><li><a href="#removing-documents">Removing documents</a></li><li><a href="#indexing">Indexing</a></li><li><a href="#browser-version">Browser version</a></li></ul><h3 id="Creating-loading-a-database"><a href="#Creating-loading-a-database" class="headerlink" title="Creating/loading a database"></a>Creating/loading a database</h3><p>You can use NeDB as an in-memory only datastore or as a persistent datastore. One datastore is the equivalent of a MongoDB collection. The constructor is used as follows <code>new Datastore(options)</code> where <code>options</code> is an object with the following fields:</p><p>可以吧NeDB作为内存数据库，当然也可以持久化存储。一个数据存储区相当于MongoDB的一个集合（collection）。</p><ul><li><code>filename</code> (optional): path to the file where the data is persisted. If left blank, the datastore is automatically considered in-memory only. It cannot end with a <code>~</code> which is used in the temporary files NeDB uses to perform crash-safe writes.</li><li><code>filename</code>（可选项），数据持久化存储的文件路径。如果为空就只是会作为内存数据库使用。不要吧这个文件当做程序崩溃向NeDB写入的临时文件。</li><li><code>inMemoryOnly</code> (optional, defaults to <code>false</code>): as the name implies.</li><li><code>inMemoryOnly</code>（缺省为false）：顾名思义，是否只作为内存数据库使用。</li><li><code>timestampData</code> (optional, defaults to <code>false</code>): timestamp the insertion and last update of all documents, with the fields <code>createdAt</code> and <code>updatedAt</code>. User-specified values override automatic generation, usually useful for testing.</li><li><code>timestampData</code>（缺省false）插入和更新时间戳到所有文档，属性<code>createdAt</code>和<code>updatedAt</code>，如果用指定了值，将会自动被覆盖，这个功能一般用于测试。</li><li><code>autoload</code> (optional, defaults to <code>false</code>): if used, the database will automatically be loaded from the datafile upon creation (you don’t need to call <code>loadDatabase</code>). Any command issued before load is finished is buffered and will be executed when load is done.</li><li><code>autoload</code>（缺省false）：如果使用这个参数，数据库将自动从数据库文件加载数据（不需要调用<code>loadDatebase</code>函数）。将在任何命令发出前进行加载。</li><li><code>onload</code> (optional): if you use autoloading, this is the handler called after the <code>loadDatabase</code>. It takes one <code>error</code> argument. If you use autoloading without specifying this handler, and an error happens during load, an error will be thrown.</li><li><code>onload</code>：如果使用了自动加载，<code>loadDatabase</code>之后会被调用。需要参数<code>error</code>，如果不指定这个回调函数，自动加载过程中发生错误就会抛出异常。</li><li><code>afterSerialization</code> (optional): hook you can use to transform data after it was serialized and before it is written to disk. Can be used for example to encrypt data before writing database to disk. This function takes a string as parameter (one line of an NeDB data file) and outputs the transformed string, <strong>which must absolutely not contain a <code>\n</code> character</strong> (or data will be lost).</li><li><code>afterSerialization</code>：可以使用这个钩子将数据序列化并写入磁盘。可以在写入磁盘之前将数据加密。此函数将一个字符串作为参数（NeDB数据文件的一行），并且需要返回一个字符串，<strong>字符串结尾不要包含<code>\n</code>字符</strong>（否则数据会丢失）。</li><li><code>beforeDeserialization</code> (optional): inverse of <code>afterSerialization</code>. Make sure to include both and not just one or you risk data loss. For the same reason, make sure both functions are inverses of one another. Some failsafe mechanisms are in place to prevent data loss if you misuse the serialization hooks: NeDB checks that never one is declared without the other, and checks that they are reverse of one another by testing on random strings of various lengths. In addition, if too much data is detected as corrupt, NeDB will refuse to start as it could mean you’re not using the deserialization hook corresponding to the serialization hook used before (see below).</li><li><code>beforeDeserialization</code>：<code>afterSerialization</code>的逆向操作，要确保该操作没有任何风险。出于安全考虑，要确保两个函数操作是逆向的。为了防止数据丢失有必要使用一些安全机制，不要滥用这个钩子函数。NeDB从来不会用随机字符串测试你的算法是否是逆向的。另外，NeDB如果检测到坏数据将会拒绝启动，因为有可能您无法逆向以前的加密数据（见下文）。</li><li><code>corruptAlertThreshold</code> (optional): between 0 and 1, defaults to 10%. NeDB will refuse to start if more than this percentage of the datafile is corrupt. 0 means you don’t tolerate any corruption, 1 means you don’t care.</li><li><code>corruptAlertThreshold</code>：介于0和1之间，缺省为10%。如果超过这个比例的数据被损坏，NeDB将会拒绝启动。0意味着你不允许任何坏数据，1意味着你不介意任何坏数据。</li><li><code>compareStrings</code> (optional): function compareStrings(a, b) compares strings a and b and return -1, 0 or 1. If specified, it overrides default string comparison which is not well adapted to non-US characters in particular accented letters. Native <code>localCompare</code> will most of the<br>time be the right choice</li><li><code>compareStrings</code>：函数compareStrings(a, b)将会比较字符串a和b并且返回-1、0、1。如果指定这个参数，他将会覆盖默认的字符串比较。默认的比较不太适合非英文字符。尽量使用适合自己语言的字符串比较函数。</li><li><code>nodeWebkitAppName</code> (optional, <strong>DEPRECATED</strong>): if you are using NeDB from whithin a Node Webkit app, specify its name (the same one you use in the <code>package.json</code>) in this field and the <code>filename</code> will be relative to the directory Node Webkit uses to store the rest of the application’s data (local storage etc.). It works on Linux, OS X and Windows. Now that you can use <code>require(&#39;nw.gui&#39;).App.dataPath</code> in Node Webkit to get the path to the data directory for your application, you should not use this option anymore and it will be removed.</li><li><code>nodeWebkitAppName</code>（该参数已弃用）</li></ul><p>If you use a persistent datastore without the <code>autoload</code> option, you need to call <code>loadDatabase</code> manually.<br>This function fetches the data from datafile and prepares the database. <strong>Don’t forget it!</strong> If you use a<br>persistent datastore, no command (insert, find, update, remove) will be executed before <code>loadDatabase</code><br>is called, so make sure to call it yourself or use the <code>autoload</code> option.</p><p>如果不使用<code>autoload</code>选项，需要手动调用<code>loadDatabase</code>函数来实现持久化存储。这个函数将数据写入数据库文件。<strong>切记切记！</strong>如果不调用这个函数，数据的任何操作（插入、查找、更新、删除）都不会保存，所以一定要确保使用了<code>autoload</code>选项选项或者手动调用了<code>loadDatabase</code>函数。</p><p>Also, if <code>loadDatabase</code> fails, all commands registered to the executor afterwards will not be executed. They will be registered and executed, in sequence, only after a successful <code>loadDatabase</code>.</p><p>另外，如果<code>loadDatabase</code>函数调用失败，那么后面的命令都不会被执行。只有在成功调用<code>loadDatabase</code>函数之后才可以执行。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Type 1: In-memory only datastore (no need to load the database)</span></span><br><span class="line"><span class="comment">// 第一种类型，内存性数据库（不需要加载数据库）</span></span><br><span class="line"><span class="keyword">var</span> Datastore = <span class="built_in">require</span>(<span class="string">'nedb'</span>)</span><br><span class="line">  , db = <span class="keyword">new</span> Datastore();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Type 2: Persistent datastore with manual loading</span></span><br><span class="line"><span class="comment">// 第二种类型，手动加载持久化数据库</span></span><br><span class="line"><span class="keyword">var</span> Datastore = <span class="built_in">require</span>(<span class="string">'nedb'</span>)</span><br><span class="line">  , db = <span class="keyword">new</span> Datastore(&#123; <span class="attr">filename</span>: <span class="string">'path/to/datafile'</span> &#125;);</span><br><span class="line">db.loadDatabase(<span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;    <span class="comment">// Callback is optional 回调函数是可选的</span></span><br><span class="line">  <span class="comment">// Now commands will be executed</span></span><br><span class="line">  <span class="comment">// 数据库写入后执行这里</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Type 3: Persistent datastore with automatic loading</span></span><br><span class="line"><span class="comment">// 第三种类型，自动加载的持久化存储数据库</span></span><br><span class="line"><span class="keyword">var</span> Datastore = <span class="built_in">require</span>(<span class="string">'nedb'</span>)</span><br><span class="line">  , db = <span class="keyword">new</span> Datastore(&#123; <span class="attr">filename</span>: <span class="string">'path/to/datafile'</span>, <span class="attr">autoload</span>: <span class="literal">true</span> &#125;);</span><br><span class="line"><span class="comment">// You can issue commands right away</span></span><br><span class="line"><span class="comment">// 这里可以立即执行数据库操作</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Type 4: Persistent datastore for a Node Webkit app called 'nwtest'</span></span><br><span class="line"><span class="comment">// For example on Linux, the datafile will be ~/.config/nwtest/nedb-data/something.db</span></span><br><span class="line"><span class="comment">// 第四种类型：Node Webkit的起名为‘nwtest’的持久化应用</span></span><br><span class="line"><span class="comment">// 比如在Linux上，数据库文件将会写入 ~/.config/nwtest/nedb-data/something.db</span></span><br><span class="line"><span class="keyword">var</span> Datastore = <span class="built_in">require</span>(<span class="string">'nedb'</span>)</span><br><span class="line">  , path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line">  , db = <span class="keyword">new</span> Datastore(&#123; <span class="attr">filename</span>: path.join(<span class="built_in">require</span>(<span class="string">'nw.gui'</span>).App.dataPath, <span class="string">'something.db'</span>) &#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Of course you can create multiple datastores if you need several</span></span><br><span class="line"><span class="comment">// collections. In this case it's usually a good idea to use autoload for all collections.</span></span><br><span class="line"><span class="comment">// 如果需要，可以创建多个数据存储集合，这种情况下建议使用自动保存</span></span><br><span class="line">db = &#123;&#125;;</span><br><span class="line">db.users = <span class="keyword">new</span> Datastore(<span class="string">'path/to/users.db'</span>);</span><br><span class="line">db.robots = <span class="keyword">new</span> Datastore(<span class="string">'path/to/robots.db'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You need to load each database (here we do it asynchronously)</span></span><br><span class="line"><span class="comment">// 这里需要加载每一个数据库（这里是异步方式）</span></span><br><span class="line">db.users.loadDatabase();</span><br><span class="line">db.robots.loadDatabase();</span><br></pre></td></tr></table></figure><h3 id="Persistence-（续篇）"><a href="#Persistence-（续篇）" class="headerlink" title="Persistence （续篇）"></a>Persistence （续篇）</h3><p>Under the hood, NeDB’s persistence uses an append-only format, meaning that all updates and deletes actually result in lines added at the end of the datafile, for performance reasons. The database is automatically compacted (i.e. put back in the one-line-per-document format) every time you load each database within your application.</p><p>该存储引擎处于性能考虑，NeDB的持久化存储仅使用追加方式，这样会导致所有的更新和删除实际上只是在末尾添加行。每次应用程序加载数据库时，数据库将自动压缩（单行文档格式）。</p><p>You can manually call the compaction function with <code>yourDatabase.persistence.compactDatafile</code> which takes no argument. It queues a compaction of the datafile in the executor, to be executed sequentially after all pending operations. The datastore will fire a <code>compaction.done</code> event once compaction is finished.</p><p>可以不带参数的调用<code>yourDatabase.persistence.compactDatafile</code>压缩数据库。他将在执行器对数据文件的压缩进行排队，等所有操作处理完再执行。如果压缩完，将会出发数据库的<code>compaction.done</code>事件。</p><p>You can also set automatic compaction at regular intervals with <code>yourDatabase.persistence.setAutocompactionInterval(interval)</code>, <code>interval</code> in milliseconds (a minimum of 5s is enforced), and stop automatic compaction with <code>yourDatabase.persistence.stopAutocompaction()</code>.</p><p>可以设置<code>yourDatabase.persistence.setAutocompactionInterval(interval)</code>定时自动压缩，<code>interval</code>以毫秒为单位（最短5秒调用一次），并且要停止使用<code>yourDatabase.persistence.stopAutocompaction()</code>的自动压缩。</p><p>Keep in mind that compaction takes a bit of time (not too much: 130ms for 50k records on a typical development machine) and no other operation can happen when it does, so most projects actually don’t need to use it.</p><p>切忌压缩是需要时间（一般的开发机无其他干扰，50k数据需要130毫秒）的，这时间内不可以执行其他操作，大部分项目不需要用着个功能。</p><p>Compaction will also immediately remove any documents whose data line has become corrupted, assuming that the total percentage of all corrupted documents in that database still falls below the specified <code>corruptAlertThreshold</code> option’s value.</p><p>压缩还将删除任何坏文档，假如数据库的坏文档比例低于<code>corruptAlertThreshold</code>选项的值。</p><p>Durability works similarly to major databases: compaction forces the OS to physically flush data to disk, while appends to the data file do not (the OS is responsible for flushing the data). That guarantees that a server crash can never cause complete data loss, while preserving performance. The worst that can happen is a crash between two syncs, causing a loss of all data between the two syncs. Usually syncs are 30 seconds appart so that’s at most 30 seconds of data. <a href="http://oldblog.antirez.com/post/redis-persistence-demystified.html" target="_blank">This post by Antirez on Redis persistence</a> explains this in more details, NeDB being very close to Redis AOF persistence with <code>appendfsync</code> option set to <code>no</code>.</p><p>持久性和其他主流数据库类似，压缩操作强制操作系统将数据保存到物理磁盘，而追加数据不会如此（操作系统负责刷新数据）。这样既能保证数据的完整性，又能保持较高性能。可能发生的最坏事情是两次同步之间发生崩溃，造成两次同步之间的数据操作丢失。通常30秒同步一次数据，最差丢30秒的数据。Antirez在<a href="http://oldblog.antirez.com/post/redis-persistence-demystified.html" target="_blank">Redis数据持久化存储</a>文章里详细的解释了这一点。NeDB非常接近Redis的AOF持久化选项<code>appendfsync</code>设置为<code>no</code>。</p><h3 id="Inserting-documents（添加文档）"><a href="#Inserting-documents（添加文档）" class="headerlink" title="Inserting documents（添加文档）"></a>Inserting documents（添加文档）</h3><p>The native types are <code>String</code>, <code>Number</code>, <code>Boolean</code>, <code>Date</code> and <code>null</code>. You can also use<br>arrays and subdocuments (objects). If a field is <code>undefined</code>, it will not be saved (this is different from<br>MongoDB which transforms <code>undefined</code> in <code>null</code>, something I find counter-intuitive).</p><p>支持的类型是<code>String</code>、<code>Number</code>、<code>Boolean</code>、<code>Date</code>和<code>null</code>。还可以试用数组和子文档，如果字段未定义不会保存（这一点与MongoDB是不同的）。</p><p>If the document does not contain an <code>_id</code> field, NeDB will automatically generated one for you (a 16-characters alphanumerical string). The <code>_id</code> of a document, once set, cannot be modified.</p><p>如果文档没有设置<code>_id</code>，NeDB将会自动生成（长16的字符串）。文档<code>_id</code>一旦设置无法修改。</p><p>Field names cannot begin by ‘$’ or contain a ‘.’.</p><p>字段名不可以’$’开头，也不可以包含’.’。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> doc = &#123; <span class="attr">hello</span>: <span class="string">'world'</span></span><br><span class="line">               , <span class="attr">n</span>: <span class="number">5</span></span><br><span class="line">               , <span class="attr">today</span>: <span class="keyword">new</span> <span class="built_in">Date</span>()</span><br><span class="line">               , <span class="attr">nedbIsAwesome</span>: <span class="literal">true</span></span><br><span class="line">               , <span class="attr">notthere</span>: <span class="literal">null</span></span><br><span class="line">               , <span class="attr">notToBeSaved</span>: <span class="literal">undefined</span>  <span class="comment">// Will not be saved</span></span><br><span class="line">               , <span class="attr">fruits</span>: [ <span class="string">'apple'</span>, <span class="string">'orange'</span>, <span class="string">'pear'</span> ]</span><br><span class="line">               , <span class="attr">infos</span>: &#123; <span class="attr">name</span>: <span class="string">'nedb'</span> &#125;</span><br><span class="line">               &#125;;</span><br><span class="line"></span><br><span class="line">db.insert(doc, <span class="function"><span class="keyword">function</span> (<span class="params">err, newDoc</span>) </span>&#123;   <span class="comment">// Callback is optional</span></span><br><span class="line">  <span class="comment">// newDoc is the newly inserted document, including its _id</span></span><br><span class="line">  <span class="comment">// newDoc has no key called notToBeSaved since its value was undefined</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>You can also bulk-insert an array of documents. This operation is atomic, meaning that if one insert fails due to a unique constraint being violated, all changes are rolled back.</p><p>可以将数组批量插入数据库，这样操作是原子操作，如果有一个不合规范，其他操作都将回滚。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">db.insert([&#123; <span class="attr">a</span>: <span class="number">5</span> &#125;, &#123; <span class="attr">a</span>: <span class="number">42</span> &#125;], <span class="function"><span class="keyword">function</span> (<span class="params">err, newDocs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Two documents were inserted in the database</span></span><br><span class="line">  <span class="comment">// newDocs is an array with these documents, augmented with their _id</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// If there is a unique constraint on field 'a', this will fail</span></span><br><span class="line">db.insert([&#123; <span class="attr">a</span>: <span class="number">5</span> &#125;, &#123; <span class="attr">a</span>: <span class="number">42</span> &#125;, &#123; <span class="attr">a</span>: <span class="number">5</span> &#125;], <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// err is a 'uniqueViolated' error</span></span><br><span class="line">  <span class="comment">// The database was not modified</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Finding-documents-（查找文档）"><a href="#Finding-documents-（查找文档）" class="headerlink" title="Finding documents （查找文档）"></a>Finding documents （查找文档）</h3><p>Use <code>find</code> to look for multiple documents matching you query, or <code>findOne</code> to look for one specific document. You can select documents based on field equality or use comparison operators (<code>$lt</code>, <code>$lte</code>, <code>$gt</code>, <code>$gte</code>, <code>$in</code>, <code>$nin</code>, <code>$ne</code>). You can also use logical operators <code>$or</code>, <code>$and</code>, <code>$not</code> and <code>$where</code>. See below for the syntax.</p><p>使用<code>find</code>查找和匹配多个文档，或者用<code>findOne</code>读取一个文档。可以用比较符号筛选文档(<code>$lt</code>、<code>$lte</code>、<code>$gt</code>、<code>$gte</code>、<code>$in</code>、<code>$nin</code>、<code>$ne</code>)。还可以用逻辑运算符<code>$or</code>、<code>$and</code>、<code>$not</code>和<code>$where</code>，请看下面的语法。</p><p>You can use regular expressions in two ways: in basic querying in place of a string, or with the <code>$regex</code> operator.</p><p>两种方式可以使用正则，基本查询中或<code>$regex</code>操作符中。</p><p>You can sort and paginate results using the cursor API (see below).</p><p>可以使用游标API对结果进行排序和分页，详见下文。</p><p>You can use standard projections to restrict the fields to appear in the results (see below).</p><p>可以使用投影的方式限制字段出现在结果中，详见下文。</p><h4 id="Basic-querying-（基本查询）"><a href="#Basic-querying-（基本查询）" class="headerlink" title="Basic querying （基本查询）"></a>Basic querying （基本查询）</h4><p>Basic querying means are looking for documents whose fields match the ones you specify. You can use regular expression to match strings.<br>You can use the dot notation to navigate inside nested documents, arrays, arrays of subdocuments and to match a specific element of an array.</p><p>基本查询是查找和条件匹配的文档。可以用正则来匹配字符串。可以用<code>.</code>号来引用嵌套文档、数组、子文档数组，并匹配指定的元素。</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Let's say our datastore contains the following collection</span></span><br><span class="line"><span class="comment">// 假设我们的数据库包含下面的数据集合</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false, satellites: ['Phobos', 'Deimos'] &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: &#123; genders: 2, eyes: true &#125; &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id4', planet: 'Omicron Persei 8', system: 'futurama', inhabited: true, humans: &#123; genders: 7 &#125; &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id5', completeData: &#123; planets: [ &#123; name: 'Earth', number: 3 &#125;, &#123; name: 'Mars', number: 2 &#125;, &#123; name: 'Pluton', number: 9 &#125; ] &#125; &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Finding all planets in the solar system</span></span><br><span class="line"><span class="comment">// 查找太阳系中的所有行星</span></span><br><span class="line">db.find(&#123; <span class="attr">system</span>: <span class="string">'solar'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is an array containing documents Mars, Earth, Jupiter</span></span><br><span class="line">  <span class="comment">// If no document is found, docs is equal to []</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Finding all planets whose name contain the substring 'ar' using a regular expression</span></span><br><span class="line"><span class="comment">// 使用正则查找名称中包含“ar”的所有行星</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="regexp">/ar/</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars and Earth</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Finding all inhabited planets in the solar system</span></span><br><span class="line"><span class="comment">// 查找太阳系中所有有人的行星</span></span><br><span class="line">db.find(&#123; <span class="attr">system</span>: <span class="string">'solar'</span>, <span class="attr">inhabited</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is an array containing document Earth only</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use the dot-notation to match fields in subdocuments</span></span><br><span class="line"><span class="comment">// 使用`.`符号匹配子文档中的字段</span></span><br><span class="line">db.find(&#123; <span class="string">"humans.genders"</span>: <span class="number">2</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Earth</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Use the dot-notation to navigate arrays of subdocuments</span></span><br><span class="line"><span class="comment">// 使用`.`符号浏览子文档中的数组</span></span><br><span class="line">db.find(&#123; <span class="string">"completeData.planets.name"</span>: <span class="string">"Mars"</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains document 5</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="string">"completeData.planets.name"</span>: <span class="string">"Jupiter"</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="string">"completeData.planets.0.name"</span>: <span class="string">"Earth"</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains document 5</span></span><br><span class="line">  <span class="comment">// If we had tested against "Mars" docs would be empty because we are matching against a specific array element</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// You can also deep-compare objects. Don't confuse this with dot-notation!</span></span><br><span class="line"><span class="comment">// 也可以深入的比较对象，不要和`.`符号混淆</span></span><br><span class="line">db.find(&#123; <span class="attr">humans</span>: &#123; <span class="attr">genders</span>: <span class="number">2</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty, because &#123; genders: 2 &#125; is not equal to &#123; genders: 2, eyes: true &#125;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Find all documents in the collection</span></span><br><span class="line">db.find(&#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// The same rules apply when you want to only find one document</span></span><br><span class="line">db.findOne(&#123; <span class="attr">_id</span>: <span class="string">'id1'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, doc</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// doc is the document Mars</span></span><br><span class="line">  <span class="comment">// If no document is found, doc is null</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="Operators-lt-lte-gt-gte-in-nin-ne-exists-regex-（操作符）"><a href="#Operators-lt-lte-gt-gte-in-nin-ne-exists-regex-（操作符）" class="headerlink" title="Operators ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex)（操作符）"></a>Operators ($lt, $lte, $gt, $gte, $in, $nin, $ne, $exists, $regex)（操作符）</h4><p>The syntax is <code>{ field: { $op: value } }</code> where <code>$op</code> is any comparison operator:  </p><ul><li><code>$lt</code>, <code>$lte</code>: less than, less than or equal</li><li><code>$lt</code>, <code>$lte</code>: 小于、小于等于</li><li><code>$gt</code>, <code>$gte</code>: greater than, greater than or equal</li><li><code>$gt</code>, <code>$gte</code>: 大于、大于等于</li><li><code>$in</code>: member of. <code>value</code> must be an array of values</li><li><code>$in</code>: 成员。<code>value</code>必须是数组</li><li><code>$ne</code>, <code>$nin</code>: not equal, not a member of</li><li><code>$ne</code>, <code>$nin</code>: 不等于、不是成员</li><li><code>$exists</code>: checks whether the document posses the property <code>field</code>. <code>value</code> should be true or false</li><li><code>$exists</code>: 检查文档是否有指定的字段属性<code>field</code>.<code>value</code>，返回true或false</li><li><code>$regex</code>: checks whether a string is matched by the regular expression. Contrary to MongoDB, the use of <code>$options</code> with <code>$regex</code> is not supported, because it doesn’t give you more power than regex flags. Basic queries are more readable so only use the <code>$regex</code> operator when you need to use another operator with it (see example below)</li><li><code>$regex</code>: 检查字符串是否与正则表达式匹配，与MongoDB相反，不支持<code>$options</code>，因为它不会比正则表达式更强。基本查询可读性更好，最好在确实需要正则是时候才使用（详情如下）</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// $lt, $lte, $gt and $gte work on numbers and strings</span></span><br><span class="line">db.find(&#123; <span class="string">"humans.genders"</span>: &#123; <span class="attr">$gt</span>: <span class="number">5</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Omicron Persei 8, whose humans have more than 5 genders (7).</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// When used with strings, lexicographical order is used</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: &#123; <span class="attr">$gt</span>: <span class="string">'Mercury'</span> &#125;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Omicron Persei 8</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using $in. $nin is used in the same way</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: &#123; <span class="attr">$in</span>: [<span class="string">'Earth'</span>, <span class="string">'Jupiter'</span>] &#125;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Earth and Jupiter</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using $exists</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: &#123; <span class="attr">$exists</span>: <span class="literal">true</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains only Mars</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using $regex with another operator</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: &#123; <span class="attr">$regex</span>: <span class="regexp">/ar/</span>, <span class="attr">$nin</span>: [<span class="string">'Jupiter'</span>, <span class="string">'Earth'</span>] &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs only contains Mars because Earth was excluded from the match by $nin</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="Array-fields"><a href="#Array-fields" class="headerlink" title="Array fields"></a>Array fields</h4><p>When a field in a document is an array, NeDB first tries to see if the query value is an array to perform an exact match, then whether there is an array-specific comparison function (for now there is only <code>$size</code> and <code>$elemMatch</code>) being used. If not, the query is treated as a query on every element and there is a match if at least one element matches.  </p><p>当一个文档中的字段是一个数组时，NeDB首先尝试查看值是否是一个数组来执行完全匹配，然后查看是否使用数组特定的比较函数（目前只支持<code>$size</code>和<code>$elemMatch</code>）。如果没有使用，会查询每一个数组元素，只要有一个数组向匹配，该文档就满足条件。</p><ul><li><code>$size</code>: match on the size of the array</li><li><code>$elemMatch</code>: matches if at least one array element matches the query entirely</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Exact match</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: [<span class="string">'Phobos'</span>, <span class="string">'Deimos'</span>] &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars</span></span><br><span class="line">&#125;)</span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: [<span class="string">'Deimos'</span>, <span class="string">'Phobos'</span>] &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty</span></span><br><span class="line">&#125;)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using an array-specific comparison function</span></span><br><span class="line"><span class="comment">// $elemMatch operator will provide match for a document, if an element from the array field satisfies all the conditions specified with the `$elemMatch` operator</span></span><br><span class="line">db.find(&#123; <span class="attr">completeData</span>: &#123; <span class="attr">planets</span>: &#123; <span class="attr">$elemMatch</span>: &#123; <span class="attr">name</span>: <span class="string">'Earth'</span>, <span class="attr">number</span>: <span class="number">3</span> &#125; &#125; &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains documents with id 5 (completeData)</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="attr">completeData</span>: &#123; <span class="attr">planets</span>: &#123; <span class="attr">$elemMatch</span>: &#123; <span class="attr">name</span>: <span class="string">'Earth'</span>, <span class="attr">number</span>: <span class="number">5</span> &#125; &#125; &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can use inside #elemMatch query any known document query operator</span></span><br><span class="line">db.find(&#123; <span class="attr">completeData</span>: &#123; <span class="attr">planets</span>: &#123; <span class="attr">$elemMatch</span>: &#123; <span class="attr">name</span>: <span class="string">'Earth'</span>, <span class="attr">number</span>: &#123; <span class="attr">$gt</span>: <span class="number">2</span> &#125; &#125; &#125; &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains documents with id 5 (completeData)</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Note: you can't use nested comparison functions, e.g. &#123; $size: &#123; $lt: 5 &#125; &#125; will throw an error</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: &#123; <span class="attr">$size</span>: <span class="number">2</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: &#123; <span class="attr">$size</span>: <span class="number">1</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// If a document's field is an array, matching it means matching any element of the array</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: <span class="string">'Phobos'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars. Result would have been the same if query had been &#123; satellites: 'Deimos' &#125;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// This also works for queries that use comparison operators</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: &#123; <span class="attr">$lt</span>: <span class="string">'Amos'</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is empty since Phobos and Deimos are after Amos in lexicographical order</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// This also works with the $in and $nin operator</span></span><br><span class="line">db.find(&#123; <span class="attr">satellites</span>: &#123; <span class="attr">$in</span>: [<span class="string">'Moon'</span>, <span class="string">'Deimos'</span>] &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars (the Earth document is not complete!)</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="Logical-operators-or-and-not-where"><a href="#Logical-operators-or-and-not-where" class="headerlink" title="Logical operators $or, $and, $not, $where"></a>Logical operators $or, $and, $not, $where</h4><p>You can combine queries using logical operators:  </p><ul><li>For <code>$or</code> and <code>$and</code>, the syntax is <code>{ $op: [query1, query2, ...] }</code>.</li><li>For <code>$not</code>, the syntax is <code>{ $not: query }</code></li><li>For <code>$where</code>, the syntax is <code>{ $where: function () { /* object is &quot;this&quot;, return a boolean */ } }</code></li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">db.find(&#123; <span class="attr">$or</span>: [&#123; <span class="attr">planet</span>: <span class="string">'Earth'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;] &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Earth and Mars</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="attr">$not</span>: &#123; <span class="attr">planet</span>: <span class="string">'Earth'</span> &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Mars, Jupiter, Omicron Persei 8</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.find(&#123; <span class="attr">$where</span>: <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; <span class="keyword">return</span> <span class="built_in">Object</span>.keys(<span class="keyword">this</span>) &gt; <span class="number">6</span>; &#125; &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs with more than 6 properties</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can mix normal queries, comparison queries and logical operators</span></span><br><span class="line">db.find(&#123; <span class="attr">$or</span>: [&#123; <span class="attr">planet</span>: <span class="string">'Earth'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;], <span class="attr">inhabited</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs contains Earth</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h4 id="Sorting-and-paginating"><a href="#Sorting-and-paginating" class="headerlink" title="Sorting and paginating"></a>Sorting and paginating</h4><p>If you don’t specify a callback to <code>find</code>, <code>findOne</code> or <code>count</code>, a <code>Cursor</code> object is returned. You can modify the cursor with <code>sort</code>, <code>skip</code> and <code>limit</code> and then execute it with <code>exec(callback)</code>.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Let's say the database contains these 4 documents</span></span><br><span class="line"><span class="comment">// doc1 = &#123; _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false, satellites: ['Phobos', 'Deimos'] &#125;</span></span><br><span class="line"><span class="comment">// doc2 = &#123; _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true, humans: &#123; genders: 2, eyes: true &#125; &#125;</span></span><br><span class="line"><span class="comment">// doc3 = &#123; _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// doc4 = &#123; _id: 'id4', planet: 'Omicron Persei 8', system: 'futurama', inhabited: true, humans: &#123; genders: 7 &#125; &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// No query used means all results are returned (before the Cursor modifiers)</span></span><br><span class="line">db.find(&#123;&#125;).sort(&#123; <span class="attr">planet</span>: <span class="number">1</span> &#125;).skip(<span class="number">1</span>).limit(<span class="number">2</span>).exec(<span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [doc3, doc1]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can sort in reverse order like this</span></span><br><span class="line">db.find(&#123; <span class="attr">system</span>: <span class="string">'solar'</span> &#125;).sort(&#123; <span class="attr">planet</span>: <span class="number">-1</span> &#125;).exec(<span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [doc1, doc3, doc2]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can sort on one field, then another, and so on like this:</span></span><br><span class="line">db.find(&#123;&#125;).sort(&#123; <span class="attr">firstField</span>: <span class="number">1</span>, <span class="attr">secondField</span>: <span class="number">-1</span> &#125;) ...   <span class="comment">// You understand how this works!</span></span><br></pre></td></tr></table></figure><h4 id="Projections"><a href="#Projections" class="headerlink" title="Projections"></a>Projections</h4><p>You can give <code>find</code> and <code>findOne</code> an optional second argument, <code>projections</code>. The syntax is the same as MongoDB: <code>{ a: 1, b: 1 }</code> to return only the <code>a</code> and <code>b</code> fields, <code>{ a: 0, b: 0 }</code> to omit these two fields. You cannot use both modes at the time, except for <code>_id</code> which is by default always returned and which you can choose to omit. You can project on nested documents.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Same database as above</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Keeping only the given fields</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="number">1</span>, <span class="attr">system</span>: <span class="number">1</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [&#123; planet: 'Mars', system: 'solar', _id: 'id1' &#125;]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Keeping only the given fields but removing _id</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="number">1</span>, <span class="attr">system</span>: <span class="number">1</span>, <span class="attr">_id</span>: <span class="number">0</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [&#123; planet: 'Mars', system: 'solar' &#125;]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Omitting only the given fields and removing _id</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="number">0</span>, <span class="attr">system</span>: <span class="number">0</span>, <span class="attr">_id</span>: <span class="number">0</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [&#123; inhabited: false, satellites: ['Phobos', 'Deimos'] &#125;]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Failure: using both modes at the same time</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="number">0</span>, <span class="attr">system</span>: <span class="number">1</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// err is the error message, docs is undefined</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can also use it in a Cursor way but this syntax is not compatible with MongoDB</span></span><br><span class="line">db.find(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;).projection(&#123; <span class="attr">planet</span>: <span class="number">1</span>, <span class="attr">system</span>: <span class="number">1</span> &#125;).exec(<span class="function"><span class="keyword">function</span> (<span class="params">err, docs</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// docs is [&#123; planet: 'Mars', system: 'solar', _id: 'id1' &#125;]</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Project on a nested document</span></span><br><span class="line">db.findOne(&#123; <span class="attr">planet</span>: <span class="string">'Earth'</span> &#125;).projection(&#123; <span class="attr">planet</span>: <span class="number">1</span>, <span class="string">'humans.genders'</span>: <span class="number">1</span> &#125;).exec(<span class="function"><span class="keyword">function</span> (<span class="params">err, doc</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// doc is &#123; planet: 'Earth', _id: 'id2', humans: &#123; genders: 2 &#125; &#125;</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Counting-documents"><a href="#Counting-documents" class="headerlink" title="Counting documents"></a>Counting documents</h3><p>You can use <code>count</code> to count documents. It has the same syntax as <code>find</code>. For example:</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Count all planets in the solar system</span></span><br><span class="line">db.count(&#123; <span class="attr">system</span>: <span class="string">'solar'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, count</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// count equals to 3</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Count all documents in the datastore</span></span><br><span class="line">db.count(&#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, count</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// count equals to 4</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Updating-documents"><a href="#Updating-documents" class="headerlink" title="Updating documents"></a>Updating documents</h3><p><code>db.update(query, update, options, callback)</code> will update all documents matching <code>query</code> according to the <code>update</code> rules:  </p><ul><li><code>query</code> is the same kind of finding query you use with <code>find</code> and <code>findOne</code></li><li><code>update</code> specifies how the documents should be modified. It is either a new document or a set of modifiers (you cannot use both together, it doesn’t make sense!)<ul><li>A new document will replace the matched docs</li><li>The modifiers create the fields they need to modify if they don’t exist, and you can apply them to subdocs. Available field modifiers are <code>$set</code> to change a field’s value, <code>$unset</code> to delete a field, <code>$inc</code> to increment a field’s value and <code>$min</code>/<code>$max</code> to change field’s value, only if provided value is less/greater than current value. To work on arrays, you have <code>$push</code>, <code>$pop</code>, <code>$addToSet</code>, <code>$pull</code>, and the special <code>$each</code> and <code>$slice</code>. See examples below for the syntax.</li></ul></li><li><code>options</code> is an object with two possible parameters<ul><li><code>multi</code> (defaults to <code>false</code>) which allows the modification of several documents if set to true</li><li><code>upsert</code> (defaults to <code>false</code>) if you want to insert a new document corresponding to the <code>update</code> rules if your <code>query</code> doesn’t match anything. If your <code>update</code> is a simple object with no modifiers, it is the inserted document. In the other case, the <code>query</code> is stripped from all operator recursively, and the <code>update</code> is applied to it.</li><li><code>returnUpdatedDocs</code> (defaults to <code>false</code>, not MongoDB-compatible) if set to true and update is not an upsert, will return the array of documents matched by the find query and updated. Updated documents will be returned even if the update did not actually modify them.</li></ul></li><li><code>callback</code> (optional) signature: <code>(err, numAffected, affectedDocuments, upsert)</code>. <strong>Warning</strong>: the API was changed between v1.7.4 and v1.8. Please refer to the <a href="https://github.com/louischatriot/nedb/wiki/Change-log" target="_blank">change log</a> to see the change.<ul><li>For an upsert, <code>affectedDocuments</code> contains the inserted document and the <code>upsert</code> flag is set to <code>true</code>.</li><li>For a standard update with <code>returnUpdatedDocs</code> flag set to <code>false</code>, <code>affectedDocuments</code> is not set.</li><li>For a standard update with <code>returnUpdatedDocs</code> flag set to <code>true</code> and <code>multi</code> to <code>false</code>, <code>affectedDocuments</code> is the updated document.</li><li>For a standard update with <code>returnUpdatedDocs</code> flag set to <code>true</code> and <code>multi</code> to <code>true</code>, <code>affectedDocuments</code> is the array of updated documents.</li></ul></li></ul><p><strong>Note</strong>: you can’t change a document’s _id.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Let's use the same example collection as in the "finding document" part</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id4', planet: 'Omicron Persia 8', system: 'futurama', inhabited: true &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Replace a document by another</span></span><br><span class="line">db.update(&#123; <span class="attr">planet</span>: <span class="string">'Jupiter'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="string">'Pluton'</span>&#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numReplaced</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// numReplaced = 1</span></span><br><span class="line">  <span class="comment">// The doc #3 has been replaced by &#123; _id: 'id3', planet: 'Pluton' &#125;</span></span><br><span class="line">  <span class="comment">// Note that the _id is kept unchanged, and the document has been replaced</span></span><br><span class="line">  <span class="comment">// (the 'system' and inhabited fields are not here anymore)</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Set an existing field's value</span></span><br><span class="line">db.update(&#123; <span class="attr">system</span>: <span class="string">'solar'</span> &#125;, &#123; <span class="attr">$set</span>: &#123; <span class="attr">system</span>: <span class="string">'solar system'</span> &#125; &#125;, &#123; <span class="attr">multi</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numReplaced</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// numReplaced = 3</span></span><br><span class="line">  <span class="comment">// Field 'system' on Mars, Earth, Jupiter now has value 'solar system'</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Setting the value of a non-existing field in a subdocument by using the dot-notation</span></span><br><span class="line">db.update(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">$set</span>: &#123; <span class="string">"data.satellites"</span>: <span class="number">2</span>, <span class="string">"data.red"</span>: <span class="literal">true</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Mars document now is &#123; _id: 'id1', system: 'solar', inhabited: false</span></span><br><span class="line">  <span class="comment">//                      , data: &#123; satellites: 2, red: true &#125;</span></span><br><span class="line">  <span class="comment">//                      &#125;</span></span><br><span class="line">  <span class="comment">// Not that to set fields in subdocuments, you HAVE to use dot-notation</span></span><br><span class="line">  <span class="comment">// Using object-notation will just replace the top-level field</span></span><br><span class="line">  db.update(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">$set</span>: &#123; <span class="attr">data</span>: &#123; <span class="attr">satellites</span>: <span class="number">3</span> &#125; &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="comment">// Mars document now is &#123; _id: 'id1', system: 'solar', inhabited: false</span></span><br><span class="line">    <span class="comment">//                      , data: &#123; satellites: 3 &#125;</span></span><br><span class="line">    <span class="comment">//                      &#125;</span></span><br><span class="line">    <span class="comment">// You lost the "data.red" field which is probably not the intended behavior</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Deleting a field</span></span><br><span class="line">db.update(&#123; <span class="attr">planet</span>: <span class="string">'Mars'</span> &#125;, &#123; <span class="attr">$unset</span>: &#123; <span class="attr">planet</span>: <span class="literal">true</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the document for Mars doesn't contain the planet field</span></span><br><span class="line">  <span class="comment">// You can unset nested fields with the dot notation of course</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Upserting a document</span></span><br><span class="line">db.update(&#123; <span class="attr">planet</span>: <span class="string">'Pluton'</span> &#125;, &#123; <span class="attr">planet</span>: <span class="string">'Pluton'</span>, <span class="attr">inhabited</span>: <span class="literal">false</span> &#125;, &#123; <span class="attr">upsert</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numReplaced, upsert</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// numReplaced = 1, upsert = &#123; _id: 'id5', planet: 'Pluton', inhabited: false &#125;</span></span><br><span class="line">  <span class="comment">// A new document &#123; _id: 'id5', planet: 'Pluton', inhabited: false &#125; has been added to the collection</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// If you upsert with a modifier, the upserted doc is the query modified by the modifier</span></span><br><span class="line"><span class="comment">// This is simpler than it sounds :)</span></span><br><span class="line">db.update(&#123; <span class="attr">planet</span>: <span class="string">'Pluton'</span> &#125;, &#123; <span class="attr">$inc</span>: &#123; <span class="attr">distance</span>: <span class="number">38</span> &#125; &#125;, &#123; <span class="attr">upsert</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// A new document &#123; _id: 'id5', planet: 'Pluton', distance: 38 &#125; has been added to the collection  </span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// If we insert a new document &#123; _id: 'id6', fruits: ['apple', 'orange', 'pear'] &#125; in the collection,</span></span><br><span class="line"><span class="comment">// let's see how we can modify the array field atomically</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// $push inserts new elements at the end of the array</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$push</span>: &#123; <span class="attr">fruits</span>: <span class="string">'banana'</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['apple', 'orange', 'pear', 'banana']</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $pop removes an element from the end (if used with 1) or the front (if used with -1) of the array</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$pop</span>: &#123; <span class="attr">fruits</span>: <span class="number">1</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['apple', 'orange']</span></span><br><span class="line">  <span class="comment">// With &#123; $pop: &#123; fruits: -1 &#125; &#125;, it would have been ['orange', 'pear']</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $addToSet adds an element to an array only if it isn't already in it</span></span><br><span class="line"><span class="comment">// Equality is deep-checked (i.e. $addToSet will not insert an object in an array already containing the same object)</span></span><br><span class="line"><span class="comment">// Note that it doesn't check whether the array contained duplicates before or not</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$addToSet</span>: &#123; <span class="attr">fruits</span>: <span class="string">'apple'</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// The fruits array didn't change</span></span><br><span class="line">  <span class="comment">// If we had used a fruit not in the array, e.g. 'banana', it would have been added to the array</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $pull removes all values matching a value or even any NeDB query from the array</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$pull</span>: &#123; <span class="attr">fruits</span>: <span class="string">'apple'</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['orange', 'pear']</span></span><br><span class="line">&#125;);</span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$pull</span>: &#123; <span class="attr">fruits</span>: $<span class="keyword">in</span>: [<span class="string">'apple'</span>, <span class="string">'pear'</span>] &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['orange']</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $each can be used to $push or $addToSet multiple values at once</span></span><br><span class="line"><span class="comment">// This example works the same way with $addToSet</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$push</span>: &#123; <span class="attr">fruits</span>: &#123; <span class="attr">$each</span>: [<span class="string">'banana'</span>, <span class="string">'orange'</span>] &#125; &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['apple', 'orange', 'pear', 'banana', 'orange']</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $slice can be used in cunjunction with $push and $each to limit the size of the resulting array.</span></span><br><span class="line"><span class="comment">// A value of 0 will update the array to an empty array. A positive value n will keep only the n first elements</span></span><br><span class="line"><span class="comment">// A negative value -n will keep only the last n elements.</span></span><br><span class="line"><span class="comment">// If $slice is specified but not $each, $each is set to []</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id6'</span> &#125;, &#123; <span class="attr">$push</span>: &#123; <span class="attr">fruits</span>: &#123; <span class="attr">$each</span>: [<span class="string">'banana'</span>], <span class="attr">$slice</span>: <span class="number">2</span> &#125; &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now the fruits array is ['apple', 'orange']</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// $min/$max to update only if provided value is less/greater than current value</span></span><br><span class="line"><span class="comment">// Let's say the database contains this document</span></span><br><span class="line"><span class="comment">// doc = &#123; _id: 'id', name: 'Name', value: 5 &#125;</span></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id1'</span> &#125;, &#123; <span class="attr">$min</span>: &#123; <span class="attr">value</span>: <span class="number">2</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// The document will be updated to &#123; _id: 'id', name: 'Name', value: 2 &#125;</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line">db.update(&#123; <span class="attr">_id</span>: <span class="string">'id1'</span> &#125;, &#123; <span class="attr">$min</span>: &#123; <span class="attr">value</span>: <span class="number">8</span> &#125; &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="comment">// The document will not be modified</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Removing-documents"><a href="#Removing-documents" class="headerlink" title="Removing documents"></a>Removing documents</h3><p><code>db.remove(query, options, callback)</code> will remove all documents matching <code>query</code> according to <code>options</code>  </p><ul><li><code>query</code> is the same as the ones used for finding and updating</li><li><code>options</code> only one option for now: <code>multi</code> which allows the removal of multiple documents if set to true. Default is false</li><li><code>callback</code> is optional, signature: err, numRemoved</li></ul><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Let's use the same example collection as in the "finding document" part</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id1', planet: 'Mars', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id2', planet: 'Earth', system: 'solar', inhabited: true &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id3', planet: 'Jupiter', system: 'solar', inhabited: false &#125;</span></span><br><span class="line"><span class="comment">// &#123; _id: 'id4', planet: 'Omicron Persia 8', system: 'futurama', inhabited: true &#125;</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// Remove one document from the collection</span></span><br><span class="line"><span class="comment">// options set to &#123;&#125; since the default for multi is false</span></span><br><span class="line">db.remove(&#123; <span class="attr">_id</span>: <span class="string">'id2'</span> &#125;, &#123;&#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numRemoved</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// numRemoved = 1</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Remove multiple documents</span></span><br><span class="line">db.remove(&#123; <span class="attr">system</span>: <span class="string">'solar'</span> &#125;, &#123; <span class="attr">multi</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numRemoved</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// numRemoved = 3</span></span><br><span class="line">  <span class="comment">// All planets from the solar system were removed</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Removing all documents with the 'match-all' query</span></span><br><span class="line">db.remove(&#123;&#125;, &#123; <span class="attr">multi</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err, numRemoved</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><h3 id="Indexing"><a href="#Indexing" class="headerlink" title="Indexing"></a>Indexing</h3><p>NeDB supports indexing. It gives a very nice speed boost and can be used to enforce a unique constraint on a field. You can index any field, including fields in nested documents using the dot notation. For now, indexes are only used to speed up basic queries and queries using <code>$in</code>, <code>$lt</code>, <code>$lte</code>, <code>$gt</code> and <code>$gte</code>. The indexed values cannot be of type array of object.</p><p>To create an index, use <code>datastore.ensureIndex(options, cb)</code>, where callback is optional and get passed an error if any (usually a unique constraint that was violated). <code>ensureIndex</code> can be called when you want, even after some data was inserted, though it’s best to call it at application startup. The options are:  </p><ul><li><strong>fieldName</strong> (required): name of the field to index. Use the dot notation to index a field in a nested document.</li><li><strong>unique</strong> (optional, defaults to <code>false</code>): enforce field uniqueness. Note that a unique index will raise an error if you try to index two documents for which the field is not defined.</li><li><strong>sparse</strong> (optional, defaults to <code>false</code>): don’t index documents for which the field is not defined. Use this option along with “unique” if you want to accept multiple documents for which it is not defined.</li><li><strong>expireAfterSeconds</strong> (number of seconds, optional): if set, the created index is a TTL (time to live) index, that will automatically remove documents when the system date becomes larger than the date on the indexed field plus <code>expireAfterSeconds</code>. Documents where the indexed field is not specified or not a <code>Date</code> object are ignored</li></ul><p>Note: the <code>_id</code> is automatically indexed with a unique constraint, no need to call <code>ensureIndex</code> on it.</p><p>You can remove a previously created index with <code>datastore.removeIndex(fieldName, cb)</code>.</p><p>If your datastore is persistent, the indexes you created are persisted in the datafile, when you load the database a second time they are automatically created for you. No need to remove any <code>ensureIndex</code> though, if it is called on a database that already has the index, nothing happens.</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line">db.ensureIndex(&#123; <span class="attr">fieldName</span>: <span class="string">'somefield'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// If there was an error, err is not null</span></span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using a unique constraint with the index</span></span><br><span class="line">db.ensureIndex(&#123; <span class="attr">fieldName</span>: <span class="string">'somefield'</span>, <span class="attr">unique</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Using a sparse unique index</span></span><br><span class="line">db.ensureIndex(&#123; <span class="attr">fieldName</span>: <span class="string">'somefield'</span>, <span class="attr">unique</span>: <span class="literal">true</span>, <span class="attr">sparse</span>: <span class="literal">true</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">// Format of the error message when the unique constraint is not met</span></span><br><span class="line">db.insert(&#123; <span class="attr">somefield</span>: <span class="string">'nedb'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// err is null</span></span><br><span class="line">  db.insert(&#123; <span class="attr">somefield</span>: <span class="string">'nedb'</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">    <span class="comment">// err is &#123; errorType: 'uniqueViolated'</span></span><br><span class="line">    <span class="comment">//        , key: 'name'</span></span><br><span class="line">    <span class="comment">//        , message: 'Unique constraint violated for key name' &#125;</span></span><br><span class="line">  &#125;);</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Remove index on field somefield</span></span><br><span class="line">db.removeIndex(<span class="string">'somefield'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// Example of using expireAfterSeconds to remove documents 1 hour</span></span><br><span class="line"><span class="comment">// after their creation (db's timestampData option is true here)</span></span><br><span class="line">db.ensureIndex(&#123; <span class="attr">fieldName</span>: <span class="string">'createdAt'</span>, <span class="attr">expireAfterSeconds</span>: <span class="number">3600</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">&#125;);</span><br><span class="line"></span><br><span class="line"><span class="comment">// You can also use the option to set an expiration date like so</span></span><br><span class="line">db.ensureIndex(&#123; <span class="attr">fieldName</span>: <span class="string">'expirationDate'</span>, <span class="attr">expireAfterSeconds</span>: <span class="number">0</span> &#125;, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line">  <span class="comment">// Now all documents will expire when system time reaches the date in their</span></span><br><span class="line">  <span class="comment">// expirationDate field</span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p><strong>Note:</strong> the <code>ensureIndex</code> function creates the index synchronously, so it’s best to use it at application startup. It’s quite fast so it doesn’t increase startup time much (35 ms for a collection containing 10,000 documents).</p><h2 id="Browser-version"><a href="#Browser-version" class="headerlink" title="Browser version"></a>Browser version</h2><p>The browser version and its minified counterpart are in the <code>browser-version/out</code> directory. You only need to require <code>nedb.js</code> or <code>nedb.min.js</code> in your HTML file and the global object <code>Nedb</code> can be used right away, with the same API as the server version:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">script</span> <span class="attr">src</span>=<span class="string">"nedb.min.js"</span>&gt;</span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="actionscript">  <span class="keyword">var</span> db = <span class="keyword">new</span> Nedb();   <span class="comment">// Create an in-memory only datastore</span></span></span><br><span class="line">  </span><br><span class="line"><span class="actionscript">  db.insert(&#123; planet: <span class="string">'Earth'</span> &#125;, <span class="function"><span class="keyword">function</span> <span class="params">(err)</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">   db.find(&#123;&#125;, <span class="function"><span class="keyword">function</span> <span class="params">(err, docs)</span> </span>&#123;</span></span><br><span class="line"><span class="actionscript">     <span class="comment">// docs contains the two planets Earth and Mars</span></span></span><br><span class="line">   &#125;);</span><br><span class="line">  &#125;);</span><br><span class="line"><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br></pre></td></tr></table></figure><p>If you specify a <code>filename</code>, the database will be persistent, and automatically select the best storage method available (IndexedDB, WebSQL or localStorage) depending on the browser. In most cases that means a lot of data can be stored, typically in hundreds of MB. <strong>WARNING</strong>: the storage system changed between v1.3 and v1.4 and is NOT back-compatible! Your application needs to resync client-side when you upgrade NeDB.</p><p>NeDB is compatible with all major browsers: Chrome, Safari, Firefox, IE9+. Tests are in the <code>browser-version/test</code> directory (files <code>index.html</code> and <code>testPersistence.html</code>).</p><p>If you fork and modify nedb, you can build the browser version from the sources, the build script is <code>browser-version/build.js</code>.</p><h2 id="Performance"><a href="#Performance" class="headerlink" title="Performance"></a>Performance</h2><h3 id="Speed"><a href="#Speed" class="headerlink" title="Speed"></a>Speed</h3><p>NeDB is not intended to be a replacement of large-scale databases such as MongoDB, and as such was not designed for speed. That said, it is still pretty fast on the expected datasets, especially if you use indexing. On a typical, not-so-fast dev machine, for a collection containing 10,000 documents, with indexing:  </p><ul><li>Insert: <strong>10,680 ops/s</strong></li><li>Find: <strong>43,290 ops/s</strong></li><li>Update: <strong>8,000 ops/s</strong></li><li>Remove: <strong>11,750 ops/s</strong>  </li></ul><p>You can run these simple benchmarks by executing the scripts in the <code>benchmarks</code> folder. Run them with the <code>--help</code> flag to see how they work.</p><h3 id="Memory-footprint"><a href="#Memory-footprint" class="headerlink" title="Memory footprint"></a>Memory footprint</h3><p>A copy of the whole database is kept in memory. This is not much on the<br>expected kind of datasets (20MB for 10,000 2KB documents).</p><h2 id="Use-in-other-services"><a href="#Use-in-other-services" class="headerlink" title="Use in other services"></a>Use in other services</h2><ul><li><a href="https://github.com/louischatriot/connect-nedb-session" target="_blank">connect-nedb-session</a> is a session store for<br>Connect and Express, backed by nedb</li><li>If you mostly use NeDB for logging purposes and don’t want the memory footprint of your application to grow too large, you can use <a href="https://github.com/louischatriot/nedb-logger" target="_blank">NeDB Logger</a> to insert documents in a NeDB-readable database</li><li>If you’ve outgrown NeDB, switching to MongoDB won’t be too hard as it is the same API. Use <a href="https://github.com/louischatriot/nedb-to-mongodb" target="_blank">this utility</a> to transfer the data from a NeDB database to a MongoDB collection</li><li>An ODM for NeDB: <a href="https://github.com/scottwrobinson/camo" target="_blank">Camo</a></li></ul><h2 id="Pull-requests"><a href="#Pull-requests" class="headerlink" title="Pull requests"></a>Pull requests</h2><p>If you submit a pull request, thanks! There are a couple rules to follow though to make it manageable:</p><ul><li>The pull request should be atomic, i.e. contain only one feature. If it contains more, please submit multiple pull requests. Reviewing massive, 1000 loc+ pull requests is extremely hard.</li><li>Likewise, if for one unique feature the pull request grows too large (more than 200 loc tests not included), please get in touch first.</li><li>Please stick to the current coding style. It’s important that the code uses a coherent style for readability.</li><li>Do not include sylistic improvements (“housekeeping”). If you think one part deserves lots of housekeeping, use a separate pull request so as not to pollute the code.</li><li>Don’t forget tests for your new feature. Also don’t forget to run the whole test suite before submitting to make sure you didn’t introduce regressions.</li><li>Do not build the browser version in your branch, I’ll take care of it once the code is merged.</li><li>Update the readme accordingly.</li><li>Last but not least: keep in mind what NeDB’s mindset is! The goal is not to be a replacement for MongoDB, but to have a pure JS database, easy to use, cross platform, fast and expressive enough for the target projects (small and self contained apps on server/desktop/browser/mobile). Sometimes it’s better to shoot for simplicity than for API completeness with regards to MongoDB.</li></ul><h2 id="Bug-reporting-guidelines"><a href="#Bug-reporting-guidelines" class="headerlink" title="Bug reporting guidelines"></a>Bug reporting guidelines</h2><p>If you report a bug, thank you! That said for the process to be manageable please strictly adhere to the following guidelines. I’ll not be able to handle bug reports that don’t:</p><ul><li>Your bug report should be a self-containing gist complete with a package.json for any dependencies you need. I need to run through a simple <code>git clone gist; npm install; node bugreport.js</code>, nothing more.</li><li>It should use assertions to showcase the expected vs actual behavior and be hysteresis-proof. It’s quite simple in fact, see this example: <a href="https://gist.github.com/louischatriot/220cf6bd29c7de06a486" target="_blank" rel="noopener">https://gist.github.com/louischatriot/220cf6bd29c7de06a486</a></li><li>Simplify as much as you can. Strip all your application-specific code. Most of the time you will see that there is no bug but an error in your code :)</li><li>50 lines max. If you need more, read the above point and rework your bug report. If you’re <strong>really</strong> convinced you need more, please explain precisely in the issue.</li><li>The code should be Javascript, not Coffeescript.</li></ul><h3 id="Bitcoins"><a href="#Bitcoins" class="headerlink" title="Bitcoins"></a>Bitcoins</h3><p>You don’t have time? You can support NeDB by sending bitcoins to this address: 1dDZLnWpBbodPiN8sizzYrgaz5iahFyb1</p><h2 id="License"><a href="#License" class="headerlink" title="License"></a>License</h2><p>See <a href="LICENSE">License</a></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;因为使用这个项目，自己的翻译分享出来，分享给大家，如果错误和遗漏，希望大家在Issues提醒指正。🙏&lt;br&gt;原文档地址：&lt;a href=&quot;https://github.com/louischatriot/nedb&quot; target=&quot;_blank&quot; rel=&quot;noopene
      
    
    </summary>
    
    
      <category term="Node.js" scheme="http://yuenshui.com/tags/Node-js/"/>
    
      <category term="Javascript" scheme="http://yuenshui.com/tags/Javascript/"/>
    
      <category term="NeDB" scheme="http://yuenshui.com/tags/NeDB/"/>
    
      <category term="文件型数据库" scheme="http://yuenshui.com/tags/%E6%96%87%E4%BB%B6%E5%9E%8B%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    
  </entry>
  
  <entry>
    <title>数独解题器</title>
    <link href="http://yuenshui.com/2017/03/02/Sudoku-solver/"/>
    <id>http://yuenshui.com/2017/03/02/Sudoku-solver/</id>
    <published>2017-03-01T16:00:00.000Z</published>
    <updated>2017-03-03T12:14:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近学习算法，就拿数独解题试刀吧。^_^</p><style>.sudokuLeft{padding: 0px 5px 0px 0px;line-height: 42px;}.sudokuFooter,.sudokuLeft{margin:0px;font-size: 22px;text-align: right;font-weight: 800;}.sudokuFooter{line-height: 24px;}.sudokuFooter div{display:inline-block; width:42px;text-align:center;padding:0px;}.sudokuLeft div{width:42px; height:42px;}.sudokuArea, .sudokuArea td{border:0px solid red; padding:0px;vertical-align:top;}.sudokuBox {margin: auto auto 1px; padding:0px;}.sudokuBox tr:nth-child(2n){background:#FFFFFF;!important}.sudokuBox td{width: 40px;height: 40px;padding:0px;    border: 1px solid #d8e2eb;}.sudokuBox div, .sudokuBox input {padding:0px;width: 40px; text-align:center}.sudokuBox input{font-size:22px;height: 24px; background: transparent;}.sudokuBox div{font-size: 12px;height: 12px;line-height: 12px;}.sudokuRight{vertical-align: top; width:250px;}.sudokuRight div {padding: 5px;} .sdkButton{display: inline-block; zoom: 1;*display: inline; vertical-align: baseline; margin: 2px; outline: none;cursor: pointer; text-align: center; text-decoration: none; font: 14px/100% Arial, Helvetica, sans-serif; padding: .5em .8em .55em; text-shadow: 0 1px 1px rgba(0,0,0,.3); -webkit-border-radius: .5em; -moz-border-radius: .5em; border-radius: .5em; -webkit-box-shadow: 0 1px 2px rgba(0,0,0,.2); -moz-box-shadow: 0 1px 2px rgba(0,0,0,.2); box-shadow: 0 1px 2px rgba(0,0,0,.2); }.bw{background: #EEEEEE;}.bb{background: #99AAFF;}.historyBox{min-height: 380px;overflow-y: auto;font-size: 12px;}</style><table class="sudokuArea"><tr><td width="40" height="370"><div id="sudokuLeft" class="sudokuLeft"></div></td><td width="370"><table class="sudokuBox" id="sudokuBox"></table><div id="sudokuFooter" class="sudokuFooter"></div><p><input type="button" id="importSudoku" class="sdkButton bw" value="导入"><input type="button" id="setSudoku" class="sdkButton bw" value="清空"><input type="button" id="cleanButton" class="sdkButton bw" value="重新计算"><input style="display:none" type="button" id="testButton" class="sdkButton" value="测试"><input type="button" id="runButton" class="sdkButton" value="计算"><input type="button" id="nextButton" style="display: none;" class="sdkButton" value="下一步"></p><p>　　导入或输入已知的数字，然后点击“计算”按钮，就可以看到数独方格内显示出标注，一直点击“下一步”，可以看到求解过程。右侧的过程描述方便学习数独解法。</p><p>　　“清空”按钮可以清空所有数字内容，方便重现填入数字。</p><p>　　“重新计算”按钮方便对数独重新反复观看解题过程。</p><br></td><td class="sudokuRight"><div><div class="historyBox" style="padding:0px;"><ol style="margin-left: 30px;" id="historyBox"></ol></div></div></td></tr></table><script>    var html = "";    var gSum = 45;    var sB = document.getElementById("sudokuBox");    var row, cell1, nodeE, style;    for(var i = 0; i < 9; i++) {        row = sB.insertRow(i);        for(var j = 0; j < 9; j++) {            cell1 = row.insertCell(j);            cell1.setAttribute("id", "sudokuPoint" + i + j);            cell1.innerHTML = "<div></div><input type=\"text\" />";            style = "";            if(j % 3 == 2) style += "border-right-width:3px;";            if(i % 3 == 2) style += "border-bottom-width:3px;";            if(j == 0) style += "border-left-width:3px;";            if(i == 0) style += "border-top-width:3px;";            if(style != "") cell1.style = style;        }    }    var sdkData = [        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0],        [0,0,0,0,0,0,0,0,0]    ];    var rowDoc = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', "I"];    var colDoc = ['1', '2', '3', '4', '5', '6', '7', '8', "9"];    var runData = [];    var vacancy = [];    var vacData = [[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1]];    var sudokuLeft = document.getElementById("sudokuLeft")    var sudokuFooter = document.getElementById("sudokuFooter")    for(var i = 0; i < 9; i++) {        var note = document.createElement('div');        note.innerHTML = rowDoc[i];        sudokuLeft.appendChild(note);        note = document.createElement('div');        note.innerHTML = colDoc[i];        sudokuFooter.appendChild(note);    }    document.getElementById("importSudoku").onclick = function() {        var html = '<div style="width:100%;text-align:center;margin:0 auto;"><textarea id="importBox" rows="9" cols="15">000000000\r\n000000000\r\n000000000\r\n000000000\r\n000000000\r\n000000000\r\n000000000\r\n000000000\r\n000000000</textarea><br><input type="button" style="width: 80px;padding: 5px;font-size: 16px;margin: 5px;" value="导入" onclick="opener.importSudoku(document.getElementById(\'importBox\').value);window.close();" /></div>';        myWindow=window.open('','','width=200, height=200, scrollbars=yes')        myWindow.document.write(html);        myWindow.focus()        return;    }    function importSudoku(content) {        var cA = content.split(/\s+/);        for(var i = 0; i < 9; i++) for(var j = 0; j < 9; j++) {            sdkData[i][j] = typeof cA[i][j] == "undefined" ? 0 : parseInt(cA[i][j]);        }        for(var i in sdkData) runData[i] = sdkData[i].concat();        vacancy = getVacancy(runData);        setNote(runData, vacancy);        viewInit();        viewNote();        cleanHistory();        document.getElementById("nextButton").style.display = "";        if(prePoint[0] != -1) document.getElementById("sudokuPoint" + prePoint[0] + prePoint[1]).className = "";    }    function viewInit () {        for(var i = 0; i < 9; i++) {            for(var j = 0; j < 9; j++) {                nodeE = document.getElementById("sudokuPoint" + i + j);                nodeE.innerHTML = "<div></div><input type=\"text\" value='" + (sdkData[i][j] > 0 ? sdkData[i][j] : "") + "' />";                nodeE.className = sdkData[i][j] > 0 ? "bw" : "";            }        }    }    document.getElementById("setSudoku").onclick = setSudoku;    function setSudoku() {        initData();        viewInit();        cleanHistory();    }    // 测试    document.getElementById("testButton").onclick = function() {        solveNakedTriplet(vacancy);    }    document.getElementById("cleanButton").onclick = function() {        //document.getElementById("nextButton").style.display = "none";        for(var i in sdkData) runData[i] = sdkData[i].concat();        vacancy = getVacancy(runData);        setNote(runData, vacancy);        viewInit();        viewNote();        cleanHistory();        document.getElementById("nextButton").style.display = "";        if(prePoint[0] != -1) document.getElementById("sudokuPoint" + prePoint[0] + prePoint[1]).className = "";    }    function initData() {        for(var i = 0; i < 9; i++) for (var j = 0; j < 9; j++) sdkData[i][j] = 0;    }    function checkInput() {        var v = 0, num = 0;        for(var i = 0; i < 9; i++) for (var j = 0; j < 9; j++) {            v = parseInt(document.getElementById("sudokuPoint" + i + j).childNodes[1].value);            sdkData[i][j] = v ? v : 0;            if(v) num++;        }        return num > 16;    }    var prePoint = [-1, -1];    document.getElementById("nextButton").onclick = function() {        var nodeE, rs, stepString = "", vIndex;        var cDoc = {'g': '九宫格', 'col': "列", 'row': '行'}        var runS = false;        for(var i in vacancy) if(vacancy[i][2] == 0) {            runS = true;             break;        }        if(!runS) {            console.log("已经计算完成，不需要计算");            return;        }        if(prePoint[0] != -1) document.getElementById("sudokuPoint" + prePoint[0] + prePoint[1]).className = "";        // 唯一候选        rs = solveEndPoint(runData);        if(rs[0] != -1) stepString = "唯一候选法[" + rowDoc[rs[0]] + "," + colDoc[rs[1]] + "]所在" + cDoc[rs[4]] + "只可以是：" + rs[2];        console.log("step1:", rs);        // solveBaseAban基础摒弃法        if(rs[0] == -1) {            rs = solveBaseAban(vacancy);            console.log("step2:", rs);            if(rs[0] != -1) stepString = "基础摒弃法[" + rowDoc[rs[0]] + "," + colDoc[rs[1]] + "]所在" + cDoc[rs[4]] + "只有他可以为：" + rs[2];        }        // 隐式唯一候选        if(rs[0] == -1) {            rs = solveEndPointHidden(vacancy);            console.log("step3:", rs);            if(rs[0] != -1) stepString = "隐式唯一候选[" + rowDoc[rs[0]] + "," + colDoc[rs[1]] + "]只可以是：" + rs[2];        }        if(rs[0] != -1) {            try {                nodeE = document.getElementById("sudokuPoint" + rs[0] + rs[1]);                nodeE.className = "bb";                nodeE.innerHTML = "<div></div><input type=\"text\" value='" + rs[2] + "' />";                vIndex = rs[5] ? rs[5] : getVacancyIndex(rs);                console.log("vIndex:", vIndex);                vacancy[vIndex][2] = rs[2];                 vIndex > -1 && setVacancy(vacancy, vIndex);                addHistory(stepString);                runData[rs[0]][rs[1]] = rs[2];                vacData[rs[0]][rs[1]] = -1;            }            catch(e) {                console.log(e, rs);            }        }        else {            console.log("solveYuanyang");            var history = solveYuanyang(vacancy);            for(var i in history) addHistory(history[i]);            history = solveAreaClean(vacancy);            for(var i in history) addHistory(history[i]);            history = solveYuanyangHidden(vacancy);            for(var i in history) addHistory(history[i]);            history = solveXwing(vacancy);            for(var i in history) addHistory(history[i]);            history = solveXYwing(vacancy);            for(var i in history) addHistory(history[i]);            history = solveNakedTriplet            for(var i in history) addHistory(history[i]);        }        //console.log(vacancy);        viewNote();        //console.log(stepString);        if(rs[0] != -1) prePoint = rs;        //console.log(runData);    }    document.getElementById("runButton").onclick = function() {        if(!checkInput()) {alert("至少输入17个数字才可以计算"); return };        for(var i in sdkData) runData[i] = sdkData[i].concat();        //for(var i in sdkData) vacData[i] = -1;        //console.log(sdkData);        vacancy = getVacancy(runData);        setNote(runData, vacancy);        viewInit();        viewNote();        //solveYuanyang(vacancy);        //viewNote();        cleanHistory();        document.getElementById("nextButton").style.display = "";    }    function getVacancyIndex(point) {        //console.log("getVacancyIndex:", point);        for(var i in vacancy) if(vacancy[i][0] == point[0] && vacancy[i][1] == point[1]) {            //console.log(vacancy[i], i);            return i;        }        return -1;    }    function cleanHistory() {        document.getElementById("historyBox").innerHTML = "";    }    function addHistory(str) {        var oLi = document.createElement('li');        oLi.innerHTML = str;        document.getElementById("historyBox").appendChild(oLi);    }    function viewNote () {        var noteString = "";        for(var i in vacancy) {            noteString = "";            if(vacancy[i][2] == 0) noteString = vacancy[i][3];            document.getElementById("sudokuPoint" + vacancy[i][0] + vacancy[i][1]).firstChild.innerHTML = noteString;        }    }    function setNote(data, vac) {        var point;        for(var v in vac) {            point = vac[v];            pa = [1, 1, 1, 1, 1, 1, 1, 1, 1];            for(var i = 0; i < 9; i++) {                if(data[i][point[1]] == 0) continue;                pa[data[i][point[1]] - 1] = 0;            }             for(var j = 0; j < 9; j++) {                if(data[point[0]][j] == 0) continue;                pa[data[point[0]][j] - 1] = 0;            }            for(var i = parseInt(point[0] / 3) * 3, p1 = 0; p1 < 3; p1++, i++) for(var j = parseInt(point[1] / 3) * 3, p2 = 0; p2 < 3; p2++, j++) {                if(data[i][j] == 0) continue;                pa[data[i][j] - 1] = 0;            }            for(var i in pa) if(pa[i] != '0') vac[v][3] += "" + (parseInt(i) + 1);        }    }    // 遍历出每一个空位，进行尝试解题    function setVacancy(data, i) {        var point = vacancy[i], xStart, yStart;        //console.log("setVacancy:", i, point);        xStart = parseInt(point[0] / 3) * 3;        yStart = parseInt(point[1] / 3) * 3;        for(var x in vacancy) {            if(vacancy[x][2] > 0) continue;            if(vacancy[x][0] == point[0] ||                 vacancy[x][1] == point[1] ||                 (vacancy[x][0] >= xStart && vacancy[x][0] < xStart + 3 && vacancy[x][1] >= yStart && vacancy[x][1] < yStart + 3))            {                //console.log("setPre:", vacancy[x], point[2]);                vacancy[x][3] = vacancy[x][3].replace(point[2], "");                //console.log("setFre:", vacancy[x]);            }        }    }    // 遍历出每一个空位，进行尝试解题    function getVacancy(data) {        var vacancy = [];        for(var i = 0; i < 9; i++) {            for(var j = 0; j < 9; j++) {                if(data[i][j] == 0) {                    vacancy.push([i, j, 0, ""]);                    vacData[i][j] = vacancy.length - 1;                }            }        }        return vacancy;    }    function existDelete(str, char) {        var ret = str.indexOf(char);        if(ret > -1) str = str.replace(char, '');        return [ret, str];    }    function getXY(vi) {        return "[" + rowDoc[vacancy[vi][0]] + "," + colDoc[vacancy[vi][1]] + "]";    }    // 三链数删减法    function solveNakedTriplet(vac) {        var historyList = [];        // 遍历每一行        for(var i = 0; i < 9; i++) for(var j = 0; j < 9; j++) {// 遍历每一个方格            var cPi = vacData[i][j];    // vac索引            if(cPi == -1) continue;            var cP = vacancy[cPi];        // vac元素            console.log("row p1:", i, j, vacData[i][j], cP);            var cStr = cP[3];            // 可选数字            if(cStr.length != 2 && cStr.length != 3) continue;            for(var j2 = j + 1; j2 < 9; j2++) {                var cPi2 = vacData[i][j2];    // vac索引                if(cPi2 == -1) continue;                var cP2 = vacancy[cPi2];        // vac元素                console.log("       p2:", i, j2, vacData[i][j2], cP2);                var cStr2 = cP2[3];            // 可选数字                var fullStr = "";                if(cStr.length == 3) {                    if(cStr2.length == 3) {                        if(cStr != cStr2) continue;                        else {                            fullStr = cStr;                        }                    }                    else if(cStr2.length == 2) {                        if(cStr.indexOf(cStr2[0]) == -1 || cStr.indexOf(cStr2[1]) == -1) continue;                        fullStr = cStr;                    }                    else continue;                }                else {                    if(cStr2.length == 2) {                        if(cStr == cStr2) continue; // 数对，跳过                        if(cStr2.indexOf(cStr[0]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[1]].sort().join();                        }                        else if(cStr2.indexOf(cStr[1]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[0]].sort().join();                        }                        else continue;                    }                    else if(cStr2.length == 3) {                        if(cStr2.indexOf(cStr[0]) == -1 || cStr2.indexOf(cStr[1]) == -1) continue;                        fullStr = cStr2;                    }                    else continue;                }                console.log("       fulstr:", fullStr);                if(fullStr == "") continue;                for (var j3 = j2 + 1; j3 < 9; j3++) {                    var cPi3 = vacData[i][j3];    // vac索引                    if(cPi3 == -1) continue;                    var cP3 = vacancy[cPi3];        // vac元素                    console.log("         p3:", i, j3, vacData[i][j3], cP3);                    var cStr3 = cP3[3];            // 可选数字                    var reSearch = new RegExp("[" + fullStr + "]");                    if(cStr3.length == 2) {                        if(fullStr.indexOf(cStr3[0]) == -1 || fullStr.indexOf(cStr3[1]) == -1) continue;                        if(cStr3.search(reSearch) == -1) continue;                    }                    else if(cStr3 == fullStr) {                    }                    else continue;                    for(var j4 = 0; j4 < 9; j4++) {                        if(j4 == j || j4 == j3 || j4 == j2) continue;                        var cPi4 = vacData[i][j4];    // vac索引                        if(cPi4 == -1) continue;                        var cP4 = vacancy[cPi4];        // vac元素                        console.log("           p4:", i, j4, vacData[i][j4], cP4);                        var cStr4 = cP4[3];            // 可选数字                        for(var n = 0; n < 3; n++) {                            if(cStr4.indexOf(fullStr[n]) == -1) continue;                            historyList.push("三链数删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi3) + ",三格的可选数字是" + fullStr + ",同行的" + getXY(cPi4) + "可选数字" + cStr4 + "中去除数字:" + fullStr[n]);                            vacancy[cPi4][3] = cStr4.replace(fullStr[n], '');                            console.log("           replace:", vacancy[cPi4], fullStr[n]);                        }                    }                }            }        }        // 遍历每一列        for(var i = 0; i < 9; i++) for(var j = 0; j < 9; j++) {// 遍历每一个方格            var cPi = vacData[j][i];    // vac索引            if(cPi == -1) continue;            var cP = vacancy[cPi];        // vac元素            console.log("col p1:", j, i, vacData[j][i], cP);            var cStr = cP[3];            // 可选数字            if(cStr.length != 2 && cStr.length != 3) continue;            for(var j2 = j + 1; j2 < 9; j2++) {                var cPi2 = vacData[j2][i];    // vac索引                if(cPi2 == -1) continue;                var cP2 = vacancy[cPi2];        // vac元素                console.log("    p2:", j2, i, vacData[j2][i], cP2);                var cStr2 = cP2[3];            // 可选数字                var fullStr = "";                if(cStr.length == 3) {                    if(cStr2.length == 3) {                        if(cStr != cStr2) continue;                        else {                            fullStr = cStr;                        }                    }                    else if(cStr2.length == 2) {                        if(cStr.indexOf(cStr2[0]) == -1 || cStr.indexOf(cStr2[1]) == -1) continue;                        fullStr = cStr;                    }                    else continue;                }                else {                    if(cStr2.length == 2) {                        if(cStr == cStr2) continue; // 数对，跳过                        if(cStr2.indexOf(cStr[0]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[1]].sort().join();                        }                        else if(cStr2.indexOf(cStr[1]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[0]].sort().join();                        }                        else continue;                    }                    else if(cStr2.length == 3) {                        if(cStr2.indexOf(cStr[0]) == -1 || cStr2.indexOf(cStr[1]) == -1) continue;                        fullStr = cStr2;                    }                    else continue;                }                console.log("    fullStr:", fullStr);                if(fullStr == "") continue;                for (var j3 = j2 + 1; j3 < 9; j3++) {                    var cPi3 = vacData[j3][i];    // vac索引                    if(cPi3 == -1) continue;                    var cP3 = vacancy[cPi3];        // vac元素                    console.log("        p3:", j3, i, vacData[j3][i], cP3);                    var cStr3 = cP3[3];            // 可选数字                    var reSearch = new RegExp("[" + fullStr + "]");                    if(cStr3.length == 2) {                        if(fullStr.indexOf(cStr3[0]) == -1 || fullStr.indexOf(cStr3[1]) == -1) continue;                        if(cStr3.search(reSearch) == -1) continue;                    }                    else if(cStr3 == fullStr) {                    }                    else continue;                    for(var j4 = 0; j4 < 9; j4++) {                        if(j4 == j || j4 == j3 || j4 == j2) continue;                        var cPi4 = vacData[j4][i];    // vac索引                        if(cPi4 == -1) continue;                        var cP4 = vacancy[cPi4];        // vac元素                        console.log("            p4:", j4, i, vacData[j4][i], cP4);                        var cStr4 = cP4[3];            // 可选数字                        for(var n = 0; n < 3; n++) {                            if(cStr4.indexOf(fullStr[n]) == -1) continue;                            historyList.push("三链数删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi3) + ",三格的可选数字是" + fullStr + ",同列的" + getXY(cPi4) + "可选数字" + cStr4 + "中去除数字:" + fullStr[n]);                            vacancy[cPi4][3] = cStr4.replace(fullStr[n], '');                            console.log("            replace:", vacancy[cPi4], fullStr[n]);                        }                    }                }            }        }        for(var g33i = 0, rowStart = 0; g33i < 3; ++g33i, rowStart = g33i * 3)        for(var g33j = 0, colStart = 0; g33j < 3; ++g33j, colStart = g33j * 3)        for(var i = rowStart; i < rowStart + 3; i++)        for(var j = colStart; j < colStart + 3; j++) {// 遍历每一个方格            var cPi = vacData[i][j];    // vac索引            if(cPi == -1) continue;            var cP = vacancy[cPi];        // vac元素            console.log("g3 p1:", i, j, vacData[i][j], cP);            var cStr = cP[3];            // 可选数字            if(cStr.length != 2 && cStr.length != 3) continue;            for(var i2 = rowStart; i2 < rowStart + 3; i2++) for(var j2 = colStart; j2 < colStart + 3; j2++) {                if(i == i2 && j == j2) continue;                var cPi2 = vacData[i2][j2];    // vac索引                if(cPi2 == -1) continue;                var cP2 = vacancy[cPi2];        // vac元素                console.log("    p2:", i2, j2, vacData[i2][j2], cP2);                var cStr2 = cP2[3];            // 可选数字                var fullStr = "";                if(cStr.length == 3) {                    if(cStr2.length == 3) {                        if(cStr != cStr2) continue;                        else {                            fullStr = cStr;                        }                    }                    else if(cStr2.length == 2) {                        if(cStr.indexOf(cStr2[0]) == -1 || cStr.indexOf(cStr2[1]) == -1) continue;                        fullStr = cStr;                    }                    else continue;                }                else {                    if(cStr2.length == 2) {                        if(cStr == cStr2) continue; // 数对，跳过                        if(cStr2.indexOf(cStr[0]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[1]].sort().join();                        }                        else if(cStr2.indexOf(cStr[1]) > -1) {                            fullStr = [cStr2[0], cStr2[1], cStr[0]].sort().join();                        }                        else continue;                    }                    else if(cStr2.length == 3) {                        if(cStr2.indexOf(cStr[0]) == -1 || cStr2.indexOf(cStr[1]) == -1) continue;                        fullStr = cStr2;                    }                    else continue;                }                console.log("    fullStr:", fullStr);                if(fullStr == "") continue;                for(var i3 = rowStart; i3 < rowStart + 3; i3++) for(var j3 = colStart; j3 < colStart + 3; j3++) {                    if((i3 == i && j3 == j) || (i3 == i2 && j3 == j2)) continue;                    var cPi3 = vacData[i3][j3];    // vac索引                    if(cPi3 == -1) continue;                    var cP3 = vacancy[cPi3];        // vac元素                    console.log("        p3:", i3, j3, vacData[i3][j3], cP3);                    var cStr3 = cP3[3];            // 可选数字                    var reSearch = new RegExp("[" + fullStr + "]");                    if(cStr3.length == 2) {                        if(fullStr.indexOf(cStr3[0]) == -1 || fullStr.indexOf(cStr3[1]) == -1) continue;                        if(cStr3.search(reSearch) == -1) continue;                    }                    else if(cStr3.length == fullStr) {                    }                    else continue;                    for(var i4 = rowStart; i4 < rowStart + 3; i4++) for(var j4 = colStart; j4 < colStart + 3; j4++) {                        if((i4 == i && j4 == j) || (i4 == i2 && j4 == j2) || (i4 == i3 && j4 == j3)) continue;                        var cPi4 = vacData[i4][j4];    // vac索引                        if(cPi4 == -1) continue;                        var cP4 = vacancy[cPi4];        // vac元素                        console.log("            p4:", i4, j4, vacData[i4][j4], cP4);                        var cStr4 = cP4[3];            // 可选数字                        for(var n = 0; n < 3; n++) {                            if(cStr4.indexOf(fullStr[n]) == -1) continue;                            historyList.push("三链数删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi3) + ",三格的可选数字是" + fullStr + ",同九宫格的" + getXY(cPi4) + "可选数字" + cStr4 + "中去除数字:" + fullStr[n]);                            vacancy[cPi4][3] = cStr4.replace(fullStr[n], '');                            console.log("            replace:", vacancy[cPi4], fullStr[n]);                        }                    }                }            }        }        console.log(historyList);        return historyList;    }    // XY-Wing    function solveXYwing(vac) {        var rowData = {}, historyList = [];        for(var i = 0; i < 9; i++) {// 遍历每一行            for(var j = 0; j < 9; j++) {// 遍历每一个方格                cPi = vacData[i][j];    // vac索引                if(cPi == -1) continue;                cP = vacancy[cPi];        // vac元素                console.log("row:", i, j, vacData[i][j], cP);                cStr = cP[3];            // 可选数字                if(cStr.length != 2) continue;                for(var k = j + 1; k < 9; k++) {// 取一个方格，向下遍历方格与之匹配                    var wingX, wingY, wingZ;                    cPi2 = vacData[i][k];                    if(cPi2 == -1) continue;                    cP2 = vacancy[cPi2];                    console.log("    row:", i, k, vacData[i][k], cP2);                    cStr2 = cP2[3];                    if(cStr2.length != 2) continue;                    if(cStr2 == cStr || (cStr2.indexOf(cStr[0]) == -1 && cStr2.indexOf(cStr[0]) == -1)) continue;                    if(cStr2.indexOf(cStr[0]) > -1) {                        wingX = cStr[0];                        wingY = cStr[1];                    }                    else {                        wingX = cStr[1];                        wingY = cStr[2];                    }                    wingZ = cStr2.replace(wingX, '');                    for(var i2 = 0; i2 < 9; i2++) {                        if(i == i2) continue;                        cPi3 = vacData[i2][j];                        if(cPi3 == -1) continue;                        cP3 = vacancy[cPi3];                        console.log("        row:", i2, j, vacData[i2][j], cP3);                        cStr3 = cP3[3];                        cPi4 = vacData[i2][k];                        if(cPi4 == -1) continue;                        cP4 = vacancy[cPi4];                        console.log("        row:", i2, k, vacData[i2][k], cP4);                        cStr4 = cP4[3];                        var wingYZ = wingY > wingZ ? wingZ + "" + wingY : wingY + "" + wingZ;                        if(cStr3 == wingYZ) {                            historyList.push("XY-Wing删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi3) + "构成XY-wing删减法条件，" + getXY(cPi4) + "的可选数字" + cStr4 + "中移除数字：" + wingZ);                            vacancy[vacData[i2][k]][3] = cStr4.replace(wingZ, '');                        }                        else if(cStr4 == wingYZ) {                            historyList.push("XY-Wing删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi4) + "构成XY-wing删减法条件，" + getXY(cPi3) + "的可选数字" + cStr3 + "中移除数字：" + wingY);                            vacancy[vacData[i2][j]][3] = cStr4.replace(wingY, '');                        }                    }                }            }        }        for(var i = 0; i < 9; i++) {// 遍历每一列            for(var j = 0; j < 9; j++) {// 遍历每一个方格                cPi = vacData[j][i];    // vac索引                if(cPi == -1) continue;                cP = vacancy[cPi];        // vac元素                console.log(i, j, vacData[j][i], cP);                cStr = cP[3];            // 可选数字                if(cStr.length != 2) continue;                for(var k = j + 1; k < 9; k++) {// 取一个方格，向下遍历方格与之匹配                    var wingX, wingY, wingZ;                    cPi2 = vacData[k][i];                    if(cPi2 == -1) continue;                    cP2 = vacancy[cPi2];                    cStr2 = cP2[3];                    if(cStr2.length != 2) continue;                    if(cStr2 == cStr || (cStr2.indexOf(cStr[0]) == -1 && cStr2.indexOf(cStr[0]) == -1)) continue;                    if(cStr2.indexOf(cStr[0]) > -1) {                        wingX = cStr[0];                        wingY = cStr[1];                    }                    else {                        wingX = cStr[1];                        wingY = cStr[2];                    }                    wingZ = cStr2.replace(wingX, '');                    for(var i2 = 0; i2 < 9; i2++) {                        if(i == i2) continue;                        cPi3 = vacData[j][i2];                        if(cPi3 == -1) continue;                        cP3 = vacancy[cPi3];                        cStr3 = cP3[3];                        cPi4 = vacData[i2][k];                        if(cPi4 == -1) continue;                        cP4 = vacancy[cPi4];                        cStr4 = cP4[3];                        var wingYZ = wingY > wingZ ? wingZ + "" + wingY : wingY + "" + wingZ;                        if(cStr3 == wingYZ) {                            historyList.push("XY-Wing删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi3) + "构成XY-wing删减法条件，" + getXY(cPi4) + "的可选数字" + cStr4 + "中移除数字：" + wingZ);                            vacancy[vacData[k][i2]][3] = cStr4.replace(wingZ, '');                        }                        else if(cStr4 == wingYZ) {                            historyList.push("XY-Wing删减法，" + getXY(cPi) + "," + getXY(cPi2) + "," + getXY(cPi4) + "构成XY-wing删减法条件，" + getXY(cPi3) + "的可选数字" + cStr3 + "中移除数字：" + wingY);                            vacancy[vacData[j][i2]][3] = cStr4.replace(wingY, '');                        }                    }                }            }        }        for(var g33i = 0, rowStart = 0; g33i < 0; ++g33i, rowStart = g33i * 3)        for(var g33j = 0, colStart = 0; g33j < 3; ++g33j, colStart = g33j * 3)        for(var i = rowStart; i < rowStart + 3; i++)        for(var j = colStart; j < colStart + 3; j++) {// 遍历每一个方格            var cPi = vacData[i][j];    // vac索引            if(cPi == -1) continue;            var cP = vacancy[cPi];        // vac元素            if(cP[2] != 0) continue;            console.log("g3 p1:", i, j, vacData[i][j], cP);            var cStr = cP[3];            // 可选数字            if(cStr.length != 2) continue;            for(var i2 = rowStart; i2 < rowStart + 3; i2++) for(var j2 = colStart; j2 < colStart + 3; j2++) {                if(i == i2 && j == j2) continue;                var cPi2 = vacData[i2][j2];    // vac索引                if(cPi2 == -1) continue;                var cP2 = vacancy[cPi2];        // vac元素                if(cP2[2] != 0) continue;                console.log("    p2:", i2, j2, vacData[i2][j2], cP2);                var cStr2 = cP2[3];            // 可选数字                if(cStr2.length != 2 || cStr2 == cStr) continue;                if(cStr2.indexOf(cStr[0]) == -1 && cStr2.indexOf(cStr[1]) == -1) continue;                if(cStr2.indexOf(cStr[0]) > -1) {                    var wingY = cStr[0];                    var wingX = cStr[1];                }                else if(cStr2.indexOf(cStr[1]) > -1) {                    var wingY = cStr[1];                    var wingX = cStr[0];                }                var wingZ = cStr2.replace(new RegExp("[" + cStr + "]"), '');                var selStr = [wingX, wingZ].sort().join('');                // 找同行、同列的XZ                for(var i3 = 0; i3 < 9; i3++) {                    var point4 = [                        vacData[i3, j],                        vacData[i, i3],                        vacData[i3, j2],                        vacData[i2, i3]                    ];                    for(var q = 0; q < 4; q++) {                        if(point4[q] == -1) continue;                        var cPcheck = vacancy[point4[q]];                        if(cPcheck[2] != 0) continue;                        var cStr3 = cPcheck[3];                        if(cStr3 != selStr) continue;                        console.log("        cPcheck:", cPcheck);                        if(cP2[0] > rowStart2 && cP2[0] < rowStart2 + 3) {                            var delNum = cStr3.replace(new RegExp("[" + cStr + "]"), '');                            var rowStart2 = parseInt(cP3[0] / 3) * 3;                            var colStart2 = parseInt(cP3[1] / 3) * 3;                            for(var i4 = colStart2; i4 < colStart2 + 3; i4++) {                                var cPi4 = varData[cP2[0]][i4];                                if(cPi4 == -1) continue;                                var cP4 = vacancy[cPi4];                                if(cP4[2] != 0) continue;                                if(cP4[3].indexOf(delNum) == -1) continue;                                historyList.input();                                vacancy[cPi4][3] = cP4[3].replace(delNum, '');                            }                            for(var i4 = colStart2; i4 < colStart2 + 3; i4++) {                                var cPi4 = varData[cP[0]][i4];                                if(cPi4 == -1) continue;                                var cP4 = vacancy[cPi4];                                if(cP4[2] != 0) continue;                                if(cP4[3].indexOf(delNum) == -1) continue;                                historyList.input();                                vacancy[cPi4][3] = cP4[3].replace(delNum, '');                            }                        }                    }                }            }        }        console.log(historyList);        return historyList;    }    // X-Wing删减法    function solveXwing(vac) {        //    行数、数字、九宫格索引：vacIndex数组        // rowData[0][5]['g00'].push(vi);        //    列数、数字、九宫格索引：vacIndex数组        // colData[0][5]['g00'].push(vi);        var g33 = {}, rowData = {}, colData = {}, gi = "", rI, cI, cC, cSel, repHist = "", historyList = [];        var cDoc = {'g': '九宫格', 'col': "列", 'row': '行'}        for(var vi in vac) {            if(vac[vi][2] != 0) continue;            rI = vac[vi][0];            cI = vac[vi][1];            if(!rowData[rI]) rowData[rI] = {};            if(!colData[cI]) colData[cI] = {};            cSel = vac[vi][3];            for(var i = cSel.length - 1; i >= 0; i--) {                cC = cSel[i];                if(!rowData[rI][cC]) rowData[rI][cC] = [vi];                else rowData[rI][cC].push(vi);                if(!colData[cI][cC]) colData[cI][cC] = [vi];                else colData[cI][cC].push(vi);            }        }        for(var rowN in rowData) for(var num in rowData[rowN]) {            var p = rowData[rowN][num];            if(p.length != 2) continue;            for(var rowN2 in rowData) {                if(rowN2 <= rowN || !rowData[rowN2][num] || rowData[rowN2][num].length != 2) continue;                var p2 = rowData[rowN2][num];                if(vac[p[0]][1] != vac[p2[0]][1] || vac[p[1]][1] != vac[p2[1]][1]) continue;                repHist = "X-Wing删减法，" + getXY(p[0]) + "," + getXY(p[1]) + "," + getXY(p2[0]) + "," + getXY(p2[1]) + ",满足X-Wing删减条件，";                cDoc = p[0] + "," + p[1] + "," + p2[0] + "," + p2[1];                for(var vi in vac) {                    if(vac[vi][2] != 0 || cDoc.indexOf(vi) > -1) continue;                    if(vac[vi][1] != vac[p[0]][1] && vac[vi][1] != vac[p[1]][1]) continue;                    if(vac[vi][3].indexOf(num) > -1) {                        historyList.push(repHist + getXY(vi) + "的可选数字" + vac[vi][3] + "中移除数字：" + num);                        vac[vi][3] = vac[vi][3].replace(num, '');                    }                }            }        }        for(var colN in colData) for(var num in colData[colN]) {            var p = colData[colN][num];            if(p.length != 2) continue;            for(var colN2 in colData) {                if(colN2 <= colN || !colData[colN2][num] || colData[colN2][num].length != 2) continue;                var p2 = colData[colN2][num];                if(vac[p[0]][0] != vac[p2[0]][0] || vac[p[1]][0] != vac[p2[1]][0]) continue;                repHist = "X-Wing删减法，" + getXY(p[0]) + "," + getXY(p[1]) + "," + getXY(p2[0]) + "," + getXY(p2[1]) + ",满足X-Wing删减条件，";                cDoc = p[0] + "," + p[1] + "," + p2[0] + "," + p2[1];                //console.log("x-wing", colN, colN2, num, cDoc);                for(var vi in vac) {                    if(vac[vi][2] != 0 || cDoc.indexOf(vi) > -1) continue;                    if(vac[vi][0] != vac[p[0]][0] && vac[vi][0] != vac[p[1]][0]) continue;                    if(vac[vi][3].indexOf(num) > -1) {                        historyList.push(repHist + getXY(vi) + "的可选数字" + vac[vi][3] + "中移除数字：" + num);                        vac[vi][3] = vac[vi][3].replace(num, '');                    }                }            }        }        return historyList;    }    function solveAreaClean(vac) {        // 九宫推行列        // 九宫格索引、行或列、数字、行数或列数：vacIndex数组        // g33['g00']['row'][3][0].push(vi);        //    行数、数字、九宫格索引：vacIndex数组        // rowData[0][5]['g00'].push(vi);        //    列数、数字、九宫格索引：vacIndex数组        // colData[0][5]['g00'].push(vi);        var g33 = {}, rowData = {}, colData = {}, gi = "", rI, cI, cC, cSel, repHist = "", historyList = [];        var cDoc = {'g': '九宫格', 'col': "列", 'row': '行'}        for(var vi in vac) {            if(vac[vi][2] != 0) continue;            gi = "g" + parseInt(vac[vi][0] / 3) + parseInt(vac[vi][1] / 3);            rI = vac[vi][0];            cI = vac[vi][1];            if(!g33[gi]) g33[gi] = {'row':{}, 'col':{}};            if(!rowData[rI]) rowData[rI] = {};            if(!colData[cI]) colData[cI] = {};            cSel = vac[vi][3];            for(var i = cSel.length - 1; i >= 0; i--) {                cC = cSel[i];                if(!g33[gi]['row'][cC]) g33[gi]['row'][cC] = {};                if(!g33[gi]['row'][cC][vac[vi][0]]) g33[gi]['row'][cC][vac[vi][0]] = [vi];                else g33[gi]['row'][cC][vac[vi][0]].push(vi);                if(!g33[gi]['col'][cC]) g33[gi]['col'][cC] = {};                if(!g33[gi]['col'][cC][vac[vi][1]]) g33[gi]['col'][cC][vac[vi][1]] = [vi];                else g33[gi]['col'][cC][vac[vi][1]].push(vi);                if(!rowData[rI][cC]) rowData[rI][cC] = {};                if(!rowData[rI][cC][gi]) rowData[rI][cC][gi] = [vi];                else rowData[rI][cC][gi].push(vi);                if(!colData[cI][cC]) colData[cI][cC] = {};                if(!colData[cI][cC][gi]) colData[cI][cC][gi] = [vi];                else colData[cI][cC][gi].push(vi);            }        }    }    // 候选数区块删减法    function solveAreaClean(vac) {        // 九宫推行列        // 九宫格索引、行或列、数字、行数或列数：vacIndex数组        // g33['g00']['row'][3][0].push(vi);        //    行数、数字、九宫格索引：vacIndex数组        // rowData[0][5]['g00'].push(vi);        //    列数、数字、九宫格索引：vacIndex数组        // colData[0][5]['g00'].push(vi);        var g33 = {}, rowData = {}, colData = {}, gi = "", rI, cI, cC, cSel, repHist = "", historyList = [];        var cDoc = {'g': '九宫格', 'col': "列", 'row': '行'}        for(var vi in vac) {            if(vac[vi][2] != 0) continue;            gi = "g" + parseInt(vac[vi][0] / 3) + parseInt(vac[vi][1] / 3);            rI = vac[vi][0];            cI = vac[vi][1];            if(!g33[gi]) g33[gi] = {'row':{}, 'col':{}};            if(!rowData[rI]) rowData[rI] = {};            if(!colData[cI]) colData[cI] = {};            cSel = vac[vi][3];            for(var i = cSel.length - 1; i >= 0; i--) {                cC = cSel[i];                if(!g33[gi]['row'][cC]) g33[gi]['row'][cC] = {};                if(!g33[gi]['row'][cC][vac[vi][0]]) g33[gi]['row'][cC][vac[vi][0]] = [vi];                else g33[gi]['row'][cC][vac[vi][0]].push(vi);                if(!g33[gi]['col'][cC]) g33[gi]['col'][cC] = {};                if(!g33[gi]['col'][cC][vac[vi][1]]) g33[gi]['col'][cC][vac[vi][1]] = [vi];                else g33[gi]['col'][cC][vac[vi][1]].push(vi);                if(!rowData[rI][cC]) rowData[rI][cC] = {};                if(!rowData[rI][cC][gi]) rowData[rI][cC][gi] = [vi];                else rowData[rI][cC][gi].push(vi);                if(!colData[cI][cC]) colData[cI][cC] = {};                if(!colData[cI][cC][gi]) colData[cI][cC][gi] = [vi];                else colData[cI][cC][gi].push(vi);            }        }        var cArr = [], cSel = "";        for(var gi in g33) for(var rc in g33[gi]) for(var sn in g33[gi][rc]) {            if(Object.keys(g33[gi][rc][sn]).length != 1) continue;            //console.log(gi, rc, sn, g33[gi][rc][sn]);            cArr = Object.values(g33[gi][rc][sn])[0];            cSel = cArr.join(",");            repHist = "候选数区块删减法，";            for(var i in cArr) {                repHist += "[" + rowDoc[vac[cArr[i]][0]] + "," + colDoc[vac[cArr[i]][1]] + "]，";            }            repHist += "中的数字" + sn + "只在所在九宫格的";            if(rc == 'row') {                cX = vac[cArr[0]][0];                for(var vi in vac) {                    if(vac[vi][2] != 0 ||                         vac[vi][0] != cX ||                         cSel.indexOf(vi) > -1 ||                         vac[vi][3].indexOf(sn) == -1) continue;                    historyList.push(repHist + rowDoc[vac[cArr[0]][0]] + "行在，同行的[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]的可选数字" + vac[vi][3] + "中去除：" + sn);                    vac[vi][3] = vac[vi][3].replace(sn, '');                }            }            else {                cY = vac[cArr[0]][1];                for(var vi in vac) {                    if(vac[vi][2] != 0 ||                         vac[vi][1] != cY ||                         cSel.indexOf(vi) > -1 ||                         vac[vi][3].indexOf(sn) == -1) continue;                    historyList.push(repHist + colDoc[vac[cArr[0]][1]] + "列存在，同列的[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]的可选数字" + vac[vi][3] + "中去除：" + sn);                    vac[vi][3] = vac[vi][3].replace(sn, '');                }            }        }        for(rI in rowData) for(var sn in rowData[rI]) {            if(Object.keys(rowData[rI][sn]).length != 1) continue;            cArr = Object.values(rowData[rI][sn])[0];            cSel = cArr.join(",");            repHist = "候选数区块删减法，";            for(var i in cArr) {                repHist += "[" + rowDoc[vac[cArr[i]][0]] + "," + colDoc[vac[cArr[i]][1]] + "]，";            }            repHist += "所在的" + rowDoc[vac[cArr[0]][0]] + "行中的数字" + sn + "只在一个九宫格中，该九宫格中的";            var rowStart = parseInt(vac[cArr[0]][0] / 3) * 3;            var colStart = parseInt(vac[cArr[0]][1] / 3) * 3;            for(var vi in vac) {                if(vac[vi][2] != 0 ||                     vac[vi][0] < rowStart ||                     vac[vi][0] >= rowStart + 3 ||                    vac[vi][1] < colStart ||                     vac[vi][1] >= colStart + 3 ||                     cSel.indexOf(vi) > -1 ||                    vac[vi][3].indexOf(sn) == -1 ) continue;                    historyList.push(repHist + "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]的可选数字" + vac[vi][3] + "中去除：" + sn);                    vac[vi][3] = vac[vi][3].replace(sn, '');            }        }        for(rI in colData) for(var sn in colData[rI]) {            if(Object.keys(colData[rI][sn]).length != 1) continue;            cArr = Object.values(colData[rI][sn])[0];            cSel = cArr.join(",");            repHist = "候选数区块删减法，";            for(var i in cArr) {                repHist += "[" + rowDoc[vac[cArr[i]][0]] + "," + colDoc[vac[cArr[i]][1]] + "]，";            }            repHist += "所在的" + colDoc[vac[cArr[0]][1]] + "列中的数字" + sn + "只在一个九宫格中，该九宫格中的";            var rowStart = parseInt(vac[cArr[0]][0] / 3) * 3;            var colStart = parseInt(vac[cArr[0]][1] / 3) * 3;            for(var vi in vac) {                if(vac[vi][2] != 0 ||                     vac[vi][0] < rowStart ||                     vac[vi][0] >= rowStart + 3 ||                    vac[vi][1] < colStart ||                     vac[vi][1] >= colStart + 3 ||                     cSel.indexOf(vi) > -1 ||                    vac[vi][3].indexOf(sn) == -1 ) continue;                    historyList.push(repHist + "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]的可选数字" + vac[vi][3] + "中去除：" + sn);                    vac[vi][3] = vac[vi][3].replace(sn, '');            }        }        //console.log(historyList);        return historyList;        //console.log(JSON.stringify(g33, null, 4));    }    // 基础摒弃法    function solveBaseAban(vac) {        var rowData = {}, colData = {}, g33 = {}, gi = "", cSel;        for(var vi in vac) {            if(vac[vi][2] != 0) continue;            gi = "g" + parseInt(vac[vi][0] / 3) + parseInt(vac[vi][1] / 3);            if(!g33[gi]) g33[gi] = {};            if(!rowData[vac[vi][0]]) rowData[vac[vi][0]] = {};            if(!colData[vac[vi][1]]) colData[vac[vi][1]] = {};            cSel = vac[vi][3];            // g33['g00'][3] = [33,39,42]            for(var i = cSel.length - 1; i >= 0; i--) {                if(!g33[gi][cSel[i]]) g33[gi][cSel[i]] = [];                g33[gi][cSel[i]].push(vi);                if(!rowData[vac[vi][0]][cSel[i]]) rowData[vac[vi][0]][cSel[i]] = [];                rowData[vac[vi][0]][cSel[i]].push(vi);                if(!colData[vac[vi][1]][cSel[i]]) colData[vac[vi][1]][cSel[i]] = [];                colData[vac[vi][1]][cSel[i]].push(vi);            }        }        for(gi in g33) for(var num in g33[gi]) {            if(g33[gi][num].length != 1) continue;            var vi = g33[gi][num][0];            return [vac[vi][0], vac[vi][1], num, 'baseAban', 'g', vi];            break;        }        for(var row in rowData) for(var num in rowData[row]) {            if(rowData[row][num].length != 1) continue;            var vi = rowData[row][num][0];            return [vac[vi][0], vac[vi][1], num, 'baseAban', 'row', vi];            break;        }        for(var col in colData) for(var num in colData[col]) {            if(colData[col][num].length != 1) continue;            var vi = colData[col][num][0];            return [vac[vi][0], vac[vi][1], num, 'baseAban', 'col', vi];            break;        }        //console.log(g33, rowData, colData);        return [-1, -1];    }    // 隐性候选数对删减法（鸳鸯）    function solveYuanyangHidden (vac) {        var rowN, colN, gN, selStr = "", rowData = {}, colData = {}, g33 = {};        var rowStr = {}, colStr = {}, gStr = {}, vacArr = [], historyList = [];        //       行号、数字::indexVac数组        // rowData[0][3].push(vi)        for(var vi in vac) {            if(vac[vi][2] != 0) continue;            rowN = vac[vi][0];            colN = vac[vi][1];            gN = 'g' + parseInt(rowN / 3) + parseInt(colN / 3);            if(!rowData[rowN]) rowData[rowN] = {};            if(!colData[colN]) colData[colN] = {};            if(!g33[gN]) g33[gN] = {};            //console.log(rowN, colN, gN, JSON.stringify(vac[vi]));            for(var si = vac[vi][3].length - 1; si >= 0; si--) {                selStr = vac[vi][3][si];                if(!rowData[rowN][selStr]) rowData[rowN][selStr] = [];                rowData[rowN][selStr].push(vi);                if(!colData[colN][selStr]) colData[colN][selStr] = [];                colData[colN][selStr].push(vi);                if(!g33[gN][selStr]) g33[gN][selStr] = [];                g33[gN][selStr].push(vi);            }        }        for(var rowN in rowData) for(var sn in rowData[rowN]) {            if(rowData[rowN][sn].length == 2) {                selStr = rowData[rowN][sn].join(',');                if(!rowStr[rowN]) rowStr[rowN] = {};                if(!rowStr[rowN][selStr]) rowStr[rowN][selStr] = []                rowStr[rowN][selStr].push(sn);            }        }        for(var colN in colData) for(var sn in colData[colN]) {            if(colData[colN][sn].length == 2) {                selStr = colData[colN][sn].join(',');                if(!colStr[colN]) colStr[colN] = {};                if(!colStr[colN][selStr]) colStr[colN][selStr] = [];                colStr[colN][selStr].push(sn);            }        }        for(var gN in g33) for(var sn in g33[gN]) {            if(g33[gN][sn].length == 2) {                selStr = g33[gN][sn].join(',');                if(!gStr[gN]) gStr[gN] = {};                if(!gStr[gN][selStr]) gStr[gN][selStr] = [];                gStr[gN][selStr].push(sn);            }        }        var repHist = "";        for(var rowN in rowStr) for(var selStr in rowStr[rowN]) {            if(rowStr[rowN][selStr].length != 2) continue;            //console.log("row:", rowN, selStr, rowStr[rowN][selStr]);            var numStr = rowStr[rowN][selStr].join(',');            vacArr = selStr.split(',');            repHist = "隐性候选数对删减法，" + numStr + "在" + rowDoc[vac[vacArr[0][0]][0]] + "行只存在于";            for(var rc in vacArr) {                var vi = vacArr[rc];                repHist += "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]，";            }            repHist += "所以";            for(var i = 0; i < 2; i++) {                var str = vac[vacArr[i]][3];                for(var j = str.length - 1; j >= 0; j--) {                    if(numStr.indexOf(str[j]) == -1) {                        historyList.push(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        //console.log(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        vac[vacArr[i]][3] = str = str.replace(str[j], '');                        //console.log(vac[vacArr[i]]);                    }                }            }        }        for(var colN in colStr) for(var selStr in colStr[colN]) {            if(colStr[colN][selStr].length != 2) continue;            //console.log("col:", colN, selStr, colStr[colN][selStr]);            var numStr = colStr[colN][selStr].join(',');            vacArr = selStr.split(',');            repHist = "隐性候选数对删减法，" + numStr + "在" + colDoc[vac[vacArr[0][0]][1]] + "列只存在于";            for(var rc in vacArr) {                var vi = vacArr[rc];                repHist += "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]，";            }            repHist += "所以";            for(var i = 0; i < 2; i++) {                var str = vac[vacArr[i]][3];                for(var j = str.length - 1; j >= 0; j--) {                    if(numStr.indexOf(str[j]) == -1) {                        historyList.push(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        //console.log(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        vac[vacArr[i]][3] = str = str.replace(str[j], '');                        //console.log(vac[vacArr[i]]);                    }                }            }        }        for(var gN in gStr) for(var selStr in gStr[gN]) {            if(gStr[gN][selStr].length != 2) continue;            //console.log("g3:", gN, selStr, gStr[gN][selStr]);            var numStr = gStr[gN][selStr].join(',');            vacArr = selStr.split(',');            repHist = "隐性候选数对删减法，" + numStr + "在[" + rowDoc[vac[vacArr[0]][0]] + "," + colDoc[vac[vacArr[0]][1]] + "]所在九宫格只存在于";            for(var rc in vacArr) {                var vi = vacArr[rc];                repHist += "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]，";            }            repHist += "所以";            for(var i = 0; i < 2; i++) {                var str = vac[vacArr[i]][3];                for(var j = str.length - 1; j >= 0; j--) {                    if(numStr.indexOf(str[j]) == -1) {                        historyList.push(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        //console.log(repHist + "[" + rowDoc[vac[vacArr[i]][0]] + "," + colDoc[vac[vacArr[i]][1]] + "]的可选数字" + str + "中去除：" + str[j]);                        vac[vacArr[i]][3] = str = str.replace(str[j], '');                        //console.log(vac[vacArr[i]]);                    }                }            }        }        //console.log(historyList);        return historyList;    }    // 候选数对删减法（鸳鸯），包括三数集删减法, 目前存在缺陷，出现类似234,234,23 时无法判断    function solveYuanyang (vac) {        var rowC = {}, colC = {}, g33 = {}, sk, ix, iy, i9, historyList = [];        for(var i in vac) {            if(vac[i][2] != 0) continue;            sk = vac[i][3];            ix = vac[i][0];            iy = vac[i][1];            if(!rowC[ix]) rowC[ix] = {};            if(!rowC[ix][sk]) {                rowC[ix][sk] = [i];            }            else {                rowC[ix][sk].push(i);            }            if(!colC[iy]) colC[iy] = {};            if(!colC[iy][sk]) {                colC[iy][sk] = [i];            }            else {                colC[iy][sk].push(i);            }            i9 = "g" + parseInt(vac[i][0] / 3) + parseInt(vac[i][1] / 3);            if(!g33[i9]) g33[i9] = {};            if(!g33[i9][sk]) {                g33[i9][sk] = [i];            }            else {                g33[i9][sk].push(i);            }        }        // console.log("row:", rowC);        // console.log("col:", colC);        // console.log("i9:", g33);        var repRs = [];        var repHist = "";        var cDoc = "";        var cH = "";        for(var i in rowC) {// 行，x            for(var k in rowC[i]) {// 备选, k 为备选                if(k.length < 2 || k.length != rowC[i][k].length) continue;                cDoc = "";                repHist = "候选数对删减法，在" + rowDoc[i] + "行存在数对" + k + "，";                for(var rc in rowC[i][k]) {                    vi = rowC[i][k][rc];                    //console.log(rc, ":", rowC[i][k][rc]);                    repHist += "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]，";                    cDoc += vac[vi][1];                }                //console.log(rowC[i][k], cDoc, repHist);                for(var vk in vac) {                    // 遍历所有的空块，同行的，如果存在就替换                    if(vac[vk][2] == 0 && vac[vk][0] == i && cDoc.indexOf(vac[vk][1]) == -1) {                        for(var ind = k.length - 1; ind >= 0; ind--) {                            cH = repHist + "该行[" + rowDoc[vac[vk][0]] + "," + colDoc[vac[vk][1]] + "]可选数" + vac[vk][3] + "中去除" + k[ind];                            repRs = existDelete(vac[vk][3], k[ind]);                            if(repRs[0] > -1) {                                vac[vk][3] = repRs[1];                                historyList.push(cH);                            }                        }                    }                }            }        }        //console.log("row:", historyList, colC);        for(var i in colC) {// 列，x            for(var k in colC[i]) {// 备选, k 为备选                if(k.length < 2 || k.length != colC[i][k].length) continue;                cDoc = "";                repHist = "候选数对删减法，在" + colDoc[i] + "列存在数对" + k + "，";                for(var rc in colC[i][k]) {                    vi = colC[i][k][rc];                    //console.log(rc, ":", colC[i][k][rc]);                    repHist += "(" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + ")，";                    cDoc += vi + ",";                }                //console.log(colC[i][k], cDoc, repHist);                for(var vk in vac) {                    // 遍历所有的空块，同列的，如果存在就替换                    if(vac[vk][2] == 0 && vac[vk][1] == i && cDoc.indexOf(vk) == -1) {                        //console.log("find exist:", vac[vk]);                        for(var ind = k.length - 1; ind >= 0; ind--) {                            cH = repHist + "该列[" + rowDoc[vac[vk][0]] + "," + colDoc[vac[vk][1]] + "]可选数" + vac[vk][3] + "中去除" + k[ind];                            repRs = existDelete(vac[vk][3], k[ind]);                            if(repRs[0] > -1) {                                vac[vk][3] = repRs[1];                                historyList.push(cH);                            }                        }                    }                }            }        }        //console.log("col:", historyList);        var xStart, yStart;        for(var i in g33) {// 列，x            for(var k in g33[i]) {// 备选, k 为备选                if(k.length < 2 || k.length != g33[i][k].length) continue;                cDoc = "";                repHist = "候选数对删减法，在" + i + "宫格中存在数对" + k + "，";                for(var rc in g33[i][k]) {                    vi = g33[i][k][rc];                    //console.log(rc, ":", g33[i][k][rc], vac[vi]);                    repHist += "[" + rowDoc[vac[vi][0]] + "," + colDoc[vac[vi][1]] + "]，";                    cDoc += vi + ",";                }                xStart = parseInt(vac[g33[i][k][0]][0] / 3) * 3;                yStart = parseInt(vac[g33[i][k][0]][1] / 3) * 3;                //console.log(g33[i][k], cDoc, repHist, xStart, yStart, g33[i][k][0]);                for(var vk in vac) {                    // 遍历所有的空块，同行的，如果存在就替换                    if(                        vac[vk][2] == 0 &&                         (                            vac[vk][0] >= xStart &&                             vac[vk][0] < xStart + 3 &&                             vac[vk][1] >= yStart &&                             vac[vk][1] < yStart + 3                            ) &&                         cDoc.indexOf(vk) == -1                        )                    {                        for(var ind = k.length - 1; ind >= 0; ind--) {                            cH = repHist + "该九宫格[" + rowDoc[vac[vk][0]] + "," + colDoc[vac[vk][1]] + "]可选数" + vac[vk][3] + "中去除" + k[ind];                            repRs = existDelete(vac[vk][3], k[ind]);                            //console.log(cH, repRs, vac[vk]);                            if(repRs[0] > -1) {                                vac[vk][3] = repRs[1];                                historyList.push(cH);                            }                        }                    }                }            }        }        //console.log("g33:", historyList);        return historyList;    }    // 隐式唯一候选数法    function solveEndPointHidden (vac) {        for(var i in vac) {            //console.log("check:", vac[i]);            if(vac[i][2] != 0 || vac[i][3].length > 1) continue;            //console.log("fire:", vac[i]);            vacancy[i][2] = parseInt(vac[i][3]);            //console.log("change:", vac[i]);            return [vac[i][0], vac[i][1], vac[i][2], 'endpoint', 'h', i];            break;        }        return [-1, -1];    }    // 唯一候选数法    function solveEndPoint(data) {        var rowData = {}, colData = {}, g33 = {}, gi = "";        for(var vi in vacancy) {            if(vacancy[vi][2] != 0) continue;            gi = "g" + parseInt(vacancy[vi][0] / 3) + parseInt(vacancy[vi][1] / 3);            if(!g33[gi]) g33[gi] = [];            if(!rowData[vacancy[vi][0]]) rowData[vacancy[vi][0]] = [];            if(!colData[vacancy[vi][1]]) colData[vacancy[vi][1]] = [];            g33[gi].push(vi);            rowData[vacancy[vi][0]].push(vi);            colData[vacancy[vi][1]].push(vi);        }        for(gi in g33) {            if(g33[gi].length != 1) continue;            var vi = g33[gi][0];            return [vacancy[vi][0], vacancy[vi][1], vacancy[vi][3], 'endpoint', 'g', vi];            break;        }        for(var i in rowData) {            if(rowData[i].length != 1) continue;            var vi = rowData[i][0];            return [vacancy[vi][0], vacancy[vi][1], vacancy[vi][3], 'endpoint', 'row', vi];            break;        }        for(var i in colData) {            if(colData[i].length != 1) continue;            var vi = colData[i][0];            return [vacancy[vi][0], vacancy[vi][1], vacancy[vi][3], 'endpoint', 'col', vi];            break;        }        // console.log(g33, rowData, colData);        return [-1, -1];    }</script>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近学习算法，就拿数独解题试刀吧。^_^&lt;/p&gt;
&lt;style&gt;.sudokuLeft{padding: 0px 5px 0px 0px;line-height: 42px;}.sudokuFooter,.sudokuLeft{margin:0px;font-size: 2
      
    
    </summary>
    
    
      <category term="数独" scheme="http://yuenshui.com/tags/%E6%95%B0%E7%8B%AC/"/>
    
      <category term="Javascript" scheme="http://yuenshui.com/tags/Javascript/"/>
    
  </entry>
  
  <entry>
    <title>Node.js实现sleep的几种办法</title>
    <link href="http://yuenshui.com/2017/02/26/Node-js-sleep/"/>
    <id>http://yuenshui.com/2017/02/26/Node-js-sleep/</id>
    <published>2017-02-25T16:00:00.000Z</published>
    <updated>2017-02-26T09:45:39.000Z</updated>
    
    <content type="html"><![CDATA[<p>最近研究Node.js应用的稳定性和性能优化，发现测试时会用到故意延迟或卡顿的场景。度娘搜了一下有不少实现sleep的方式，顺便总结一下，分享给各位看官。</p><h2 id="死循环"><a href="#死循环" class="headerlink" title="死循环"></a>死循环</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sleep</span>(<span class="params">sleepTime</span>) </span>&#123;</span><br><span class="line"><span class="keyword">for</span>(<span class="keyword">var</span> start = +<span class="keyword">new</span> <span class="built_in">Date</span>; +<span class="keyword">new</span> <span class="built_in">Date</span> - start &lt;= sleepTime;) &#123;&#125;;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">var</span> t1 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line">sleep(<span class="number">3000</span>);</span><br><span class="line"><span class="keyword">var</span> t2 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="built_in">console</span>.log(t2 - t1);</span><br></pre></td></tr></table></figure><p>这是最简单粗暴的实现，确实sleep了，也确实卡死了，CPU会飙升，无论你的服务器CPU有多么Niubility。</p><h2 id="async"><a href="#async" class="headerlink" title="async"></a>async</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="keyword">async</span> = <span class="built_in">require</span>(<span class="string">'asyncawait/async'</span>);</span><br><span class="line"><span class="keyword">var</span> <span class="keyword">await</span> = <span class="built_in">require</span>(<span class="string">'asyncawait/await'</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sleep</span>(<span class="params">ms</span>) </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="params">resolve</span> =&gt;</span> setTimeout(resolve, ms))</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">(<span class="keyword">async</span>(</span><br><span class="line"><span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">var</span> t1 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="keyword">await</span>(sleep(<span class="number">3000</span>));</span><br><span class="line"><span class="keyword">var</span> t2 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="built_in">console</span>.log(t2 - t1);</span><br><span class="line">&#125;</span><br><span class="line">))();</span><br></pre></td></tr></table></figure><p>缺陷是程序需要在async域里执行。等ES7出来后会有原生的await/async可用，不用引入asyncawait模块了。<br>优点是，这种方式实际上是用了setTimeout，没有形成进程阻塞，不会造成性能和负载问题。</p><h2 id="generator"><a href="#generator" class="headerlink" title="generator"></a>generator</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> co = <span class="built_in">require</span>(<span class="string">'co'</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sleep</span>(<span class="params">milliSeconds</span>) </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params">done</span>) </span>&#123;</span><br><span class="line">setTimeout(done, milliSeconds);</span><br><span class="line">&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">co(<span class="function"><span class="keyword">function</span>*(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">var</span> t1 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="keyword">yield</span> sleep(<span class="number">3000</span>);</span><br><span class="line"><span class="keyword">var</span> t2 = +<span class="keyword">new</span> <span class="built_in">Date</span>();</span><br><span class="line"><span class="built_in">console</span>.log(t2 - t1);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>目前generator在开发中应用的越来越多，尤其是koa在更多的业务中使用之后。缺点和async一样，必须在generator的域里才可使用。优点和上例相同。</p><h2 id="addon"><a href="#addon" class="headerlink" title="addon"></a>addon</h2><p>这里有C++实现的模块：<a href="https://github.com/ErikDubbelboer/node-sleep" target="_blank" rel="noopener">https://github.com/ErikDubbelboer/node-sleep</a></p><ul><li>sleep.sleep(n): sleep for n seconds</li><li>sleep.msleep(n): sleep for n miliseconds</li><li>sleep.usleep(n): sleep for n microseconds (1 second is 1000000 microseconds)<br>能够实现更加精细的时间精确度。缺点需要安装这个模块，^_^，这也许算不上什么缺点。</li></ul><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>上面4种实现方法，各有优略表现最好的是用C++实现的模块，最简单的实现是死循环，对于不同的业务和应用场景，就看大家喜欢哪种方式了。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><p><a href="https://www.zhihu.com/question/31636244" target="_blank" rel="noopener">https://www.zhihu.com/question/31636244</a><br><a href="http://www.zhouhua.info/2015/03/04/sleep/" target="_blank" rel="noopener">http://www.zhouhua.info/2015/03/04/sleep/</a><br><a href="https://github.com/ErikDubbelboer/node-sleep" target="_blank" rel="noopener">https://github.com/ErikDubbelboer/node-sleep</a></p><p><strong> 注意：本站博文均系原创，欢迎转载，请注明出处和原网址 </strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;最近研究Node.js应用的稳定性和性能优化，发现测试时会用到故意延迟或卡顿的场景。度娘搜了一下有不少实现sleep的方式，顺便总结一下，分享给各位看官。&lt;/p&gt;
&lt;h2 id=&quot;死循环&quot;&gt;&lt;a href=&quot;#死循环&quot; class=&quot;headerlink&quot; title=&quot;死
      
    
    </summary>
    
    
      <category term="Node.js" scheme="http://yuenshui.com/tags/Node-js/"/>
    
      <category term="sleep" scheme="http://yuenshui.com/tags/sleep/"/>
    
  </entry>
  
  <entry>
    <title>让Node.js程序稳定运行</title>
    <link href="http://yuenshui.com/2017/02/25/How-to-make-the-node-js-application-run-stably/"/>
    <id>http://yuenshui.com/2017/02/25/How-to-make-the-node-js-application-run-stably/</id>
    <published>2017-02-24T16:00:00.000Z</published>
    <updated>2017-02-26T09:33:05.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="How-to-make-the-node-js-application-run-stably"><a href="#How-to-make-the-node-js-application-run-stably" class="headerlink" title="How to make the node.js application run stably"></a>How to make the node.js application run stably</h2><p>Node.js程序在越来越多的公司部署在生产环境，程序的稳定运行逐渐被大家所关注。下面我就分享一下这方面的经验，仅供参考。</p><h2 id="一、捕获错误"><a href="#一、捕获错误" class="headerlink" title="一、捕获错误"></a>一、捕获错误</h2><h3 id="try-语句"><a href="#try-语句" class="headerlink" title="try 语句"></a>try 语句</h3><p>try语句捕获错误是Javascript在网页或服务器上最常见也是最简单的捕获错误方式。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line"><span class="comment">// 业务代码</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span>(e) &#123;</span><br><span class="line"><span class="comment">// 异常时执行的代码</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="comment">// 最后执行的代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>这种方式适用于当前执行的代码，语法错误，变量使用异常等情况的异常捕获。但是对于函数、对象里的异常，是无法捕获的。比如：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> testVar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line"><span class="keyword">var</span> testFun = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(testVar.toString());</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span>(e) &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run catch"</span>, e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run finally"</span>);</span><br><span class="line">&#125;</span><br><span class="line">testFun();</span><br></pre></td></tr></table></figure></p><p>如果你们觉得会运行“console.log(testVar.toString())”时捕获到变量未初始化，那就错了。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"> yuenshui@yuenshuideMacBook-Pro ~/Documents/website/nodejs&gt; node test001.js</span><br><span class="line">run finally</span><br><span class="line">/Users/yuenshui/Documents/website/nodejs/test001.js:9</span><br><span class="line">       console.log(testVar.toString());</span><br><span class="line">                          ^</span><br><span class="line"></span><br><span class="line">TypeError: Cannot <span class="built_in">read</span> property <span class="string">'toString'</span> of undefined</span><br><span class="line">    at testFun (/Users/yuenshui/Documents/website/nodejs/test001.js:9:29)</span><br><span class="line">    at Object.&lt;anonymous&gt; (/Users/yuenshui/Documents/website/nodejs/test001.js:19:1)</span><br><span class="line">    at Module._compile (module.js:409:26)</span><br><span class="line">    at Object.Module._extensions..js (module.js:416:10)</span><br><span class="line">    at Module.load (module.js:343:32)</span><br><span class="line">    at Function.Module._load (module.js:300:12)</span><br><span class="line">    at Function.Module.runMain (module.js:441:10)</span><br><span class="line">    at startup (node.js:139:18)</span><br><span class="line">    at node.js:968:3</span><br></pre></td></tr></table></figure></p><p>程序执行到这里就会闪退(Crash)。</p><p>解决办法是，在执行的地方捕获错误。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> testVar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> testFun = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(testVar.toString());</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">testFun();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span>(e) &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run catch"</span>, e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run finally"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">yuenshui@yuenshuideMacBook-Pro ~/Documents/website/nodejs&gt; node test001.js</span><br><span class="line">run catch [TypeError: Cannot <span class="built_in">read</span> property <span class="string">'toString'</span> of undefined]</span><br><span class="line">run finally</span><br></pre></td></tr></table></figure></p><h3 id="类型判断"><a href="#类型判断" class="headerlink" title="类型判断"></a>类型判断</h3><p>如上例，只要在使用变量前加上类型判断，就不会出现程序异常。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> testVar;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> testFun = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">if</span>(<span class="keyword">typeof</span> testVar != <span class="string">'undefined'</span>) &#123;</span><br><span class="line"><span class="built_in">console</span>.log(testVar.toString());</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">testFun();</span><br></pre></td></tr></table></figure></p><p>像这样的情况，一般的熟手都能做的很好，不用依赖try捕获异常来保证稳定运行。</p><h3 id="Promise"><a href="#Promise" class="headerlink" title="Promise"></a>Promise</h3><p>如果认为上面两个例子通过判断类型避免异常和try语句捕获异常就能解决异常的出现，那就错误。因为还有异步的存在（^_^ 天刹的异步），异步执行的代码中如果产生异常，调用阶段使用try是没意义的。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="built_in">Promise</span> = <span class="built_in">require</span>(<span class="string">"bluebird"</span>);</span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> testFun = <span class="function"><span class="keyword">function</span>(<span class="params">callback</span>) </span>&#123;</span><br><span class="line">fs.readFile(<span class="string">"./abc.bin"</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line"><span class="keyword">if</span>(err) <span class="keyword">throw</span> err;</span><br><span class="line">&#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">testFun(<span class="function"><span class="keyword">function</span>(<span class="params">data</span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(data);</span><br><span class="line">&#125;);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">catch</span>(e) &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run catch"</span>, e);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">finally</span> &#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"run finally"</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p><p>运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">yuenshui@yuenshuideMacBook-Pro ~/Documents/website/nodejs&gt; node test013.js                            </span><br><span class="line">module.js:327</span><br><span class="line">    throw err;</span><br><span class="line">    ^</span><br><span class="line"></span><br><span class="line">Error: Cannot find module <span class="string">'/Users/yuenshui/Documents/website/nodejs/test013.js'</span></span><br><span class="line">    at Function.Module._resolveFilename (module.js:325:15)</span><br><span class="line">    at Function.Module._load (module.js:276:25)</span><br><span class="line">    at Function.Module.runMain (module.js:441:10)</span><br><span class="line">    at startup (node.js:139:18)</span><br><span class="line">    at node.js:968:3</span><br></pre></td></tr></table></figure></p><p>异步下可以使用Promise捕获：<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="built_in">Promise</span> = <span class="built_in">require</span>(<span class="string">"bluebird"</span>);</span><br><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> testFun = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">Promise</span>(<span class="function"><span class="keyword">function</span>(<span class="params">resolve, reject</span>) </span>&#123;</span><br><span class="line">fs.readFile(<span class="string">"./test.txt"</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err, data</span>) </span>&#123;</span><br><span class="line"><span class="keyword">if</span>(err) reject(err);</span><br><span class="line"><span class="keyword">else</span> &#123;</span><br><span class="line">resolve(data);</span><br><span class="line">&#125;</span><br><span class="line">&#125;);</span><br><span class="line">&#125;);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">testFun()</span><br><span class="line">.then(<span class="function"><span class="keyword">function</span> (<span class="params">data</span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"then:"</span>, data.toString());</span><br><span class="line">&#125;)</span><br><span class="line">.catch(<span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">"catch:"</span>, err);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><p>运行结果：<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">yuenshui@yuenshuideMacBook-Pro ~/Documents/website/nodejs/chat&gt; node test015.js</span><br><span class="line">catch: &#123; [Error: ENOENT: no such file or directory, open <span class="string">'./test.txt'</span>] errno: -2, code: <span class="string">'ENOENT'</span>, syscall: <span class="string">'open'</span>, path: <span class="string">'./test.txt'</span> &#125;</span><br></pre></td></tr></table></figure></p><h3 id="使用domain"><a href="#使用domain" class="headerlink" title="使用domain"></a>使用domain</h3><p>这是官方给的一个例子，在用domain创建的域里执行程序，将会捕获到程序的错误。<br>官方给的解释是：domain提供了将多个不同IO操作作为单个组处理的方式。如果任何注册到域的事件发射器或回调函数发出“error”事件或者抛出一个错误，那么将通知域对象，而不是直接让这个错误的上下文从process.on(‘uncaughtException’)处理程序中丢失掉，甚至程序立即退出并显示错误代码。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> d = <span class="built_in">require</span>(<span class="string">'domain'</span>).create();</span><br><span class="line">d.on(<span class="string">'error'</span>, (er) =&gt; &#123;</span><br><span class="line">  <span class="comment">// The error won't crash the process, but what it does is worse!</span></span><br><span class="line">  <span class="comment">// Though we've prevented abrupt process restarting, we are leaking</span></span><br><span class="line">  <span class="comment">// resources like crazy if this ever happens.</span></span><br><span class="line">  <span class="comment">// This is no better than process.on('uncaughtException')!</span></span><br><span class="line">  <span class="built_in">console</span>.log(<span class="string">'error, but oh well'</span>, er.message);</span><br><span class="line">&#125;);</span><br><span class="line">d.run(<span class="function"><span class="params">()</span> =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">require</span>(<span class="string">'http'</span>).createServer(<span class="function">(<span class="params">req, res</span>) =&gt;</span> &#123;</span><br><span class="line">    handleRequest(req, res);</span><br><span class="line">  &#125;).listen(PORT);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h3 id="uncaughtException-事件"><a href="#uncaughtException-事件" class="headerlink" title="uncaughtException 事件"></a>uncaughtException 事件</h3><p>通过process的uncaughtException事件，来处理未捕获的异常。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">process.on(<span class="string">'uncaughtException'</span>, <span class="function"><span class="keyword">function</span> (<span class="params">err</span>) </span>&#123;</span><br><span class="line"><span class="built_in">console</span>.log(<span class="string">'catch fatal error: '</span> + err.stack || err);</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure></p><h2 id="二、进程守护"><a href="#二、进程守护" class="headerlink" title="二、进程守护"></a>二、进程守护</h2><h3 id="pm2"><a href="#pm2" class="headerlink" title="pm2"></a>pm2</h3><p>官网：<a href="http://pm2.keymetrics.io/" target="_blank" rel="noopener">http://pm2.keymetrics.io/</a><br>PM2是一个进程管理器，在进程闪退后立即重启该进程，起到了守护作用，并且记录error_log，用于错误分析。<br>安装<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install -g pm2</span><br></pre></td></tr></table></figure></p><p>用法<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">$ npm install pm2 -g     <span class="comment"># 命令行安装 pm2 </span></span><br><span class="line">$ pm2 start app.js -i 4 <span class="comment">#后台运行pm2，启动4个app.js </span></span><br><span class="line">                                <span class="comment"># 也可以把'max' 参数传递给 start</span></span><br><span class="line">                                <span class="comment"># 正确的进程数目依赖于Cpu的核心数目</span></span><br><span class="line">$ pm2 start app.js --name my-api <span class="comment"># 命名进程</span></span><br><span class="line">$ pm2 list               <span class="comment"># 显示所有进程状态</span></span><br><span class="line">$ pm2 monit              <span class="comment"># 监视所有进程</span></span><br><span class="line">$ pm2 logs               <span class="comment">#  显示所有进程日志</span></span><br><span class="line">$ pm2 stop all           <span class="comment"># 停止所有进程</span></span><br><span class="line">$ pm2 restart all        <span class="comment"># 重启所有进程</span></span><br><span class="line">$ pm2 reload all         <span class="comment"># 0秒停机重载进程 (用于 NETWORKED 进程)</span></span><br><span class="line">$ pm2 stop 0             <span class="comment"># 停止指定的进程</span></span><br><span class="line">$ pm2 restart 0          <span class="comment"># 重启指定的进程</span></span><br><span class="line">$ pm2 startup            <span class="comment"># 产生 init 脚本 保持进程活着</span></span><br><span class="line">$ pm2 web                <span class="comment"># 运行健壮的 computer API endpoint (http://localhost:9615)</span></span><br><span class="line">$ pm2 delete 0           <span class="comment"># 杀死指定的进程</span></span><br><span class="line">$ pm2 delete all         <span class="comment"># 杀死全部进程</span></span><br><span class="line"></span><br><span class="line">运行进程的不同方式：</span><br><span class="line">$ pm2 start app.js -i max  <span class="comment"># 根据有效CPU数目启动最大进程数目</span></span><br><span class="line">$ pm2 start app.js -i 3      <span class="comment"># 启动3个进程</span></span><br><span class="line">$ pm2 start app.js -x        <span class="comment">#用fork模式启动 app.js 而不是使用 cluster</span></span><br><span class="line">$ pm2 start app.js -x -- -a 23   <span class="comment"># 用fork模式启动 app.js 并且传递参数 (-a 23)</span></span><br><span class="line">$ pm2 start app.js --name serverone  <span class="comment"># 启动一个进程并把它命名为 serverone</span></span><br><span class="line">$ pm2 stop serverone       <span class="comment"># 停止 serverone 进程</span></span><br><span class="line">$ pm2 start app.json        <span class="comment"># 启动进程, 在 app.json里设置选项</span></span><br><span class="line">$ pm2 start app.js -i max -- -a 23                   <span class="comment">#在--之后给 app.js 传递参数</span></span><br><span class="line">$ pm2 start app.js -i max -e err.log -o out.log  <span class="comment"># 启动 并 生成一个配置文件</span></span><br><span class="line">你也可以执行用其他语言编写的app  ( fork 模式):</span><br><span class="line">$ pm2 start my-bash-script.sh    -x --interpreter bash</span><br><span class="line">$ pm2 start my-python-script.py -x --interpreter python</span><br></pre></td></tr></table></figure></p><h3 id="forever"><a href="#forever" class="headerlink" title="forever"></a>forever</h3><p>官网：<a href="https://github.com/foreverjs/forever" target="_blank" rel="noopener">https://github.com/foreverjs/forever</a><br>从功能上来看，个人感觉不如PM2全面和完善，具体的网上有很多教程和资料，就不赘述了。</p><h2 id="调试分析"><a href="#调试分析" class="headerlink" title="调试分析"></a>调试分析</h2><h3 id="node-inspector"><a href="#node-inspector" class="headerlink" title="node-inspector"></a>node-inspector</h3><p>这个模块提供了基于V8分析器的调试接口，配合chrome浏览器，可以直观的浏览运行时内存。<br>用于分析业务中可能的缺陷很有帮助。<br>安装并运行node-inspector<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&gt; node --debug app.js</span><br><span class="line">&gt; node-inspector</span><br><span class="line">Node Inspector v0.12.8</span><br><span class="line">Visit http://127.0.0.1:8080/?port=5858 to start debugging.</span><br></pre></td></tr></table></figure></p><p>运行需要调试的项目，加“—-debug”参数</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">&gt; node —-debug app.js</span><br><span class="line">Debugger listening on [::]:5858</span><br></pre></td></tr></table></figure><p>chrome 打开  <a href="http://127.0.0.1:8080/?port=5858" target="_blank" rel="noopener">http://127.0.0.1:8080/?port=5858</a> 就可以看到调试界面。<br>点击“Profiles”，选择“Take Heap Snapshor” 点击 “Take Snapshot”就可以浏览运行时内存数据了。<br><img src="/img/node-take-snapshot.png" alt="Node.js Take Snapshot"></p><h3 id="node-memwatch"><a href="#node-memwatch" class="headerlink" title="node-memwatch"></a>node-memwatch</h3><h3 id="node-mtrace"><a href="#node-mtrace" class="headerlink" title="node-mtrace"></a>node-mtrace</h3><h3 id="node-heap-dump"><a href="#node-heap-dump" class="headerlink" title="node-heap-dump"></a>node-heap-dump</h3><p><strong> 注意：本站博文均系原创，欢迎转载，请注明出处和原网址 </strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;How-to-make-the-node-js-application-run-stably&quot;&gt;&lt;a href=&quot;#How-to-make-the-node-js-application-run-stably&quot; class=&quot;headerlink&quot; title=&quot;
      
    
    </summary>
    
    
      <category term="node.js" scheme="http://yuenshui.com/tags/node-js/"/>
    
  </entry>
  
  <entry>
    <title>最小化安装Centos7 编译安装Nginx PHP7 MySQL5.7 Redis3.2 swoole1.8</title>
    <link href="http://yuenshui.com/2016/06/27/Centos7-Nginx-PHP7-MySQL5-7-Redis3-2-swoole1-8/"/>
    <id>http://yuenshui.com/2016/06/27/Centos7-Nginx-PHP7-MySQL5-7-Redis3-2-swoole1-8/</id>
    <published>2016-06-26T16:00:00.000Z</published>
    <updated>2017-02-26T09:33:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>技术更新，好久没有更新开发环境了，根据官方文档和网友的分享吧工作环境编译了一遍，过程分享给大家。<br>centos 7最小化安装（CentOS-7-x86_64-Minimal-1511.iso）</p><p>yum search ifconfig 不然自己ip都看不到<br>yum install net-tools.x86_64 wget</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># cd /etc/yum.repos.d/</span></span><br><span class="line"><span class="comment"># cp CentOS-Base.repo CentOS-Base.repo.backup</span></span><br><span class="line"><span class="comment"># wget -O CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo</span></span><br></pre></td></tr></table></figure><h2 id="安装系统包"><a href="#安装系统包" class="headerlink" title="安装系统包"></a>安装系统包</h2><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># yum -y install wget python-lockfile lrzsz bison vim cmake unzip gcc gcc-c++ make automake autoconf curl curl-devel libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel e2fsprogs e2fsprogs-devel libidn libidn-devel openssl openssl-devel openldap openldap-devel openldap-clients openldap-servers libevent libevent-devel net-tools nss_ldap pcre-devel gd kernel kernel-headers perl php-common php-gd libtool* patch libmcrypt libmcrypt-devel</span></span><br></pre></td></tr></table></figure><p>如果没有mkdir命令，yum install python-lockfile<br>如果 libmcrypt libmcrypt-devel 没找到</p><p>下载libmcrypt(<a href="https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz/download" target="_blank" rel="noopener">https://sourceforge.net/projects/mcrypt/files/Libmcrypt/2.5.8/libmcrypt-2.5.8.tar.gz/download</a>)</p><p>下载mash(<a href="https://sourceforge.net/projects/mhash/" target="_blank" rel="noopener">https://sourceforge.net/projects/mhash/</a>)</p><p>下载mcrypt(<a href="https://sourceforge.net/projects/mcrypt/files/latest/download" target="_blank" rel="noopener">https://sourceforge.net/projects/mcrypt/files/latest/download</a>)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tar xzf libmcrypt-2.5.8.tar.gz</span></span><br><span class="line"><span class="comment"># cd libmcrypt-2.5.8</span></span><br><span class="line"><span class="comment"># ./configure</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># tar xzf mhash-0.9.9.9.tar.gz</span></span><br><span class="line"><span class="comment"># cd mhash-0.9.9.9</span></span><br><span class="line"><span class="comment"># ./configure</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># tar xzf mcrypt-2.6.8.tar.gz</span></span><br><span class="line"><span class="comment"># cd mcrypt-2.6.8</span></span><br><span class="line"><span class="comment"># LD_LIBRARY_PATH=/usr/local/lib ./configure</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br></pre></td></tr></table></figure><h2 id="安装Nginx"><a href="#安装Nginx" class="headerlink" title="安装Nginx"></a>安装Nginx</h2><p>官网下载最新版本(<a href="http://nginx.org/" target="_blank" rel="noopener">http://nginx.org/</a>)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tar xzf nginx-1.10.1.tar.gz</span></span><br><span class="line"><span class="comment"># cd nginx-1.10.1</span></span><br><span class="line"><span class="comment"># ./configure --prefix=/usr/local/nginx --user=www --group=www --with-http_stub_status_module --with-http_ssl_module --pid-path=/usr/local/nginx/nginx.pid --conf-path=/usr/local/nginx/nginx.conf</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br></pre></td></tr></table></figure><p>nginx启动的shell： /etc/rc.d/init.d/nginx<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#! /bin/bash</span></span><br><span class="line"><span class="comment"># chkconfig: 35 85 15</span></span><br><span class="line"><span class="comment"># description: Nginx is an HTTP(S) server, HTTP(S) reverse</span></span><br><span class="line"><span class="built_in">set</span> -e</span><br><span class="line">PATH=/usr/<span class="built_in">local</span>/sbin:/usr/<span class="built_in">local</span>/bin:/sbin:/bin:/usr/sbin:/usr/bin</span><br><span class="line">DESC=<span class="string">"nginx daemon"</span></span><br><span class="line">NAME=nginx</span><br><span class="line">DAEMON=/usr/<span class="built_in">local</span>/nginx/sbin/<span class="variable">$NAME</span></span><br><span class="line">SCRIPTNAME=/etc/init.d/<span class="variable">$NAME</span></span><br><span class="line"><span class="built_in">test</span> -x <span class="variable">$DAEMON</span> || <span class="built_in">exit</span> 0</span><br><span class="line"><span class="function"><span class="title">d_start</span></span>()&#123;</span><br><span class="line">    <span class="variable">$DAEMON</span> || <span class="built_in">echo</span> -n <span class="string">" already running"</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="title">d_stop</span></span>() &#123;</span><br><span class="line">    <span class="variable">$DAEMON</span> -s quit || <span class="built_in">echo</span> -n <span class="string">" not running"</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="title">d_reload</span></span>() &#123;</span><br><span class="line">    <span class="variable">$DAEMON</span> -s reload || <span class="built_in">echo</span> -n <span class="string">" counld not reload"</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$1</span>"</span> <span class="keyword">in</span></span><br><span class="line">start)</span><br><span class="line">    <span class="built_in">echo</span> -n <span class="string">"Starting <span class="variable">$DESC</span>:<span class="variable">$NAME</span>"</span></span><br><span class="line">    d_start</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"."</span></span><br><span class="line">;;</span><br><span class="line">stop)</span><br><span class="line">    <span class="built_in">echo</span> -n <span class="string">"Stopping <span class="variable">$DESC</span>:<span class="variable">$NAME</span>"</span></span><br><span class="line">    d_stop</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"."</span></span><br><span class="line">;;</span><br><span class="line">reload)</span><br><span class="line">    <span class="built_in">echo</span> -n <span class="string">"Reloading <span class="variable">$DESC</span> configuration..."</span></span><br><span class="line">    d_reload</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"reloaded."</span></span><br><span class="line">;;</span><br><span class="line">restart)</span><br><span class="line">    <span class="built_in">echo</span> -n <span class="string">"Restarting <span class="variable">$DESC</span>: <span class="variable">$NAME</span>"</span></span><br><span class="line">    d_stop</span><br><span class="line">    sleep 2</span><br><span class="line">    d_start</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"."</span></span><br><span class="line">;;</span><br><span class="line">*)</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"Usage: <span class="variable">$SCRIPTNAME</span> &#123;start|stop|restart|reload&#125;"</span> &gt;&amp;2</span><br><span class="line">    <span class="built_in">exit</span> 3</span><br><span class="line">;;</span><br><span class="line"><span class="keyword">esac</span></span><br><span class="line"><span class="built_in">exit</span> 0</span><br></pre></td></tr></table></figure></p><p>启动 nginx</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># service nginx start</span></span><br></pre></td></tr></table></figure><h2 id="编译PHP"><a href="#编译PHP" class="headerlink" title="编译PHP"></a>编译PHP</h2><p>php.net 官网下载PHP7(<a href="http://php.net/downloads.php" target="_blank" rel="noopener">http://php.net/downloads.php</a>)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tar xzf php-7.0.8.tar.gz</span></span><br><span class="line"><span class="comment"># cd php-7.0.8</span></span><br><span class="line"><span class="comment"># ./configure --prefix=/usr/local/php7 \</span></span><br><span class="line">--with-config-file-path=/usr/<span class="built_in">local</span>/php7/etc \</span><br><span class="line">--with-config-file-scan-dir=/usr/<span class="built_in">local</span>/php7/etc/php.d \</span><br><span class="line">--with-mcrypt=/usr/include \</span><br><span class="line">--<span class="built_in">enable</span>-mysqlnd \</span><br><span class="line">--with-mysqli \</span><br><span class="line">--with-pdo-mysql \</span><br><span class="line">--<span class="built_in">enable</span>-fpm \</span><br><span class="line">--with-fpm-user=www \</span><br><span class="line">--with-fpm-group=www \</span><br><span class="line">--with-gd \</span><br><span class="line">--with-iconv \</span><br><span class="line">--with-zlib \</span><br><span class="line">--<span class="built_in">enable</span>-xml \</span><br><span class="line">--<span class="built_in">enable</span>-shmop \</span><br><span class="line">--<span class="built_in">enable</span>-sysvsem \</span><br><span class="line">--<span class="built_in">enable</span>-inline-optimization \</span><br><span class="line">--<span class="built_in">enable</span>-mbregex \</span><br><span class="line">--<span class="built_in">enable</span>-mbstring \</span><br><span class="line">--<span class="built_in">enable</span>-ftp \</span><br><span class="line">--<span class="built_in">enable</span>-gd-native-ttf \</span><br><span class="line">--with-openssl \</span><br><span class="line">--<span class="built_in">enable</span>-pcntl \</span><br><span class="line">--<span class="built_in">enable</span>-sockets \</span><br><span class="line">--with-xmlrpc \</span><br><span class="line">--<span class="built_in">enable</span>-zip \</span><br><span class="line">--<span class="built_in">enable</span>-soap \</span><br><span class="line">--without-pear \</span><br><span class="line">--with-gettext \</span><br><span class="line">--<span class="built_in">enable</span>-session \</span><br><span class="line">--with-curl \</span><br><span class="line">--with-jpeg-dir \</span><br><span class="line">--with-freetype-dir \</span><br><span class="line">--<span class="built_in">enable</span>-opcache</span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment"># cp php.ini-production  /usr/local/php7/etc/php.ini</span></span><br><span class="line"><span class="comment"># cp sapi/fpm/init.d.php-fpm /etc/init.d/php-fpm</span></span><br><span class="line"><span class="comment"># chmod +x /etc/init.d/php-fpm</span></span><br><span class="line"><span class="comment"># chkconfig --add php-fpm</span></span><br><span class="line"><span class="comment"># chkconfig php-fpm on</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># cd /usr/local/php7/etc</span></span><br><span class="line"><span class="comment"># cp php-fpm.conf.default php-fpm.conf</span></span><br><span class="line"><span class="comment"># cp php-fpm.d/www.conf.default php-fpm.d/www.conf</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># service php-fpm start</span></span><br></pre></td></tr></table></figure><p>此时应该可以通过 验证PHP的安装信息了<br>修改nginx的php相关配置<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">        location ~ \.php &#123;</span><br><span class="line">            root           /media/psf/Home/Documents/website;</span><br><span class="line">               proxy_buffer_size  128k;</span><br><span class="line">               proxy_buffers   32 32k;</span><br><span class="line">               proxy_busy_buffers_size 128k;</span><br><span class="line">               fastcgi_pass    127.0.0.1:9000;</span><br><span class="line">               fastcgi_index   index.php;</span><br><span class="line">               fastcgi_param  SCRIPT_FILENAME   <span class="variable">$document_root</span><span class="variable">$fastcgi_script_name</span>;</span><br><span class="line">               fastcgi_param  PHP_VALUE        open_basedir=<span class="variable">$document_root</span>:/tmp/:/proc/;</span><br><span class="line">               include fastcgi_params;</span><br><span class="line">        &#125;</span><br></pre></td></tr></table></figure></p><h2 id="编译MySQL"><a href="#编译MySQL" class="headerlink" title="编译MySQL"></a>编译MySQL</h2><p>mysql 官网下载(<a href="http://dev.mysql.com/downloads/mysql/" target="_blank" rel="noopener">http://dev.mysql.com/downloads/mysql/</a>)<br>网页下面的 Select Platform 选择 Source Code，<br>下载网页下面的 mysql-5.7.13.tar.gz mysql-boost-5.7.13.tar.gz<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># groupadd mysql</span></span><br><span class="line"><span class="comment"># useradd -g mysql mysql</span></span><br><span class="line"><span class="comment"># usermod -s /bin/false mysql</span></span><br><span class="line"><span class="comment"># mkdir -p /data/mysql</span></span><br><span class="line"><span class="comment"># chown mysql:mysql /data/mysql</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># tar xzf mysql-boost-5.7.13.tar.gz</span></span><br><span class="line"><span class="comment"># tar xzf mysql-5.7.13.tar.gz</span></span><br><span class="line"><span class="comment"># cd mysql-5.7.13</span></span><br></pre></td></tr></table></figure></p><p>从MySQL 5.7.5开始Boost库是必需的<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># cmake . -DCMAKE_INSTALL_PREFIX=/usr/local/mysql \</span></span><br><span class="line">-DMYSQL_DATADIR=/data/mysql \</span><br><span class="line">-DWITH_BOOST=boost \</span><br><span class="line">-DSYSCONFDIR=/usr/<span class="built_in">local</span>/mysql/etc \</span><br><span class="line">-DWITH_INNOBASE_STORAGE_ENGINE=1 \</span><br><span class="line">-DWITH_PARTITION_STORAGE_ENGINE=1 \</span><br><span class="line">-DWITH_FEDERATED_STORAGE_ENGINE=1 \</span><br><span class="line">-DWITH_BLACKHOLE_STORAGE_ENGINE=1 \</span><br><span class="line">-DWITH_MYISAM_STORAGE_ENGINE=1 \</span><br><span class="line">-DENABLED_LOCAL_INFILE=1 \</span><br><span class="line">-DENABLE_DTRACE=0 \</span><br><span class="line">-DDEFAULT_CHARSET=utf8mb4 \</span><br><span class="line">-DDEFAULT_COLLATION=utf8mb4_general_ci \</span><br><span class="line">-DWITH_EMBEDDED_SERVER=1</span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment"># cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld</span></span><br><span class="line"><span class="comment"># chmod +x /etc/init.d/mysqld</span></span><br><span class="line"><span class="comment"># chkconfig --add mysqld</span></span><br><span class="line"><span class="comment"># chkconfig mysqld on</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --basedir=/usr/local/mysql/ --datadir=/data/mysql/</span></span><br><span class="line"><span class="comment"># mkdir /var/run/mariadb</span></span><br><span class="line"><span class="comment"># chown mysql:mysql -R /var/run/mariadb</span></span><br><span class="line"><span class="comment"># mkdir /var/log/mariadb</span></span><br><span class="line"><span class="comment"># chown mysql:mysql -R /var/log/mariadb</span></span><br><span class="line"><span class="comment"># service mysqld start</span></span><br><span class="line"> </span><br><span class="line"><span class="comment"># /usr/local/mysql/bin/mysql -uroot -p</span></span><br><span class="line">mysql&gt; ALTER USER <span class="string">'root'</span>@<span class="string">'localhost'</span> IDENTIFIED BY <span class="string">'root'</span>;</span><br><span class="line">mysql&gt; flush privileges;</span><br><span class="line">mysql&gt; <span class="built_in">exit</span>;</span><br></pre></td></tr></table></figure></p><p>此时可以安装一个phpMyAdmin测试一下数据库了。</p><h2 id="安装redis"><a href="#安装redis" class="headerlink" title="安装redis"></a>安装redis</h2><p>官网：<a href="http://redis.io/" target="_blank" rel="noopener">http://redis.io/</a><br>下载 redis 目前最新版是：redis-3.2.1.tar.gz<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tar xzf redis-3.2.1.tar.gz</span></span><br><span class="line"><span class="comment"># cd redis-3.2.1</span></span><br><span class="line"><span class="comment"># make PREFIX=/usr/local/redis install</span></span><br><span class="line"><span class="comment"># mkdir /usr/local/redis/etc</span></span><br><span class="line"><span class="comment"># cp redis.conf /usr/local/redis/etc/6379.conf</span></span><br><span class="line"><span class="comment"># mkdir /data/redis</span></span><br></pre></td></tr></table></figure></p><p>修改配置文件/usr/local/redis/etc/6379.conf<br>dir 参数改为 /data/redis<br>daemonize 参数改为 yes</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># vim /etc/init.d/redis</span></span><br></pre></td></tr></table></figure><p>添加启动shell<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#!/bin/sh</span></span><br><span class="line"><span class="comment"># chkconfig: 2345 10 90</span></span><br><span class="line"><span class="comment"># description: Start and Stop redis</span></span><br><span class="line"> </span><br><span class="line">REDISPORT=6379</span><br><span class="line">EXEC=/usr/<span class="built_in">local</span>/redis/bin/redis-server</span><br><span class="line">REDIS_CLI=/usr/<span class="built_in">local</span>/redis/bin/redis-cli</span><br><span class="line"> </span><br><span class="line">PIDFILE=/data/redis/6379.pid</span><br><span class="line">CONF=<span class="string">"/usr/local/redis/etc/6379.conf"</span></span><br><span class="line"> </span><br><span class="line"><span class="keyword">case</span> <span class="string">"<span class="variable">$1</span>"</span> <span class="keyword">in</span></span><br><span class="line">    start)</span><br><span class="line">        <span class="keyword">if</span> [ -f <span class="variable">$PIDFILE</span> ]</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"<span class="variable">$PIDFILE</span> exists, process is already running or crashed."</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"Starting Redis server..."</span></span><br><span class="line">                <span class="variable">$EXEC</span> <span class="variable">$CONF</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">        <span class="keyword">if</span> [ <span class="string">"$?"</span>=<span class="string">"0"</span> ]</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"Redis is running..."</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">        ;;</span><br><span class="line">    stop)</span><br><span class="line">        <span class="keyword">if</span> [ ! -f <span class="variable">$PIDFILE</span> ]</span><br><span class="line">        <span class="keyword">then</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"<span class="variable">$PIDFILE</span> exists, process is not running."</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">                PID=$(cat <span class="variable">$PIDFILE</span>)</span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"Stopping..."</span></span><br><span class="line">               <span class="variable">$REDIS_CLI</span> -p <span class="variable">$REDISPORT</span>  SHUTDOWN</span><br><span class="line">                sleep 2</span><br><span class="line">               <span class="keyword">while</span> [ -x <span class="variable">$PIDFILE</span> ]</span><br><span class="line">               <span class="keyword">do</span></span><br><span class="line">                        <span class="built_in">echo</span> <span class="string">"Waiting for Redis to shutdown..."</span></span><br><span class="line">                       sleep 1</span><br><span class="line">                <span class="keyword">done</span></span><br><span class="line">                <span class="built_in">echo</span> <span class="string">"Redis stopped"</span></span><br><span class="line">        <span class="keyword">fi</span></span><br><span class="line">        ;;</span><br><span class="line">    restart|force-reload)</span><br><span class="line">        <span class="variable">$&#123;0&#125;</span> stop</span><br><span class="line">        <span class="variable">$&#123;0&#125;</span> start</span><br><span class="line">        ;;</span><br><span class="line">    *)</span><br><span class="line">    <span class="built_in">echo</span> <span class="string">"Usage: /etc/init.d/redis &#123;start|stop|restart|force-reload&#125;"</span> &gt;&amp;2</span><br><span class="line">    <span class="built_in">exit</span> 1</span><br><span class="line"><span class="keyword">esac</span></span><br></pre></td></tr></table></figure></p><p>启动redis服务<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># service redis start</span></span><br></pre></td></tr></table></figure></p><p>redis安装完成，如果需要多个redis服务，可以cp多个配置文件和多个redis服务器脚本</p><h2 id="安装redis-for-php模块"><a href="#安装redis-for-php模块" class="headerlink" title="安装redis for php模块"></a>安装redis for php模块</h2><p>官网下载(<a href="https://github.com/phpredis/phpredis" target="_blank" rel="noopener">https://github.com/phpredis/phpredis</a>)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">unzip phpredis-develop.zip</span><br><span class="line"><span class="built_in">cd</span> phpredis-develop</span><br><span class="line">/usr/<span class="built_in">local</span>/php7/bin/phpize</span><br><span class="line">./configure --with-php-config=/usr/<span class="built_in">local</span>/php7/bin/php-config</span><br><span class="line">make</span><br><span class="line">make install</span><br><span class="line">vim /usr/<span class="built_in">local</span>/php7/etc/php.ini +</span><br></pre></td></tr></table></figure><p>添加一行</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">extension=redis.so</span><br></pre></td></tr></table></figure><p>重启php-fpm<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service php-fpm restart</span><br></pre></td></tr></table></figure></p><p>此时查看phpinfo的页面，应该可以看到redis模块的信息</p><h2 id="安装swoole模块"><a href="#安装swoole模块" class="headerlink" title="安装swoole模块"></a>安装swoole模块</h2><p>官网下载(<a href="https://github.com/swoole/swoolw-src/releases" target="_blank" rel="noopener">https://github.com/swoole/swoolw-src/releases</a>)</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># mv 1.8.6-stable.tar.gz swoole-1.8.6-stable.tar.gz</span></span><br><span class="line"><span class="comment"># tar xzf swoole-1.8.6-stable.tar.gz</span></span><br><span class="line"><span class="comment"># cd swoole-src-1.8.6-stable/</span></span><br><span class="line"><span class="comment"># ./configure --with-php-config=/usr/local/php7/bin/php-config</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment"># vim /usr/local/php7/etc/php.ini +</span></span><br></pre></td></tr></table></figure><p>添加一行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">extension=swoole.so</span><br></pre></td></tr></table></figure></p><p>重启php-fpm<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">service php-fpm restart</span><br></pre></td></tr></table></figure></p><p>此时查看phpinfo的页面，应该可以看到swoole模块的信息</p><h2 id="安装-Node-js"><a href="#安装-Node-js" class="headerlink" title="安装 Node.js"></a>安装 Node.js</h2><p>官网下载(<a href="https://nodejs.org/en/download/" target="_blank" rel="noopener">https://nodejs.org/en/download/</a>)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># tar xzf node-v4.4.6.tar.gz</span></span><br><span class="line"><span class="comment"># cd node-v4.4.6</span></span><br><span class="line"><span class="comment"># ./configure --prefix=/usr/local/node</span></span><br><span class="line"><span class="comment"># make &amp;&amp; make install</span></span><br><span class="line"><span class="comment">#</span></span><br><span class="line"><span class="comment"># vim + /etc/profile</span></span><br></pre></td></tr></table></figure></p><p>最后增加一行<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">export</span> PATH=/usr/<span class="built_in">local</span>/mysql/bin:/usr/<span class="built_in">local</span>/php7/bin:/usr/<span class="built_in">local</span>/node/bin:<span class="variable">$PATH</span></span><br></pre></td></tr></table></figure></p><p>立即生效<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># source /etc/profile</span></span><br></pre></td></tr></table></figure></p><p>安装pm2<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># npm install -g pm2</span></span><br></pre></td></tr></table></figure></p><p><strong> 注意：本站博文均系原创，欢迎转载，请注明出处和原网址 </strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;技术更新，好久没有更新开发环境了，根据官方文档和网友的分享吧工作环境编译了一遍，过程分享给大家。&lt;br&gt;centos 7最小化安装（CentOS-7-x86_64-Minimal-1511.iso）&lt;/p&gt;
&lt;p&gt;yum search ifconfig 不然自己ip都看不到
      
    
    </summary>
    
    
      <category term="PHP" scheme="http://yuenshui.com/tags/PHP/"/>
    
      <category term="Centos7" scheme="http://yuenshui.com/tags/Centos7/"/>
    
      <category term="Nginx" scheme="http://yuenshui.com/tags/Nginx/"/>
    
      <category term="MySQL5.7" scheme="http://yuenshui.com/tags/MySQL5-7/"/>
    
      <category term="Redis3.2" scheme="http://yuenshui.com/tags/Redis3-2/"/>
    
      <category term="swoole1.8" scheme="http://yuenshui.com/tags/swoole1-8/"/>
    
      <category term="node.js" scheme="http://yuenshui.com/tags/node-js/"/>
    
  </entry>
  
  <entry>
    <title>PHP 实现文件下载的断点续传</title>
    <link href="http://yuenshui.com/2005/10/20/PHP-breakpoint-resume/"/>
    <id>http://yuenshui.com/2005/10/20/PHP-breakpoint-resume/</id>
    <published>2005-10-19T16:00:00.000Z</published>
    <updated>2017-02-26T09:32:59.000Z</updated>
    
    <content type="html"><![CDATA[<p>有些文件或数据的下载需要收到保护，会用动态语言控制下载的权限控制。下面是早期学习HTTP协议时写的一个PHP下载文件支持断点续传的例子：  </p><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?php</span></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 作者 于恩水&lt;yuenshui@126.com&gt;</span></span><br><span class="line"><span class="comment">* 支持断点续传下载</span></span><br><span class="line"><span class="comment">* 实例代码：</span></span><br><span class="line"><span class="comment">*          $down = new SD_DownLoad();</span></span><br><span class="line"><span class="comment">*          $down-&gt;Down('E:/iso/MS.Office2003SP1.CHS.iso');</span></span><br><span class="line"><span class="comment">**/</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SD_DownLoad</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 下载的开始点</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> integer</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">private</span> $mDownStart;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 文件大小</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> integer</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">private</span> $mFileSize;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 文件句柄</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> integer</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">private</span> $mFileHandle;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 文件全路径</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> string</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">private</span> $mFilePath;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 文件下载时显示的文件名</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@var</span> string</span></span><br><span class="line"><span class="comment">      */</span></span><br><span class="line">    <span class="keyword">private</span> $mFileName;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 构造函数</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> public</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">__construct</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 下载</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@param</span> string $pFilePath 文件全路径</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@param</span> string pFileName 文件下载时显示的文件名，缺省为实际文件名</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> public</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">Down</span><span class="params">($pFilePath, $pFileName = <span class="string">''</span>)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;mFilePath = $pFilePath;</span><br><span class="line">        <span class="keyword">if</span>(!<span class="keyword">$this</span>-&gt;IniFile()) <span class="keyword">$this</span>-&gt;SendError();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;mFileName = <span class="keyword">empty</span>($pFileName) ? <span class="keyword">$this</span>-&gt;GetFileName() : $pFileName;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">$this</span>-&gt;IniFile();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;SetStart();</span><br><span class="line">        <span class="keyword">$this</span>-&gt;SetHeader();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">$this</span>-&gt;Send();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 初始化文件信息</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> boolean</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">IniFile</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!is_file(<span class="keyword">$this</span>-&gt;mFilePath)) <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">$this</span>-&gt;mFileHandle = fopen(<span class="keyword">$this</span>-&gt;mFilePath, <span class="string">'rb'</span>);</span><br><span class="line">        <span class="keyword">$this</span>-&gt;mFileSize = filesize(<span class="keyword">$this</span>-&gt;mFilePath);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">    * 设置下载开始点</span></span><br><span class="line"><span class="comment">    * </span></span><br><span class="line"><span class="comment">    * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">    * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">    **/</span></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">SetStart</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (!<span class="keyword">empty</span>($_SERVER[<span class="string">'HTTP_RANGE'</span>]) &amp;&amp; preg_match(<span class="string">"/^bytes=([d]?)-([d]?)$/i"</span>, $_SERVER[<span class="string">'HTTP_RANGE'</span>], $match)) &#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="keyword">empty</span>($match[<span class="number">1</span>])) <span class="keyword">$this</span>-&gt;mDownStart = $match[<span class="number">1</span>];</span><br><span class="line">            fseek(<span class="keyword">$this</span>-&gt;mFileHandle, <span class="keyword">$this</span>-&gt;mDownStart);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">$this</span>-&gt;mDownStart = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 设置http头</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">SetHeader</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        @header(<span class="string">"Cache-control: public"</span>);</span><br><span class="line">        @header(<span class="string">"Pragma: public"</span>);</span><br><span class="line">        Header(<span class="string">"Content-Length: "</span> . (<span class="keyword">$this</span>-&gt;mFileSize - <span class="keyword">$this</span>-&gt;mDownStart));</span><br><span class="line">        <span class="keyword">if</span> (<span class="keyword">$this</span>-&gt;mDownStart &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            @Header(<span class="string">"HTTP/1.1 206 Partial Content"</span>);</span><br><span class="line">            Header(<span class="string">"Content-Ranges: bytes"</span> . <span class="keyword">$this</span>-&gt;mDownStart . <span class="string">"-"</span> . (<span class="keyword">$this</span>-&gt;mFileSize - <span class="number">1</span>) . <span class="string">"/"</span> . <span class="keyword">$this</span>-&gt;mFileSize);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            Header(<span class="string">"Accept-Ranges: bytes"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        @header(<span class="string">"Content-Type: application/octet-stream"</span>);</span><br><span class="line">        @header(<span class="string">"Content-Disposition: attachment;filename="</span> . <span class="keyword">$this</span>-&gt;mFileName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 获取全路径里的文件名部分</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> string</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">GetFileName</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> basename (<span class="keyword">$this</span>-&gt;mFilePath);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 发送数据</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> private</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">private</span> <span class="function"><span class="keyword">function</span> <span class="title">Send</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        fpassthru(<span class="keyword">$this</span>-&gt;mFileHandle);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">      * 发送错误</span></span><br><span class="line"><span class="comment">      * </span></span><br><span class="line"><span class="comment">      * <span class="doctag">@access</span> public</span></span><br><span class="line"><span class="comment">      * <span class="doctag">@return</span> void</span></span><br><span class="line"><span class="comment">      **/</span></span><br><span class="line">    <span class="keyword">public</span> <span class="function"><span class="keyword">function</span> <span class="title">SendError</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        @header(<span class="string">"HTTP/1.0 404 Not Found"</span>);</span><br><span class="line">        @header(<span class="string">"Status: 404 Not Found"</span>);</span><br><span class="line">        <span class="keyword">exit</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="meta">?&gt;</span></span><br></pre></td></tr></table></figure><p><strong> 注意：本站博文均系原创，欢迎转载，请注明出处和原网址 </strong></p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;p&gt;有些文件或数据的下载需要收到保护，会用动态语言控制下载的权限控制。下面是早期学习HTTP协议时写的一个PHP下载文件支持断点续传的例子：  &lt;/p&gt;
&lt;figure class=&quot;highlight php&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td class=&quot;gutter&quot;&gt;&lt;p
      
    
    </summary>
    
    
      <category term="PHP" scheme="http://yuenshui.com/tags/PHP/"/>
    
      <category term="断点续传" scheme="http://yuenshui.com/tags/%E6%96%AD%E7%82%B9%E7%BB%AD%E4%BC%A0/"/>
    
  </entry>
  
</feed>
