UNPKG

elasticlunr

Version:

Lightweight full-text search engine in Javascript for browser search and offline search.

1,018 lines 50.1 kB
<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" content="width=device-width" charset="utf-8">
    <title>elasticlunr</title>
    <link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/bootstrap.css">
    <link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/bootstrap-responsive.css">
    <link rel="stylesheet" href="http://getbootstrap.com/2.3.2/assets/css/docs.css">
    <style>
      body > .navbar .brand {
        float:left;
        text-shadow: rgba(255, 255, 255, 0.0980392) 0px 1px 0px, rgba(255, 255, 255, 0.4) 0px 0px 30px;
        color: white;
        margin-left:0px;
        font-weight:normal;
      }
      
      .bs-docs-sidenav.affix {
        box-shadow: 0 0 20px 1px rgba(0, 0, 0, 0.5);
        z-index: 10;
      }
      
      .bs-docs-sidenav i{
        width: 8px;
        height: 8px;
        padding: 0px;
        margin: 0px;
        display: inline-block;
        margin-right:0.5em;
      }
      
      .bs-docs-sidenav > li > a {
          word-wrap: break-word;
      }
      
      .bs-docs-sidenav > li:first-child > a {
        border-top-right-radius: 6px;
        border-top-left-radius: 6px;
      }
      
      code[class*="language-"],pre[class*="language-"]{color:black;text-shadow:0 1px white;font-family:Consolas,Monaco,'Andale Mono',monospace;direction:ltr;text-align:left;white-space:pre;word-spacing:normal;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*="language-"],pre[class*="language-"]{background:#f5f2f0}:not(pre)>code[class*="language-"]{padding:.1em;border-radius:.3em}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:slategray}.token.punctuation{color:#999}.namespace{opacity:.7}.token.property,.token.tag,.token.boolean,.token.number{color:#905}.token.selector,.token.attr-name,.token.string{color:#690}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{color:#a67f59;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.regex,.token.important{color:#e90}.token.important{font-weight:bold}.token.entity{cursor:help}
      div.description {margin: 14px 0; padding-top: 14px; border-bottom:1px solid #eee; }
      .tags {}
      .ctx-type {
        display:inline-block;
        margin-right:0.5em;
        //- float:right; margin-top:8px
      }
      
      footer iframe{vertical-align:middle;}
      
    </style>
  </head>
  <body data-spy="scroll" data-target=".scrollspy">
    <div class="navbar navbar-inverse navbar-fixed-top">
      <div class="navbar-inner">
        <div class="container"><a class="brand">Doxx</a>
          <div class="nav-collapse collapse">
            <ul class="nav pull-right sponsored"></ul>
          </div>
        </div>
      </div>
    </div>
    <header id="overview" class="jumbotron subhead">
      <div class="container">
        <h1>elasticlunr</h1>
        <p class="lead"></p>
      </div>
    </header>
    <div class="container">
      <div class="row">
        <div class="span3 bs-docs-sidebar">
          <ul class="nav nav-list bs-docs-sidenav affix-top">
            <li><a href="index.html">Main</a></li>
            <li><a href="configuration.js.html">configuration.js</a></li>
            <li><a href="document_store.js.html">document_store.js</a></li>
            <li><a href="elasticlunr.js.html">elasticlunr.js</a></li>
            <li><a href="event_emitter.js.html">event_emitter.js</a></li>
            <li class="active"><a href="index.js.html">index.js</a></li>
            <li><a href="inverted_index.js.html">inverted_index.js</a></li>
            <li><a href="pipeline.js.html">pipeline.js</a></li>
            <li><a href="sorted_set.js.html">sorted_set.js</a></li>
            <li><a href="stemmer.js.html">stemmer.js</a></li>
            <li><a href="stop_word_filter.js.html">stop_word_filter.js</a></li>
            <li><a href="tokenizer.js.html">tokenizer.js</a></li>
            <li><a href="trimmer.js.html">trimmer.js</a></li>
            <li><a href="utils.js.html">utils.js</a></li>
          </ul>
          <div class="scrollspy">
            <ul class="nav nav-list bs-docs-sidenav affix-top">
              <li><a href="#Index"><i class="alert alert-success"></i><span>Index</span></a>
              </li>
              <li><a href="#on"><i class="alert alert-info"></i><span>on</span></a>
              </li>
              <li><a href="#off"><i class="alert alert-info"></i><span>off</span></a>
              </li>
              <li><a href="#load"><i class="alert alert-info"></i><span>load</span></a>
              </li>
              <li><a href="#addField"><i class="alert alert-info"></i><span>addField</span></a>
              </li>
              <li><a href="#setRef"><i class="alert alert-info"></i><span>setRef</span></a>
              </li>
              <li><a href="#saveDocument"><i class="alert alert-info"></i><span>saveDocument</span></a>
              </li>
              <li><a href="#addDoc"><i class="alert alert-info"></i><span>addDoc</span></a>
              </li>
              <li><a href="#removeDocByRef"><i class="alert alert-info"></i><span>removeDocByRef</span></a>
              </li>
              <li><a href="#removeDoc"><i class="alert alert-info"></i><span>removeDoc</span></a>
              </li>
              <li><a href="#updateDoc"><i class="alert alert-info"></i><span>updateDoc</span></a>
              </li>
              <li><a href="#getFields"><i class="alert alert-info"></i><span>getFields</span></a>
              </li>
              <li><a href="#search"><i class="alert alert-info"></i><span>search</span></a>
              </li>
              <li><a href="#fieldSearch"><i class="alert alert-info"></i><span>fieldSearch</span></a>
              </li>
              <li><a href="#mergeScores"><i class="alert alert-info"></i><span>mergeScores</span></a>
              </li>
              <li><a href="#fieldSearchStats"><i class="alert alert-info"></i><span>fieldSearchStats</span></a>
              </li>
              <li><a href="#coordNorm"><i class="alert alert-info"></i><span>coordNorm</span></a>
              </li>
              <li><a href="#toJSON"><i class="alert alert-info"></i><span>toJSON</span></a>
              </li>
              <li><a href="#use"><i class="alert alert-info"></i><span>use</span></a>
              </li>
            </ul>
          </div>
        </div>
        <div class="span9">
          <section id="Index">
            <h1>Index</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-success radius ctx-type">constructor</div><span>elasticlunr.Index()</span>
            </p>
          </section>
          <div class="description"><p>elasticlunr.Index is object that manages a search index.  It contains the indexes<br />and stores all the tokens and document lookups.  It also provides the main<br />user facing API for the library.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index = function () {
  this._fields = [];
  this._ref = 'id';
  this.pipeline = new elasticlunr.Pipeline;
  this.documentStore = new elasticlunr.DocumentStore;
  this.index = {};
  this.eventEmitter = new elasticlunr.EventEmitter;
  this._idfCache = {};

  this.on('add', 'remove', 'update', (function () {
    this._idfCache = {};
  }).bind(this));
};</code></pre>
          <section id="on">
            <h1>on</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.on()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>[eventName]</td>
                <td>String</td>
                <td><p>The name(s) of events to bind the function to.</p></td>
              </tr>
              <tr>
                <td>fn</td>
                <td>Function</td>
                <td><p>The serialised set to load.</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Bind a handler to events being emitted by the index.</p><p>The handler can be bound to many events at the same time.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.on = function () {
  var args = Array.prototype.slice.call(arguments);
  return this.eventEmitter.addListener.apply(this.eventEmitter, args);
};</code></pre>
          <section id="off">
            <h1>off</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.off()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>eventName</td>
                <td>String</td>
                <td><p>The name of events to remove the function from.</p></td>
              </tr>
              <tr>
                <td>fn</td>
                <td>Function</td>
                <td><p>The serialised set to load.</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Removes a handler from an event being emitted by the index.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.off = function (name, fn) {
  return this.eventEmitter.removeListener(name, fn);
};</code></pre>
          <section id="load">
            <h1>load</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.load()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>serialisedData</td>
                <td>Object</td>
                <td><p>The serialised set to load.</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>elasticlunr.Index</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Loads a previously serialised index.</p><p>Issues a warning if the index being imported was serialised<br />by a different version of elasticlunr.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.load = function (serialisedData) {
  if (serialisedData.version !== elasticlunr.version) {
    elasticlunr.utils.warn('version mismatch: current '
                    + elasticlunr.version + ' importing ' + serialisedData.version);
  }

  var idx = new this;

  idx._fields = serialisedData.fields;
  idx._ref = serialisedData.ref;
  idx.documentStore = elasticlunr.DocumentStore.load(serialisedData.documentStore);
  idx.pipeline = elasticlunr.Pipeline.load(serialisedData.pipeline);
  idx.index = {};
  for (var field in serialisedData.index) {
    idx.index[field] = elasticlunr.InvertedIndex.load(serialisedData.index[field]);
  }

  return idx;
};</code></pre>
          <section id="addField">
            <h1>addField</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.addField()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>fieldName</td>
                <td>String</td>
                <td><p>The name of the field within the document that should be indexed</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>elasticlunr.Index</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Adds a field to the list of fields that will be searchable within documents in the index.</p><p>Remember that inner index is build based on field, which means each field has one inverted index.</p><p>Fields should be added before any documents are added to the index, fields<br />that are added after documents are added to the index will only apply to new<br />documents added to the index.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.addField = function (fieldName) {
  this._fields.push(fieldName);
  this.index[fieldName] = new elasticlunr.InvertedIndex;
  return this;
};</code></pre>
          <section id="setRef">
            <h1>setRef</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.setRef()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>refName</td>
                <td>String</td>
                <td><p>The property to use to uniquely identify the documents in the index.</p></td>
              </tr>
              <tr>
                <td>emitEvent</td>
                <td>Boolean</td>
                <td><p>Whether to emit add events, defaults to true</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>elasticlunr.Index</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Sets the property used to uniquely identify documents added to the index,<br />by default this property is &#39;id&#39;.</p><p>This should only be changed before adding documents to the index, changing<br />the ref property without resetting the index can lead to unexpected results.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.setRef = function (refName) {
  this._ref = refName;
  return this;
};</code></pre>
          <section id="saveDocument">
            <h1>saveDocument</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.saveDocument()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>save</td>
                <td>Boolean</td>
                <td><p>Whether to save the original JSON documents.</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>elasticlunr.Index</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Set if the JSON format original documents are save into elasticlunr.DocumentStore</p><p>Defaultly save all the original JSON documents.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.saveDocument = function (save) {
  this.documentStore = new elasticlunr.DocumentStore(save);
  return this;
};</code></pre>
          <section id="addDoc">
            <h1>addDoc</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.addDoc()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>doc</td>
                <td>Object</td>
                <td><p>The JSON format document to add to the index.</p></td>
              </tr>
              <tr>
                <td>emitEvent</td>
                <td>Boolean</td>
                <td><p>Whether or not to emit events, default true.</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Add a JSON format document to the index.</p><p>This is the way new documents enter the index, this function will run the<br />fields from the document through the index&#39;s pipeline and then add it to<br />the index, it will then show up in search results.</p><p>An &#39;add&#39; event is emitted with the document that has been added and the index<br />the document has been added to. This event can be silenced by passing false<br />as the second argument to add.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.addDoc = function (doc, emitEvent) {
  if (!doc) return;
  var emitEvent = emitEvent === undefined ? true : emitEvent;

  var docRef = doc[this._ref];

  this.documentStore.addDoc(docRef, doc);
  this._fields.forEach(function (field) {
    var fieldTokens = this.pipeline.run(elasticlunr.tokenizer(doc[field]));
    this.documentStore.addFieldLength(docRef, field, fieldTokens.length);

    var tokenCount = {};
    fieldTokens.forEach(function (token) {
      if (token in tokenCount) tokenCount[token] += 1;
      else tokenCount[token] = 1;
    }, this);

    for (var token in tokenCount) {
      var termFrequency = tokenCount[token];
      termFrequency = Math.sqrt(termFrequency);
      this.index[field].addToken(token, { ref: docRef, tf: termFrequency });
    }
  }, this);

  if (emitEvent) this.eventEmitter.emit('add', doc, this);
};</code></pre>
          <section id="removeDocByRef">
            <h1>removeDocByRef</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.removeDocByRef()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>docRef</td>
                <td>String,Integer</td>
                <td><p>The document ref to remove from the index.</p></td>
              </tr>
              <tr>
                <td>emitEvent</td>
                <td>Boolean</td>
                <td><p>Whether to emit remove events, defaults to true</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Removes a document from the index by doc ref.</p><p>To make sure documents no longer show up in search results they can be<br />removed from the index using this method.</p><p>A &#39;remove&#39; event is emitted with the document that has been removed and the index<br />the document has been removed from. This event can be silenced by passing false<br />as the second argument to remove.</p><p>If user setting DocumentStore not storing the documents, then remove doc by docRef is not allowed.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.removeDocByRef = function (docRef, emitEvent) {
  if (!docRef) return;
  if (this.documentStore.isDocStored() === false) {
    return;
  }

  if (!this.documentStore.hasDoc(docRef)) return;
  var doc = this.documentStore.getDoc(docRef);
  this.removeDoc(doc, false);
};</code></pre>
          <section id="removeDoc">
            <h1>removeDoc</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.removeDoc()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>doc</td>
                <td>Object</td>
                <td><p>The document ref to remove from the index.</p></td>
              </tr>
              <tr>
                <td>emitEvent</td>
                <td>Boolean</td>
                <td><p>Whether to emit remove events, defaults to true</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Removes a document from the index.<br />This remove operation could work even the original doc is not store in the DocumentStore.</p><p>To make sure documents no longer show up in search results they can be<br />removed from the index using this method.</p><p>A &#39;remove&#39; event is emitted with the document that has been removed and the index<br />the document has been removed from. This event can be silenced by passing false<br />as the second argument to remove.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.removeDoc = function (doc, emitEvent) {
  if (!doc) return;

  var emitEvent = emitEvent === undefined ? true : emitEvent;

  var docRef = doc[this._ref];
  if (!this.documentStore.hasDoc(docRef)) return;

  this.documentStore.removeDoc(docRef);

  this._fields.forEach(function (field) {
    var fieldTokens = this.pipeline.run(elasticlunr.tokenizer(doc[field]));
    fieldTokens.forEach(function (token) {
      this.index[field].removeToken(token, docRef);
    }, this);
  }, this);

  if (emitEvent) this.eventEmitter.emit('remove', doc, this);
};</code></pre>
          <section id="updateDoc">
            <h1>updateDoc</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.updateDoc()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>doc</td>
                <td>Object</td>
                <td><p>The document to update in the index.</p></td>
              </tr>
              <tr>
                <td>emitEvent</td>
                <td>Boolean</td>
                <td><p>Whether to emit update events, defaults to true</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Updates a document in the index.</p><p>When a document contained within the index gets updated, fields changed,<br />added or removed, to make sure it correctly matched against search queries,<br />it should be updated in the index.</p><p>This method is just a wrapper around <code>remove</code> and <code>add</code></p><p>An &#39;update&#39; event is emitted with the document that has been updated and the index.<br />This event can be silenced by passing false as the second argument to update. Only<br />an update event will be fired, the &#39;add&#39; and &#39;remove&#39; events of the underlying calls<br />are silenced.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.updateDoc = function (doc, emitEvent) {
  var emitEvent = emitEvent === undefined ? true : emitEvent;

  this.removeDocByRef(doc[this._ref], false);
  this.addDoc(doc, false);

  if (emitEvent) this.eventEmitter.emit('update', doc, this);
};</code></pre>
          <section id="getFields">
            <h1>getFields</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.getFields()</span>
            </p>
          </section>
          <div class="description"><p>get fields of current index instance</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.getFields = function () {
  return this._fields.slice();
};</code></pre>
          <section id="search">
            <h1>search</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.search()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>query</td>
                <td>String</td>
                <td><p>The query to search the index with.</p></td>
              </tr>
              <tr>
                <td>userConfig</td>
                <td>JSON</td>
                <td><p>The user query config, JSON format.</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>Object</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Searches the index using the passed query.<br />Queries should be a string, multiple words are allowed.</p><p>If config is null, will search all fields defaultly, and lead to OR based query.<br />If config is specified, will search specified with query time boosting.</p><p>All query tokens are passed through the same pipeline that document tokens<br />are passed through, so any language processing involved will be run on every<br />query term.</p><p>Each query term is expanded, so that the term &#39;he&#39; might be expanded to<br />&#39;hello&#39; and &#39;help&#39; if those terms were already included in the index.</p><p>Matching documents are returned as an array of objects, each object contains<br />the matching document ref, as set for this index, and the similarity score<br />for this document against the query.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.search = function (query, userConfig) {
  if (!query) return [];

  var configStr = null;
  if (userConfig != null) {
    configStr = JSON.stringify(userConfig);
  }

  var config = new elasticlunr.Configuration(configStr, this.getFields()).get();

  var queryTokens = this.pipeline.run(elasticlunr.tokenizer(query));

  var queryResults = {};

  for (var field in config) {
    var fieldSearchResults = this.fieldSearch(queryTokens, field, config);
    var fieldBoost = config[field].boost;

    for (var docRef in fieldSearchResults) {
      fieldSearchResults[docRef] = fieldSearchResults[docRef] * fieldBoost;
    }

    for (var docRef in fieldSearchResults) {
      if (docRef in queryResults) {
        queryResults[docRef] += fieldSearchResults[docRef];
      } else {
        queryResults[docRef] = fieldSearchResults[docRef];
      }
    }
  }

  var results = [];
  for (var docRef in queryResults) {
    results.push({ref: docRef, score: queryResults[docRef]});
  }

  results.sort(function (a, b) { return b.score - a.score; });
  return results;
};</code></pre>
          <section id="fieldSearch">
            <h1>fieldSearch</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.fieldSearch()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>queryTokens</td>
                <td>Array</td>
                <td><p>The query tokens to query in this field.</p></td>
              </tr>
              <tr>
                <td>field</td>
                <td>String</td>
                <td><p>Field to query in.</p></td>
              </tr>
              <tr>
                <td>config</td>
                <td>elasticlunr.Configuration</td>
                <td><p>The user query config, JSON format.</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>Object</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>search queryTokens in specified field.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.fieldSearch = function (queryTokens, fieldName, config) {
  var booleanType = config[fieldName].bool;
  var expand = config[fieldName].expand;
  var boost = config[fieldName].boost;
  var scores = null;
  var docTokens = {};

  // Do nothing if the boost is 0
  if (boost === 0) {
    return;
  }

  queryTokens.forEach(function (token) {
    var tokens = [token];
    if (expand == true) {
      tokens = this.index[fieldName].expandToken(token);
    }
    // Consider every query token in turn. If expanded, each query token
    // corresponds to a set of tokens, which is all tokens in the 
    // index matching the pattern queryToken* .
    // For the set of tokens corresponding to a query token, find and score
    // all matching documents. Store those scores in queryTokenScores, 
    // keyed by docRef.
    // Then, depending on the value of booleanType, combine the scores
    // for this query token with previous scores.  If booleanType is OR,
    // then merge the scores by summing into the accumulated total, adding
    // new document scores are required (effectively a union operator). 
    // If booleanType is AND, accumulate scores only if the document 
    // has previously been scored by another query token (an intersection
    // operation0. 
    // Furthermore, since when booleanType is AND, additional 
    // query tokens can't add new documents to the result set, use the
    // current document set to limit the processing of each new query 
    // token for efficiency (i.e., incremental intersection).
    
    var queryTokenScores = {};
    tokens.forEach(function (key) {
      var docs = this.index[fieldName].getDocs(key);
      var idf = this.idf(key, fieldName);
      
      if (scores &amp;&amp; booleanType == 'AND') {
          // special case, we can rule out documents that have been
          // already been filtered out because they weren't scored
          // by previous query token passes.
          var filteredDocs = {};
          for (var docRef in scores) {
              if (docRef in docs) {
                  filteredDocs[docRef] = docs[docRef];
              }
          }
          docs = filteredDocs;
      }
      // only record appeared token for retrieved documents for the
      // original token, not for expaned token.
      // beause for doing coordNorm for a retrieved document, coordNorm only care how many
      // query token appear in that document.
      // so expanded token should not be added into docTokens, if added, this will pollute the
      // coordNorm
      if (key == token) {
        this.fieldSearchStats(docTokens, key, docs);
      }

      for (var docRef in docs) {
        var tf = this.index[fieldName].getTermFrequency(key, docRef);
        var fieldLength = this.documentStore.getFieldLength(docRef, fieldName);
        var fieldLengthNorm = 1;
        if (fieldLength != 0) {
          fieldLengthNorm = 1 / Math.sqrt(fieldLength);
        }

        var penality = 1;
        if (key != token) {
          // currently I'm not sure if this penality is enough,
          // need to do verification
          penality = (1 - (key.length - token.length) / key.length) * 0.15;
        }

        var score = tf * idf * fieldLengthNorm * penality;

        if (docRef in queryTokenScores) {
          queryTokenScores[docRef] += score;
        } else {
          queryTokenScores[docRef] = score;
        }
      }
    }, this);
    
    scores = this.mergeScores(scores, queryTokenScores, booleanType);
  }, this);

  scores = this.coordNorm(scores, docTokens, queryTokens.length);
  return scores;
};</code></pre>
          <section id="mergeScores">
            <h1>mergeScores</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.mergeScores()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>bool</td>
                <td>Object</td>
                <td><p>accumulated scores. Should be null on first call.</p></td>
              </tr>
              <tr>
                <td>scores</td>
                <td>String</td>
                <td><p>new scores to merge into accumScores.</p></td>
              </tr>
              <tr>
                <td>op</td>
                <td>Object</td>
                <td><p>merge operation (should be &#39;AND&#39; or &#39;OR&#39;).</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Merge the scores from one set of tokens into an accumulated score table.<br />Exact operation depends on the op parameter. If op is &#39;AND&#39;, then only the<br />intersection of the two score lists is retained. Otherwise, the union of<br />the two score lists is returned. For internal use only.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.mergeScores = function (accumScores, scores, op) {
    if (!accumScores) {
        return scores; 
    }
    if (op == 'AND') {
        var intersection = {};
        for (var docRef in scores) {
            if (docRef in accumScores) {
                intersection[docRef] = accumScores[docRef] + scores[docRef];
            }
        }
        return intersection;
    } else {
        for (var docRef in scores) {
            if (docRef in accumScores) {
                accumScores[docRef] += scores[docRef];
            } else {
                accumScores[docRef] = scores[docRef];
            }
        }
        return accumScores;
    }
};</code></pre>
          <section id="fieldSearchStats">
            <h1>fieldSearchStats</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.fieldSearchStats()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>docTokens</td>
                <td>Object</td>
                <td><p>a data structure stores which token appears in the retrieved doc.</p></td>
              </tr>
              <tr>
                <td>token</td>
                <td>String</td>
                <td><p>query token</p></td>
              </tr>
              <tr>
                <td>docs</td>
                <td>Object</td>
                <td><p>the retrieved documents of the query token</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Record the occuring query token of retrieved doc specified by doc field.<br />Only for inner user.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.fieldSearchStats = function (docTokens, token, docs) {
  for (var doc in docs) {
    if (doc in docTokens) {
      docTokens[doc].push(token);
    } else {
      docTokens[doc] = [token];
    }
  }
};</code></pre>
          <section id="coordNorm">
            <h1>coordNorm</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.coordNorm()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>results</td>
                <td>Object</td>
                <td><p>first results</p></td>
              </tr>
              <tr>
                <td>docs</td>
                <td>Object</td>
                <td><p>field search results of a token</p></td>
              </tr>
              <tr>
                <td>n</td>
                <td>Integer</td>
                <td><p>query token number</p></td>
              </tr>
              <tr>
                <td>return</td>
                <td>Object</td>
                <td></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>coord norm the score of a doc.<br />if a doc contain more query tokens, then the score will larger than the doc<br />contains less query tokens.</p><p>only for inner use.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.coordNorm = function (scores, docTokens, n) {
  for (var doc in scores) {
    if (!(doc in docTokens)) continue;
    var tokens = docTokens[doc].length;
    scores[doc] = scores[doc] * tokens / n;
  }

  return scores;
};</code></pre>
          <section id="toJSON">
            <h1>toJSON</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.toJSON()</span>
            </p>
          </section>
          <div class="description"><p>Returns a representation of the index ready for serialisation.</p> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.toJSON = function () {
  var indexJson = {};
  this._fields.forEach(function (field) {
    indexJson[field] = this.index[field].toJSON();
  }, this);

  return {
    version: elasticlunr.version,
    fields: this._fields,
    ref: this._ref,
    documentStore: this.documentStore.toJSON(),
    index: indexJson,
    pipeline: this.pipeline.toJSON()
  };
};</code></pre>
          <section id="use">
            <h1>use</h1>
            <h5 class="subheader"></h5>
            <p>
              <div class="label label-info radius ctx-type">method</div><span>elasticlunr.Index.prototype.use()</span>
            </p>
          </section>
          <table class="table table-bordered table-striped">
            <thead>
              <tr>
                <th style="width:20%">Option name</th>
                <th style="width:20%">Type</th>
                <th>Description</th>
              </tr>
            </thead>
            <tbody>
              <tr>
                <td>plugin</td>
                <td>Function</td>
                <td><p>The plugin to apply.</p></td>
              </tr>
            </tbody>
          </table>
          <div class="description"><p>Applies a plugin to the current index.</p><p>A plugin is a function that is called with the index as its context.<br />Plugins can be used to customise or extend the behaviour the index<br />in some way. A plugin is just a function, that encapsulated the custom<br />behaviour that should be applied to the index.</p><p>The plugin function will be called with the index as its argument, additional<br />arguments can also be passed when calling use. The function will be called<br />with the index as its context.</p><p>Example:</p><pre><code>var myPlugin = function (idx, arg1, arg2) {
  // `this` is the index to be extended
  // apply any extensions etc here.
}

