docs: function(entities, options) {
        if (entities == null) return;
  
        options = langx.mixin({}, setOptions, options);
        if (options.parse && !this._isEntity(entities)) {
          entities = this.parse(entities, options) || [];
        }
  
        var singular = !langx.isArray(entities);
        entities = singular ? [entities] : entities.slice();
  
        var at = options.at;
        if (at != null) at = +at;
        if (at > this.length) at = this.length;
        if (at < 0) at += this.length + 1;
  
        var set = [];
        var toAdd = [];
        var toMerge = [];
        var toRemove = [];
        var modelMap = {};
  
        var add = options.add;
        var merge = options.merge;
        var remove = options.remove;
  
        var sort = false;
        var sortable = this.comparator && at == null && options.sort !== false;
        var sortAttr = langx.isString(this.comparator) ? this.comparator : null;
  
        // Turn bare objects into entity references, and prevent invalid entities
        // from being added.
        var entity, i;
        for (i = 0; i < entities.length; i++) {
          entity = entities[i];
  
          // If a duplicate is found, prevent it from being added and
          // optionally merge it into the existing entity.
          var existing = this.get(entity);
          if (existing) {
            if (merge && entity !== existing) {
              var attrs = this._isEntity(entity) ? entity.attributes : entity;
              if (options.parse) attrs = existing.parse(attrs, options);
              existing.set(attrs, options);
              toMerge.push(existing);
              if (sortable && !sort) sort = existing.hasChanged(sortAttr);
            }
            if (!modelMap[existing.cid]) {
              modelMap[existing.cid] = true;
              set.push(existing);
            }
            entities[i] = existing;
  
          // If this is a new, valid entity, push it to the `toAdd` list.
          } else if (add) {
            entity = entities[i] = this._prepareEntity(entity, options);
            if (entity) {
              toAdd.push(entity);
              this._addReference(entity, options);
              modelMap[entity.cid] = true;
              set.push(entity);
            }
          }
        }
  
        // Remove stale entities.
        if (remove) {
          for (i = 0; i < this.length; i++) {
            entity = this.entities[i];
            if (!modelMap[entity.cid]) toRemove.push(entity);
          }
          if (toRemove.length) this._removeEntitys(toRemove, options);
        }
  
        // See if sorting is needed, update `length` and splice in new entities.
        var orderChanged = false;
        var replace = !sortable && add && remove;
        if (set.length && replace) {
          orderChanged = this.length !== set.length || this.entities.some(function(m, index) {
            return m !== set[index];
          });
          this.entities.length = 0;
          splice(this.entities, set, 0);
          this.length = this.entities.length;
        } else if (toAdd.length) {
          if (sortable) sort = true;
          splice(this.entities, toAdd, at == null ? this.length : at);
          this.length = this.entities.length;
        }
  
        // Silently sort the collection if appropriate.
        if (sort) this.sort({silent: true});
  
        // Unless silenced, it's time to fire all appropriate add/sort/update events.
        if (!options.silent) {
          for (i = 0; i < toAdd.length; i++) {
            if (at != null) options.index = at + i;
            entity = toAdd[i];
            entity.trigger('add', entity, this, options);
          }
          if (sort || orderChanged) this.trigger('sort', this, options);
          if (toAdd.length || toRemove.length || toMerge.length) {
            options.changes = {
              added: toAdd,
              removed: toRemove,
              merged: toMerge
            };
            this.trigger('update', this, options);
          }
        }
  
        // Return the added (or merged) entity (or entities).
        return singular ? entities[0] : entities;
      }