private BSONObject updateDocument(BSONObject document, BSONObject updateQuery, Integer matchPos)
      throws MongoServerException {
    synchronized (document) {
      // copy document
      BSONObject oldDocument = new BasicBSONObject();
      cloneInto(oldDocument, document);

      BSONObject newDocument = calculateUpdateDocument(document, updateQuery, matchPos, false);

      if (!newDocument.equals(oldDocument)) {
        for (Index index : indexes) {
          index.checkUpdate(oldDocument, newDocument);
        }
        for (Index index : indexes) {
          index.updateInPlace(oldDocument, newDocument);
        }

        long oldSize = Utils.calculateSize(oldDocument);
        long newSize = Utils.calculateSize(newDocument);
        dataSize.addAndGet(newSize - oldSize);

        // only keep fields that are also in the updated document
        Set<String> fields = new HashSet<String>(document.keySet());
        fields.removeAll(newDocument.keySet());
        for (String key : fields) {
          document.removeField(key);
        }

        // update the fields
        for (String key : newDocument.keySet()) {
          if (key.contains(".")) {
            throw new MongoServerException(
                "illegal field name. must not happen as it must be catched by the driver");
          }
          document.put(key, newDocument.get(key));
        }
      }
      return oldDocument;
    }
  }