var idx = elasticlunr(function () {
  this.use(myPlugin, &#39;arg1&#39;, &#39;arg2&#39;)
})
</code></pre> </div>
          <pre><code class="language-javascript">elasticlunr.Index.prototype.use = function (plugin) {
  var args = Array.prototype.slice.call(arguments, 1);
  args.unshift(this);
  plugin.apply(this, args);
};</code></pre>
        </div>
      </div>
    </div>
    <footer class="footer">
      <div class="container">
        <p>Documentation generated with <a href="https://github.com/FGRibreau/doxx">Doxx </a> created by <a href="https://twitter.com/FGRibreau" data-show-count="false" class="twitter-follow-button">Francois-Guillaume Ribreau </a></p>
        <p>Doxx is sponsored by <a href="http://bringr.net/?btt" title="Outil d'analyse des réseaux sociaux" class="bringr">Bringr </a> and <a href="https://redsmin.com/?btt" title="Full Redis GUI" class="redsmin">Redsmin</a></p>
        <p>Theme borrowed from Twitter Bootstrap</p>
      </div>
    </footer>
    <script src="http://platform.twitter.com/widgets.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
    <script src="http://leaverou.github.com/prefixfree/prefixfree.js"></script>
    <script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap-transition.js"></script>
    <script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap-scrollspy.js"></script>
    <script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap-dropdown.js"></script>
    <script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap-collapse.js"></script>
    <script src="http://getbootstrap.com/2.3.2/assets/js/bootstrap-affix.js"></script>
    <script>
      /**
       * Prism: Lightweight, robust, elegant syntax highlighting
       * MIT license http://www.opensource.org/licenses/mit-license.php/
       * @author Lea Verou http://lea.verou.me
       */(function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},clone:function(e){var n=t.util.type(e);switch(n){case"Object":var r={};for(var i in e)e.hasOwnProperty(i)&&(r[i]=t.util.clone(e[i]));return r;case"Array":return e.slice()}return e}},languages:{extend:function(e,n){var r=t.util.clone(t.languages[e]);for(var i in n)r[i]=n[i];return r},insertBefore:function(e,n,r,i){i=i||t.languages;var s=i[e],o={};for(var u in s)if(s.hasOwnProperty(u)){if(u==n)for(var a in r)r.hasOwnProperty(a)&&(o[a]=r[a]);o[u]=s[u]}return i[e]=o},DFS:function(e,n){for(var r in e){n.call(e,r,e[r]);t.util.type(e)==="Object"&&t.languages.DFS(e[r],n)}}},highlightAll:function(e,n){var r=document.querySelectorAll('code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code');for(var i=0,s;s=r[i++];)t.highlightElement(s,e===!0,n)},highlightElement:function(r,i,s){var o,u,a=r;while(a&&!e.test(a.className))a=a.parentNode;if(a){o=(a.className.match(e)||[,""])[1];u=t.languages[o]}if(!u)return;r.className=r.className.replace(e,"").replace(/\s+/g," ")+" language-"+o;a=r.parentNode;/pre/i.test(a.nodeName)&&(a.className=a.className.replace(e,"").replace(/\s+/g," ")+" language-"+o);var f=r.textContent;if(!f)return;f=f.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/\u00a0/g," ");var l={element:r,language:o,grammar:u,code:f};t.hooks.run("before-highlight",l);if(i&&self.Worker){var c=new Worker(t.filename);c.onmessage=function(e){l.highlightedCode=n.stringify(JSON.parse(e.data));l.element.innerHTML=l.highlightedCode;s&&s.call(l.element);t.hooks.run("after-highlight",l)};c.postMessage(JSON.stringify({language:l.language,code:l.code}))}else{l.highlightedCode=t.highlight(l.code,l.grammar);l.element.innerHTML=l.highlightedCode;s&&s.call(r);t.hooks.run("after-highlight",l)}},highlight:function(e,r){return n.stringify(t.tokenize(e,r))},tokenize:function(e,n){var r=t.Token,i=[e],s=n.rest;if(s){for(var o in s)n[o]=s[o];delete n.rest}e:for(var o in n){if(!n.hasOwnProperty(o)||!n[o])continue;var u=n[o],a=u.inside,f=!!u.lookbehind||0;u=u.pattern||u;for(var l=0;l<i.length;l++){var c=i[l];if(i.length>e.length)break e;if(c instanceof r)continue;u.lastIndex=0;var h=u.exec(c);if(h){f&&(f=h[1].length);var p=h.index-1+f,h=h[0].slice(f),d=h.length,v=p+d,m=c.slice(0,p+1),g=c.slice(v+1),y=[l,1];m&&y.push(m);var b=new r(o,a?t.tokenize(h,a):h);y.push(b);g&&y.push(g);Array.prototype.splice.apply(i,y)}}}return i},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[];r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(!r||!r.length)return;for(var i=0,s;s=r[i++];)s(n)}}},n=t.Token=function(e,t){this.type=e;this.content=t};n.stringify=function(e){if(typeof e=="string")return e;if(Object.prototype.toString.call(e)=="[object Array]"){for(var r=0;r<e.length;r++)e[r]=n.stringify(e[r]);return e.join("")}var i={type:e.type,content:n.stringify(e.content),tag:"span",classes:["token",e.type],attributes:{}};i.type=="comment"&&(i.attributes.spellcheck="true");t.hooks.run("wrap",i);var s="";for(var o in i.attributes)s+=o+'="'+(i.attributes[o]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+s+">"+i.content+"</"+i.tag+">"};if(!self.document){self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.tokenize(i,t.languages[r])));self.close()},!1);return}var r=document.getElementsByTagName("script");r=r[r.length-1];if(r){t.filename=r.src;document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)}})();;
      Prism.languages.markup={comment:/&lt;!--[\w\W]*?--(&gt;|&gt;)/g,prolog:/&lt;\?.+?\?&gt;/,doctype:/&lt;!DOCTYPE.+?&gt;/,cdata:/&lt;!\[CDATA\[[\w\W]+?]]&gt;/i,tag:{pattern:/&lt;\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?&gt;/gi,inside:{tag:{pattern:/^&lt;\/?[\w:-]+/i,inside:{punctuation:/^&lt;\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|&gt;|"/g}},punctuation:/\/?&gt;/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/&amp;#?[\da-z]{1,8};/gi};Prism.hooks.add("wrap",function(e){e.type==="entity"&&(e.attributes.title=e.content.replace(/&amp;/,"&"))});;
      Prism.languages.css={comment:/\/\*[\w\W]*?\*\//g,atrule:/@[\w-]+?(\s+[^;{]+)?(?=\s*{|\s*;)/gi,url:/url\((["']?).*?\1\)/gi,selector:/[^\{\}\s][^\{\}]*(?=\s*\{)/g,property:/(\b|\B)[a-z-]+(?=\s*:)/ig,string:/("|')(\\?.)*?\1/g,important:/\B!important\b/gi,ignore:/&(lt|gt|amp);/gi,punctuation:/[\{\};:]/g};Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{style:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/style(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)style[\w\W]*?(>|&gt;)|(&lt;|<)\/style(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.css}}});;
      Prism.languages.clike={comment:{pattern:/(^|[^\\])(\/\*[\w\W]*?\*\/|\/\/.*?(\r?\n|$))/g,lookbehind:!0},string:/("|')(\\?.)*?\1/g,keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,number:/\b-?(0x)?\d*\.?[\da-f]+\b/g,operator:/[-+]{1,2}|!|=?&lt;|=?&gt;|={1,2}|(&amp;){1,2}|\|?\||\?|\*|\//g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};;
      Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|catch|finally|null|break|continue)\b/g,number:/\b(-?(0x)?\d*\.?[\da-f]+|NaN|-?Infinity)\b/g});Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}});Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)[\w\W]*?(&lt;|<)\/script(>|&gt;)/ig,inside:{tag:{pattern:/(&lt;|<)script[\w\W]*?(>|&gt;)|(&lt;|<)\/script(>|&gt;)/ig,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});;
      
    </script>
    <!-- App js-->
    <script>
      $(function(){
        var $window = $(window);
        $('.scrollspy .nav').affix({
          offset: {
            top: function () { return $window.width() <= 980 ? 480 : 400 }
          , bottom: 50
          }
        });
      })
    </script>
  </body>
</html>