@@ -9,80 +9,119 @@ var assert = require('assert');
99
1010/*!
1111 * Get a near filter from a given where object. For connector use only.
12+ * NB: This only supports one near parameter per where object. eg, this:
13+ * {where: {or: [{location: {near: "29,-90"}},{location: {near: "50,-72"}}] }}
14+ * would throw an error.
1215 */
1316
1417exports . nearFilter = function nearFilter ( where ) {
15- var result = false ;
16-
17- if ( where && typeof where === 'object' ) {
18- Object . keys ( where ) . forEach ( function ( key ) {
19- var ex = where [ key ] ;
20-
21- if ( ex && ex . near ) {
22- result = {
23- near : ex . near ,
24- maxDistance : ex . maxDistance ,
25- unit : ex . unit ,
26- key : key ,
27- } ;
18+ function nearSearch ( clause , parentKeys ) {
19+ if ( typeof clause !== 'object' ) {
20+ return false ;
21+ }
22+ if ( ! parentKeys ) {
23+ parentKeys = [ ] ;
24+ }
25+
26+ Object . keys ( clause ) . forEach ( function ( clauseKey ) {
27+ if ( Array . isArray ( clause [ clauseKey ] ) ) {
28+ clause [ clauseKey ] . forEach ( function ( el ) {
29+ var ret = nearSearch ( el , parentKeys . concat ( clauseKey ) ) ;
30+ if ( ret ) return ret ;
31+ } ) ;
32+ } else {
33+ if ( clause [ clauseKey ] . hasOwnProperty ( 'near' ) ) {
34+ var result = clause [ clauseKey ] ;
35+ nearResults . push ( {
36+ near : result . near ,
37+ maxDistance : result . maxDistance ,
38+ unit : result . unit ,
39+ key : parentKeys . concat ( clauseKey ) ,
40+ } ) ;
41+ }
2842 }
2943 } ) ;
3044 }
45+ var nearResults = [ ] ;
46+ nearSearch ( where ) ;
3147
32- return result ;
48+ if ( nearResults . length === 0 ) {
49+ return false ;
50+ }
51+ return nearResults ;
3352} ;
3453
3554/*!
36- * Filter a set of objects using the given `nearFilter`.
55+ * Filter a set of objects using the given `nearFilter`. Can support multiple
56+ * locations, but will include results from all of them.
57+ *
58+ * WARNING: "or" operator with GeoPoint does not work as expected, eg:
59+ * {where: {or: [{location: {near: (29,-90)}},{name:'Sean'}] }}
60+ * Will actually work as if you had used "and". This is because geo filtering
61+ * takes place outside of the SQL query, so the result set of "name = Sean" is
62+ * returned by the database, and then the location filtering happens in the app
63+ * logic. So the "near" operator is always an "and" of the SQL filters, and "or"
64+ * of other GeoPoint filters.
65+ *
66+ * Additionally, since this step occurs after the SQL result set is returned,
67+ * if using GeoPoints with pagination the result set may be smaller than the
68+ * page size. The page size is enforced at the DB level, and then we may
69+ * remove results at the Geo-app level. If we "limit: 25", but 4 of those results
70+ * do not have a matching geopoint field, the request will only return 21 results.
71+ * This may make it erroneously look like a given page is the end of the result set.
3772 */
3873
3974exports . filter = function ( arr , filter ) {
40- var origin = filter . near ;
41- var max = filter . maxDistance > 0 ? filter . maxDistance : false ;
42- var unit = filter . unit ;
43- var key = filter . key ;
44-
45- // create distance index
4675 var distances = { } ;
4776 var result = [ ] ;
4877
49- arr . forEach ( function ( obj ) {
50- var loc = obj [ key ] ;
78+ filters . forEach ( function ( filter ) {
79+ var origin = filter . near ;
80+ var max = filter . maxDistance > 0 ? filter . maxDistance : false ;
81+ var unit = filter . unit ;
82+ var key = filter . key ;
5183
52- // filter out objects without locations
53- if ( ! loc ) return ;
84+ // create distance index
85+ arr . forEach ( function ( obj ) {
86+ var loc = obj [ key ] ;
5487
55- if ( ! ( loc instanceof GeoPoint ) ) {
56- loc = GeoPoint ( loc ) ;
57- }
88+ // filter out objects without locations
89+ if ( ! loc ) return ;
90+
91+ if ( ! ( loc instanceof GeoPoint ) ) {
92+ loc = GeoPoint ( loc ) ;
93+ }
5894
59- if ( typeof loc . lat !== 'number' ) return ;
60- if ( typeof loc . lng !== 'number' ) return ;
95+ if ( typeof loc . lat !== 'number' ) return ;
96+ if ( typeof loc . lng !== 'number' ) return ;
6197
62- var d = GeoPoint . distanceBetween ( origin , loc , { type : unit } ) ;
98+ var d = GeoPoint . distanceBetween ( origin , loc , { type : unit } ) ;
6399
64- if ( max && d > max ) {
65- // dont add
66- } else {
67- distances [ obj . id ] = d ;
68- result . push ( obj ) ;
69- }
70- } ) ;
100+ if ( max && d > max ) {
101+ // dont add
102+ } else {
103+ distances [ obj . id ] = d ;
104+ result . push ( obj ) ;
105+ }
106+ } ) ;
71107
72- return result . sort ( function ( objA , objB ) {
73- var a = objA [ key ] ;
74- var b = objB [ key ] ;
108+ result . sort ( function ( objA , objB ) {
109+ var a = objA [ key ] ;
110+ var b = objB [ key ] ;
75111
76- if ( a && b ) {
77- var da = distances [ objA . id ] ;
78- var db = distances [ objB . id ] ;
112+ if ( a && b ) {
113+ var da = distances [ objA . id ] ;
114+ var db = distances [ objB . id ] ;
79115
80- if ( db === da ) return 0 ;
81- return da > db ? 1 : - 1 ;
82- } else {
83- return 0 ;
84- }
116+ if ( db === da ) return 0 ;
117+ return da > db ? 1 : - 1 ;
118+ } else {
119+ return 0 ;
120+ }
121+ } ) ;
85122 } ) ;
123+
124+ return result ;
86125} ;
87126
88127exports . GeoPoint = GeoPoint ;
0 commit comments