summaryrefslogtreecommitdiff
path: root/db/jsobj.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'db/jsobj.cpp')
-rw-r--r--db/jsobj.cpp199
1 files changed, 124 insertions, 75 deletions
diff --git a/db/jsobj.cpp b/db/jsobj.cpp
index 50c7e2ae720..423adb07131 100644
--- a/db/jsobj.cpp
+++ b/db/jsobj.cpp
@@ -1,5 +1,21 @@
// jsobj.cpp
+/**
+* Copyright (C) 2008 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 <http://www.gnu.org/licenses/>.
+*/
+
#include "stdafx.h"
#include "jsobj.h"
#include "../util/goodies.h"
@@ -95,6 +111,7 @@ public:
JSMatcher::~JSMatcher() {
for( int i = 0; i < nBuilders; i++ )
delete builders[i];
+ delete in;
delete where;
}
@@ -136,10 +153,18 @@ string Element::toString() {
s << '"' << valuestr() << '"';
}
break;
+ case DBRef:
+ s << fieldName();
+ s << " : DBRef('" << valuestr() << "',";
+ {
+ OID *x = (OID *) (valuestr() + valuestrsize());
+ s << hex << x->a << x->b << dec << ')';
+ }
+ break;
case jstOID:
- s << fieldName() << " : ObjId(";
- s << hex << oid().a << hex << oid().b << ')';
- break;
+ s << fieldName() << " : ObjId(";
+ s << hex << oid().a << oid().b << dec << ')';
+ break;
default:
s << fieldName() << ": ?type=" << type();
break;
@@ -147,7 +172,7 @@ string Element::toString() {
return s.str();
}
-int Element::size() {
+int Element::size() const {
if( totalSize >= 0 )
return totalSize;
@@ -194,7 +219,7 @@ int Element::size() {
cout << "Element: bad type " << (int) type() << endl;
assert(false);
}
- totalSize = x + fieldNameSize;
+ ((Element *) this)->totalSize = x + fieldNameSize;
if( !eoo() ) {
const char *next = data + totalSize;
@@ -214,7 +239,7 @@ int Element::size() {
}
/* must be same type! */
-inline int compareElementValues(Element& l, Element& r) {
+int compareElementValues(const Element& l, const Element& r) {
int f;
double x;
switch( l.type() ) {
@@ -289,15 +314,19 @@ int getGtLtOp(Element& e) {
Element fe = e.embeddedObject().firstElement();
const char *fn = fe.fieldName();
- if( fn[0] == '$' && fn[1] && fn[2] == 't' ) {
- if( fn[1] == 'g' ) {
- if( fn[3] == 0 ) op = JSMatcher::GT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = JSMatcher::GTE;
- }
- else if( fn[1] == 'l' ) {
- if( fn[3] == 0 ) op = JSMatcher::LT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = JSMatcher::LTE;
+ if( fn[0] == '$' && fn[1] ) {
+ if( fn[2] == 't' ) {
+ if( fn[1] == 'g' ) {
+ if( fn[3] == 0 ) op = JSMatcher::GT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = JSMatcher::GTE;
+ }
+ else if( fn[1] == 'l' ) {
+ if( fn[3] == 0 ) op = JSMatcher::LT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = JSMatcher::LTE;
+ }
}
+ else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 )
+ op = JSMatcher::opIN;
}
return op;
}
@@ -305,7 +334,7 @@ int getGtLtOp(Element& e) {
#include "pdfile.h"
JSMatcher::JSMatcher(JSObj &_jsobj) :
- where(0), jsobj(_jsobj), nRegex(0)
+ in(0), where(0), jsobj(_jsobj), nRegex(0)
{
nBuilders = 0;
@@ -353,33 +382,64 @@ JSMatcher::JSMatcher(JSObj &_jsobj) :
}
// greater than / less than...
- // { a : { $gt: 3 } }
+ // e.g., e == { a : { $gt : 3 } }
+ // or
+ // { a : { $in : [1,2,3] } }
if( e.type() == Object ) {
- Element fe = e.embeddedObject().firstElement();
- const char *fn = fe.fieldName();
- if( fn[0] == '$' && fn[1] && fn[2] == 't' ) {
- int op = Equality;
- if( fn[1] == 'g' ) {
- if( fn[3] == 0 ) op = GT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = GTE;
- }
- else if( fn[1] == 'l' ) {
- if( fn[3] == 0 ) op = LT;
- else if( fn[3] == 'e' && fn[4] == 0 ) op = LTE;
+ // e.g., fe == { $gt : 3 }
+ JSElemIter j(e.embeddedObject());
+ bool ok = false;
+ while( j.more() ) {
+ Element fe = j.next();
+ if( fe.eoo() )
+ break;
+ // Element fe = e.embeddedObject().firstElement();
+ const char *fn = fe.fieldName();
+ if( fn[0] == '$' && fn[1] ) {
+ if( fn[2] == 't' ) {
+ int op = Equality;
+ if( fn[1] == 'g' ) {
+ if( fn[3] == 0 ) op = GT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = GTE;
+ }
+ else if( fn[1] == 'l' ) {
+ if( fn[3] == 0 ) op = LT;
+ else if( fn[3] == 'e' && fn[4] == 0 ) op = LTE;
+ }
+ if( op && nBuilders < 8) {
+ JSObjBuilder *b = new JSObjBuilder();
+ builders[nBuilders++] = b;
+ b->appendAs(fe, e.fieldName());
+ toMatch.push_back( b->done().firstElement() );
+ compareOp.push_back(op);
+ n++;
+ ok = true;
+ }
+ }
+ else if( fn[1] == 'i' && fn[2] == 'n' && fn[3] == 0 && fe.type() == Array ) {
+ // $in
+ assert( in == 0 ); // only one per query supported so far. finish...
+ in = new set<Element,element_lt>();
+ JSElemIter i(fe.embeddedObject());
+ while( i.more() )
+ in->insert(i.next());
+ toMatch.push_back(e); // not actually used at the moment
+ compareOp.push_back(opIN);
+ n++;
+ ok = true;
+ }
}
- if( op && nBuilders < 8) {
- JSObjBuilder *b = new JSObjBuilder();
- builders[nBuilders++] = b;
- b->appendAs(fe, e.fieldName());
- toMatch.push_back( b->done().firstElement() );
- compareOp.push_back(op);
- n++;
- continue;
+ else {
+ ok = false;
+ break;
}
}
+ if( ok )
+ continue;
}
{
+ // normal, simple case e.g. { a : "foo" }
toMatch.push_back(e);
compareOp.push_back(Equality);
n++;
@@ -391,15 +451,35 @@ inline int JSMatcher::valuesMatch(Element& l, Element& r, int op) {
if( op == 0 )
return l.valuesEqual(r);
+ if( op == opIN ) {
+ // { $in : [1,2,3] }
+ return in->count(l);
+ }
+
+ /* check LT, GTE, ... */
if( l.type() != r.type() )
return false;
-
int c = compareElementValues(l, r);
int z = 1 << (c+1);
return (op & z);
}
-/* return value
+/* Check if a particular field matches.
+
+ fieldName - field to match "a.b" if we are reaching into an embedded object.
+ toMatch - element we want to match.
+ obj - database object to check against
+ compareOp - Equality, LT, GT, etc.
+ deep - out param. set to true/false if we scanned an array
+ isArr -
+
+ Special forms:
+
+ { "a.b" : 3 } means obj.a.b == 3
+ { a : { $lt : 3 } } means obj.a < 3
+ { a : { $in : [1,2] } } means [1,2].contains(obj.a)
+
+ return value
-1 mismatch
0 missing element
1 match
@@ -503,15 +583,6 @@ bool JSMatcher::matches(JSObj& jsobj, bool *deep) {
/* assuming there is usually only one thing to match. if more this
could be slow sometimes. */
- for( int r = 0; r < nRegex; r++ ) {
- RegexMatcher& rm = regexs[r];
- Element e = jsobj.getFieldDotted(rm.fieldName);
- if( e.eoo() )
- return false;
- if( !regexMatches(rm, e, deep) )
- return false;
- }
-
// check normal non-regex cases:
for( int i = 0; i < n; i++ ) {
Element& m = toMatch[i];
@@ -524,35 +595,14 @@ bool JSMatcher::matches(JSObj& jsobj, bool *deep) {
return false;
}
-/*
- Element e = jsobj.getFieldDotted(m.fieldName(), arrayElName);
- if( !e.eoo() ) {
- if( valuesMatch(e, m, compareOp[i]) ) {
- goto ok;
- }
- else if( e.type() == Array ) {
- JSElemIter ai(e.embeddedObject());
- while( ai.more() ) {
- Element z = ai.next();
- if( valuesMatch( z, m, compareOp[i]) ) {
- if( deep )
- *deep = true;
- goto ok;
- }
- }
- }
+ for( int r = 0; r < nRegex; r++ ) {
+ RegexMatcher& rm = regexs[r];
+ Element e = jsobj.getFieldDotted(rm.fieldName);
+ if( e.eoo() )
return false;
- }
-*/
-
- /* missing. that is ok iff we were looking for null */
-// if( m.type() == jstNULL || m.type() == Undefined )
-// ;
-////// else
-// return false;
-//ok:
-// ;
-// }
+ if( !regexMatches(rm, e, deep) )
+ return false;
+ }
if( where ) {
if( where->func == 0 )
@@ -829,4 +879,3 @@ struct RXTest {
assert( part.PartialMatch("dwight") );
}
} rxtest;
-