1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
|
/**
* Copyright (C) 2013 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 "mongo/db/index/s2_index_cursor.h"
#include "mongo/db/btreecursor.h"
#include "mongo/db/index_names.h"
#include "mongo/db/index/index_cursor.h"
#include "mongo/db/index/index_descriptor.h"
#include "mongo/db/index/s2_near_cursor.h"
#include "mongo/db/index/s2_simple_cursor.h"
#include "mongo/db/pdfile.h"
#include "mongo/db/queryutil.h"
#include "third_party/s2/s2regionintersection.h"
namespace mongo {
S2IndexCursor::S2IndexCursor(const S2IndexingParams& params, IndexDescriptor* descriptor)
: _params(params), _descriptor(descriptor) { }
Status S2IndexCursor::seek(const BSONObj &position) {
vector<GeoQuery> regions;
bool isNearQuery = false;
NearQuery nearQuery;
// Go through the fields that we index, and for each geo one, make
// a GeoQuery object for the S2*Cursor class to do intersection
// testing/cover generating with.
BSONObjIterator keyIt(_descriptor->keyPattern());
while (keyIt.more()) {
BSONElement keyElt = keyIt.next();
if (keyElt.type() != String || IndexNames::GEO_2DSPHERE != keyElt.valuestr()) {
continue;
}
BSONElement e = position.getFieldDotted(keyElt.fieldName());
if (e.eoo()) { continue; }
if (!e.isABSONObj()) { continue; }
BSONObj obj = e.Obj();
if (nearQuery.parseFrom(obj)) {
if (isNearQuery) {
return Status(ErrorCodes::BadValue, "Only one $near clause allowed: " +
position.toString(), 16685);
}
isNearQuery = true;
if (nearQuery.fromRadians) {
nearQuery.minDistance *= _params.radius;
nearQuery.maxDistance *= _params.radius;
}
nearQuery.field = keyElt.fieldName();
continue;
}
GeoQuery geoQueryField(keyElt.fieldName());
if (!geoQueryField.parseFrom(obj)) {
return Status(ErrorCodes::BadValue, "can't parse query (2dsphere): "
+ obj.toString(), 16535);
}
if (!geoQueryField.hasS2Region()) {
return Status(ErrorCodes::BadValue, "Geometry unsupported: " + obj.toString(),
16684);
}
regions.push_back(geoQueryField);
}
// Remove all the indexed geo regions from the query. The s2*cursor will
// instead create a covering for that key to speed up the search.
//
// One thing to note is that we create coverings for indexed geo keys during
// a near search to speed it up further.
BSONObjBuilder geoFieldsToNuke;
if (isNearQuery) {
geoFieldsToNuke.append(nearQuery.field, "");
}
for (size_t i = 0; i < regions.size(); ++i) {
geoFieldsToNuke.append(regions[i].getField(), "");
}
// false means we want to filter OUT geoFieldsToNuke, not filter to include only that.
BSONObj filteredQuery = position.filterFieldsUndotted(geoFieldsToNuke.obj(), false);
if (isNearQuery) {
S2NearIndexCursor* nearCursor = new S2NearIndexCursor(_descriptor, _params);
_underlyingCursor.reset(nearCursor);
nearCursor->seek(filteredQuery, nearQuery, regions);
} else {
S2SimpleCursor* simpleCursor = new S2SimpleCursor(_descriptor, _params);
_underlyingCursor.reset(simpleCursor);
simpleCursor->seek(filteredQuery, regions);
}
return Status::OK();
}
Status S2IndexCursor::setOptions(const CursorOptions& options) { return Status::OK(); }
bool S2IndexCursor::isEOF() const { return _underlyingCursor->isEOF(); }
BSONObj S2IndexCursor::getKey() const { return _underlyingCursor->getKey(); }
DiskLoc S2IndexCursor::getValue() const { return _underlyingCursor->getValue(); }
void S2IndexCursor::next() { _underlyingCursor->next(); }
string S2IndexCursor::toString() { return _underlyingCursor->toString(); }
Status S2IndexCursor::savePosition() { return _underlyingCursor->savePosition(); }
Status S2IndexCursor::restorePosition() { return _underlyingCursor->restorePosition(); }
} // namespace mongo
|