1# Licensed under the Apache License, Version 2.0 (the "License"); you may not 2# use this file except in compliance with the License. You may obtain a copy of 3# the License at 4# 5# http://www.apache.org/licenses/LICENSE-2.0 6# 7# Unless required by applicable law or agreed to in writing, software 8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10# License for the specific language governing permissions and limitations under 11# the License. 12 13import copy 14import mango 15import unittest 16 17DOCS = [ 18 {"_id": "100", "name": "Jimi", "location": "AUS", "user_id": 1, "same": "value"}, 19 {"_id": "200", "name": "Eddie", "location": "BRA", "user_id": 2, "same": "value"}, 20 {"_id": "300", "name": "Harry", "location": "CAN", "user_id": 3, "same": "value"}, 21 {"_id": "400", "name": "Eddie", "location": "DEN", "user_id": 4, "same": "value"}, 22 {"_id": "500", "name": "Jones", "location": "ETH", "user_id": 5, "same": "value"}, 23 { 24 "_id": "600", 25 "name": "Winnifried", 26 "location": "FRA", 27 "user_id": 6, 28 "same": "value", 29 }, 30 {"_id": "700", "name": "Marilyn", "location": "GHA", "user_id": 7, "same": "value"}, 31 {"_id": "800", "name": "Sandra", "location": "ZAR", "user_id": 8, "same": "value"}, 32] 33 34oldschoolnoselectorddoc = { 35 "_id": "_design/oldschoolnoselector", 36 "language": "query", 37 "views": { 38 "oldschoolnoselector": { 39 "map": {"fields": {"location": "asc"}}, 40 "reduce": "_count", 41 "options": {"def": {"fields": ["location"]}}, 42 } 43 }, 44} 45 46oldschoolddoc = { 47 "_id": "_design/oldschool", 48 "language": "query", 49 "views": { 50 "oldschool": { 51 "map": { 52 "fields": {"location": "asc"}, 53 "selector": {"location": {"$gte": "FRA"}}, 54 }, 55 "reduce": "_count", 56 "options": {"def": {"fields": ["location"]}}, 57 } 58 }, 59} 60 61oldschoolddoctext = { 62 "_id": "_design/oldschooltext", 63 "language": "query", 64 "indexes": { 65 "oldschooltext": { 66 "index": { 67 "default_analyzer": "keyword", 68 "default_field": {}, 69 "selector": {"location": {"$gte": "FRA"}}, 70 "fields": [{"name": "location", "type": "string"}], 71 "index_array_lengths": True, 72 }, 73 "analyzer": { 74 "name": "perfield", 75 "default": "keyword", 76 "fields": {"$default": "standard"}, 77 }, 78 } 79 }, 80} 81 82 83class IndexSelectorJson(mango.DbPerClass): 84 def setUp(self): 85 self.db.recreate() 86 self.db.save_docs(copy.deepcopy(DOCS)) 87 88 def test_saves_partial_filter_selector_in_index(self): 89 selector = {"location": {"$gte": "FRA"}} 90 self.db.create_index(["location"], partial_filter_selector=selector) 91 indexes = self.db.list_indexes() 92 self.assertEqual(indexes[1]["def"]["partial_filter_selector"], selector) 93 94 def test_partial_filter_only_in_return_if_not_default(self): 95 self.db.create_index(["location"]) 96 index = self.db.list_indexes()[1] 97 self.assertEqual("partial_filter_selector" in index["def"], False) 98 99 def test_saves_selector_in_index_throws(self): 100 selector = {"location": {"$gte": "FRA"}} 101 try: 102 self.db.create_index(["location"], selector=selector) 103 except Exception as e: 104 assert e.response.status_code == 400 105 else: 106 raise AssertionError("bad index creation") 107 108 def test_uses_partial_index_for_query_selector(self): 109 selector = {"location": {"$gte": "FRA"}} 110 self.db.create_index( 111 ["location"], 112 partial_filter_selector=selector, 113 ddoc="Selected", 114 name="Selected", 115 ) 116 resp = self.db.find(selector, explain=True, use_index="Selected") 117 self.assertEqual(resp["index"]["name"], "Selected") 118 docs = self.db.find(selector, use_index="Selected") 119 self.assertEqual(len(docs), 3) 120 121 def test_uses_partial_index_with_different_selector(self): 122 selector = {"location": {"$gte": "FRA"}} 123 selector2 = {"location": {"$gte": "A"}} 124 self.db.create_index( 125 ["location"], 126 partial_filter_selector=selector, 127 ddoc="Selected", 128 name="Selected", 129 ) 130 resp = self.db.find(selector2, explain=True, use_index="Selected") 131 self.assertEqual(resp["index"]["name"], "Selected") 132 docs = self.db.find(selector2, use_index="Selected") 133 self.assertEqual(len(docs), 3) 134 135 def test_doesnot_use_selector_when_not_specified(self): 136 selector = {"location": {"$gte": "FRA"}} 137 self.db.create_index( 138 ["location"], 139 partial_filter_selector=selector, 140 ddoc="Selected", 141 name="Selected", 142 ) 143 resp = self.db.find(selector, explain=True) 144 self.assertEqual(resp["index"]["name"], "_all_docs") 145 146 def test_doesnot_use_selector_when_not_specified_with_index(self): 147 selector = {"location": {"$gte": "FRA"}} 148 self.db.create_index( 149 ["location"], 150 partial_filter_selector=selector, 151 ddoc="Selected", 152 name="Selected", 153 ) 154 self.db.create_index(["location"], name="NotSelected") 155 resp = self.db.find(selector, explain=True) 156 self.assertEqual(resp["index"]["name"], "NotSelected") 157 158 def test_old_selector_with_no_selector_still_supported(self): 159 selector = {"location": {"$gte": "FRA"}} 160 self.db.save_doc(oldschoolnoselectorddoc) 161 resp = self.db.find(selector, explain=True, use_index="oldschoolnoselector") 162 self.assertEqual(resp["index"]["name"], "oldschoolnoselector") 163 docs = self.db.find(selector, use_index="oldschoolnoselector") 164 self.assertEqual(len(docs), 3) 165 166 def test_old_selector_still_supported(self): 167 selector = {"location": {"$gte": "FRA"}} 168 self.db.save_doc(oldschoolddoc) 169 resp = self.db.find(selector, explain=True, use_index="oldschool") 170 self.assertEqual(resp["index"]["name"], "oldschool") 171 docs = self.db.find(selector, use_index="oldschool") 172 self.assertEqual(len(docs), 3) 173 174 @unittest.skipUnless(mango.has_text_service(), "requires text service") 175 def test_text_saves_partialfilterselector_in_index(self): 176 selector = {"location": {"$gte": "FRA"}} 177 self.db.create_text_index( 178 fields=[{"name": "location", "type": "string"}], 179 partial_filter_selector=selector, 180 ) 181 indexes = self.db.list_indexes() 182 self.assertEqual(indexes[1]["def"]["partial_filter_selector"], selector) 183 184 @unittest.skipUnless(mango.has_text_service(), "requires text service") 185 def test_text_uses_partial_index_for_query_selector(self): 186 selector = {"location": {"$gte": "FRA"}} 187 self.db.create_text_index( 188 fields=[{"name": "location", "type": "string"}], 189 partial_filter_selector=selector, 190 ddoc="Selected", 191 name="Selected", 192 ) 193 resp = self.db.find(selector, explain=True, use_index="Selected") 194 self.assertEqual(resp["index"]["name"], "Selected") 195 docs = self.db.find(selector, use_index="Selected", fields=["_id", "location"]) 196 self.assertEqual(len(docs), 3) 197 198 @unittest.skipUnless(mango.has_text_service(), "requires text service") 199 def test_text_uses_partial_index_with_different_selector(self): 200 selector = {"location": {"$gte": "FRA"}} 201 selector2 = {"location": {"$gte": "A"}} 202 self.db.create_text_index( 203 fields=[{"name": "location", "type": "string"}], 204 partial_filter_selector=selector, 205 ddoc="Selected", 206 name="Selected", 207 ) 208 resp = self.db.find(selector2, explain=True, use_index="Selected") 209 self.assertEqual(resp["index"]["name"], "Selected") 210 docs = self.db.find(selector2, use_index="Selected") 211 self.assertEqual(len(docs), 3) 212 213 @unittest.skipUnless(mango.has_text_service(), "requires text service") 214 def test_text_doesnot_use_selector_when_not_specified(self): 215 selector = {"location": {"$gte": "FRA"}} 216 self.db.create_text_index( 217 fields=[{"name": "location", "type": "string"}], 218 partial_filter_selector=selector, 219 ddoc="Selected", 220 name="Selected", 221 ) 222 resp = self.db.find(selector, explain=True) 223 self.assertEqual(resp["index"]["name"], "_all_docs") 224 225 @unittest.skipUnless(mango.has_text_service(), "requires text service") 226 def test_text_doesnot_use_selector_when_not_specified_with_index(self): 227 selector = {"location": {"$gte": "FRA"}} 228 self.db.create_text_index( 229 fields=[{"name": "location", "type": "string"}], 230 partial_filter_selector=selector, 231 ddoc="Selected", 232 name="Selected", 233 ) 234 self.db.create_text_index( 235 fields=[{"name": "location", "type": "string"}], name="NotSelected" 236 ) 237 resp = self.db.find(selector, explain=True) 238 self.assertEqual(resp["index"]["name"], "NotSelected") 239 240 @unittest.skipUnless(mango.has_text_service(), "requires text service") 241 def test_text_old_selector_still_supported(self): 242 selector = {"location": {"$gte": "FRA"}} 243 self.db.save_doc(oldschoolddoctext) 244 resp = self.db.find(selector, explain=True, use_index="oldschooltext") 245 self.assertEqual(resp["index"]["name"], "oldschooltext") 246 docs = self.db.find(selector, use_index="oldschooltext") 247 self.assertEqual(len(docs), 3) 248 249 @unittest.skipUnless(mango.has_text_service(), "requires text service") 250 def test_text_old_selector_still_supported_via_api(self): 251 selector = {"location": {"$gte": "FRA"}} 252 self.db.create_text_index( 253 fields=[{"name": "location", "type": "string"}], 254 selector=selector, 255 ddoc="Selected", 256 name="Selected", 257 ) 258 docs = self.db.find({"location": {"$exists": True}}, use_index="Selected") 259 self.assertEqual(len(docs), 3) 260 261 @unittest.skipUnless(mango.has_text_service(), "requires text service") 262 def test_text_partial_filter_only_in_return_if_not_default(self): 263 self.db.create_text_index(fields=[{"name": "location", "type": "string"}]) 264 index = self.db.list_indexes()[1] 265 self.assertEqual("partial_filter_selector" in index["def"], False) 266