private BSONObject projectDocument(BSONObject document, BSONObject fields) {
    if (document == null) return null;
    BSONObject newDocument = new BasicBSONObject();
    for (String key : fields.keySet()) {
      if (Utils.isTrue(fields.get(key))) {
        newDocument.put(key, document.get(key));
      }
    }

    // implicitly add _id if not mentioned
    // http://docs.mongodb.org/manual/core/read-operations/#result-projections
    if (!fields.containsField(idField)) {
      newDocument.put(idField, document.get(idField));
    }

    return newDocument;
  }
  public synchronized BSONObject findAndModify(BSONObject query) throws MongoServerException {

    boolean returnNew = Utils.isTrue(query.get("new"));

    if (!query.containsField("remove") && !query.containsField("update")) {
      throw new MongoServerException("need remove or update");
    }

    BSONObject queryObject = new BasicBSONObject();

    if (query.containsField("query")) {
      queryObject.put("query", query.get("query"));
    } else {
      queryObject.put("query", new BasicBSONObject());
    }

    if (query.containsField("sort")) {
      queryObject.put("orderby", query.get("sort"));
    }

    BSONObject lastErrorObject = null;
    BSONObject returnDocument = null;
    int num = 0;
    for (BSONObject document : handleQuery(queryObject, 0, 1)) {
      num++;
      if (Utils.isTrue(query.get("remove"))) {
        removeDocument(document);
        returnDocument = document;
      } else if (query.get("update") != null) {
        BSONObject updateQuery = (BSONObject) query.get("update");

        Integer matchPos = matcher.matchPosition(document, (BSONObject) queryObject.get("query"));

        BSONObject oldDocument = updateDocument(document, updateQuery, matchPos);
        if (returnNew) {
          returnDocument = document;
        } else {
          returnDocument = oldDocument;
        }
        lastErrorObject = new BasicBSONObject("updatedExisting", Boolean.TRUE);
        lastErrorObject.put("n", Integer.valueOf(1));
      }
    }
    if (num == 0 && Utils.isTrue(query.get("upsert"))) {
      BSONObject selector = (BSONObject) query.get("query");
      BSONObject updateQuery = (BSONObject) query.get("update");
      BSONObject newDocument = handleUpsert(updateQuery, selector);
      if (returnNew) {
        returnDocument = newDocument;
      } else {
        returnDocument = new BasicBSONObject();
      }
      num++;
    }

    if (query.get("fields") != null) {
      BSONObject fields = (BSONObject) query.get("fields");
      returnDocument = projectDocument(returnDocument, fields);
    }

    BSONObject result = new BasicBSONObject();
    if (lastErrorObject != null) {
      result.put("lastErrorObject", lastErrorObject);
    }
    result.put("value", returnDocument);
    Utils.markOkay(result);
    return result;
  }