/**
* Copyright (c) 2011 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*/
#include "pch.h"
#include "db/jsobj.h"
#include "db/pipeline/document.h"
#include "db/pipeline/value.h"
#include "util/mongoutils/str.h"
namespace mongo {
using namespace mongoutils;
string Document::idName("_id");
intrusive_ptr Document::createFromBsonObj(BSONObj *pBsonObj) {
intrusive_ptr pDocument(new Document(pBsonObj));
return pDocument;
}
Document::Document(BSONObj *pBsonObj):
vFieldName(),
vpValue() {
BSONObjIterator bsonIterator(pBsonObj->begin());
while(bsonIterator.more()) {
BSONElement bsonElement(bsonIterator.next());
string fieldName(bsonElement.fieldName());
intrusive_ptr pValue(
Value::createFromBsonElement(&bsonElement));
vFieldName.push_back(fieldName);
vpValue.push_back(pValue);
}
}
void Document::toBson(BSONObjBuilder *pBuilder) {
const size_t n = vFieldName.size();
for(size_t i = 0; i < n; ++i)
vpValue[i]->addToBsonObj(pBuilder, vFieldName[i]);
}
intrusive_ptr Document::create(size_t sizeHint) {
intrusive_ptr pDocument(new Document(sizeHint));
return pDocument;
}
Document::Document(size_t sizeHint):
vFieldName(),
vpValue() {
if (sizeHint) {
vFieldName.reserve(sizeHint);
vpValue.reserve(sizeHint);
}
}
intrusive_ptr Document::clone() {
const size_t n = vFieldName.size();
intrusive_ptr pNew(Document::create(n));
for(size_t i = 0; i < n; ++i)
pNew->addField(vFieldName[i], vpValue[i]);
return pNew;
}
Document::~Document() {
}
FieldIterator *Document::createFieldIterator() {
return new FieldIterator(intrusive_ptr(this));
}
intrusive_ptr Document::getValue(const string &fieldName) {
/*
For now, assume the number of fields is small enough that iteration
is ok. Later, if this gets large, we can create a map into the
vector for these lookups.
Note that because of the schema-less nature of this data, we always
have to look, and can't assume that the requested field is always
in a particular place as we would with a statically compilable
reference.
*/
const size_t n = vFieldName.size();
for(size_t i = 0; i < n; ++i) {
if (fieldName.compare(vFieldName[i]) == 0)
return vpValue[i];
}
return(intrusive_ptr());
}
void Document::addField(const string &fieldName,
const intrusive_ptr &pValue) {
uassert(15945, str::stream() << "cannot add undefined field " <<
fieldName << " to document", pValue->getType() != Undefined);
vFieldName.push_back(fieldName);
vpValue.push_back(pValue);
}
void Document::setField(size_t index,
const string &fieldName,
const intrusive_ptr &pValue) {
/* special case: should this field be removed? */
if (!pValue.get()) {
vFieldName.erase(vFieldName.begin() + index);
vpValue.erase(vpValue.begin() + index);
return;
}
/* make sure we have a valid value */
uassert(15968, str::stream() << "cannot set undefined field " <<
fieldName << " to document", pValue->getType() != Undefined);
/* set the indicated field */
vFieldName[index] = fieldName;
vpValue[index] = pValue;
}
intrusive_ptr Document::getField(const string &fieldName) const {
const size_t n = vFieldName.size();
for(size_t i = 0; i < n; ++i) {
if (fieldName.compare(vFieldName[i]) == 0)
return vpValue[i];
}
/* if we got here, there's no such field */
return intrusive_ptr();
}
size_t Document::getApproximateSize() const {
size_t size = sizeof(Document);
const size_t n = vpValue.size();
for(size_t i = 0; i < n; ++i)
size += vpValue[i]->getApproximateSize();
return size;
}
size_t Document::getFieldIndex(const string &fieldName) const {
const size_t n = vFieldName.size();
size_t i = 0;
for(; i < n; ++i) {
if (fieldName.compare(vFieldName[i]) == 0)
break;
}
return i;
}
void Document::hash_combine(size_t &seed) const {
const size_t n = vFieldName.size();
for(size_t i = 0; i < n; ++i) {
boost::hash_combine(seed, vFieldName[i]);
vpValue[i]->hash_combine(seed);
}
}
int Document::compare(const intrusive_ptr &rL,
const intrusive_ptr &rR) {
const size_t lSize = rL->vFieldName.size();
const size_t rSize = rR->vFieldName.size();
for(size_t i = 0; true; ++i) {
if (i >= lSize) {
if (i >= rSize)
return 0; // documents are the same length
return -1; // left document is shorter
}
if (i >= rSize)
return 1; // right document is shorter
const int nameCmp = rL->vFieldName[i].compare(rR->vFieldName[i]);
if (nameCmp)
return nameCmp; // field names are unequal
const int valueCmp = Value::compare(rL->vpValue[i], rR->vpValue[i]);
if (valueCmp)
return valueCmp; // fields are unequal
}
/* NOTREACHED */
assert(false);
return 0;
}
/* ----------------------- FieldIterator ------------------------------- */
FieldIterator::FieldIterator(const intrusive_ptr &pTheDocument):
pDocument(pTheDocument),
index(0) {
}
bool FieldIterator::more() const {
return (index < pDocument->vFieldName.size());
}
pair > FieldIterator::next() {
assert(more());
pair > result(
pDocument->vFieldName[index], pDocument->vpValue[index]);
++index;
return result;
}
}