|
| 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 | + |
| 13 | +defmodule OperatorTest do |
| 14 | + use CouchTestCase |
| 15 | + |
| 16 | + @db_name "operator" |
| 17 | + |
| 18 | + setup do |
| 19 | + UserDocs.setup(@db_name) |
| 20 | + end |
| 21 | + |
| 22 | + test "all" do |
| 23 | + q = %{"manager" => true, "favorites" => %{"$all" => ["Lisp", "Python"]}} |
| 24 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 25 | + |
| 26 | + assert length(docs) == 3 |
| 27 | + user_ids = Enum.map(docs, fn doc -> doc["user_id"] end) |
| 28 | + assert user_ids == [2, 12, 9] |
| 29 | + end |
| 30 | + |
| 31 | + test "all non array" do |
| 32 | + q = %{"manager" => true, "location" => %{"$all" => ["Ohai"]}} |
| 33 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 34 | + |
| 35 | + assert Enum.empty?(docs) |
| 36 | + end |
| 37 | + |
| 38 | + test "elem match" do |
| 39 | + emdocs = [ |
| 40 | + %{"user_id" => "a", "bang" => [%{"foo" => 1, "bar" => 2}]}, |
| 41 | + %{"user_id" => "b", "bang" => [%{"foo" => 2, "bam" => true}]}, |
| 42 | + ] |
| 43 | + MangoDatabase.save_docs(@db_name, emdocs) |
| 44 | + |
| 45 | + q = %{ |
| 46 | + "_id" => %{"$gt" => nil}, |
| 47 | + "bang" => %{"$elemMatch" => %{"foo": %{"$gte": 1}, "bam": true}} |
| 48 | + } |
| 49 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 50 | + |
| 51 | + assert length(docs) == 1 |
| 52 | + assert Enum.at(docs, 0)["user_id"] == "b" |
| 53 | + end |
| 54 | + |
| 55 | + test "all match" do |
| 56 | + amdocs = [ |
| 57 | + %{"user_id" => "a", "bang" => [%{"foo" => 1, "bar" => 2}, %{"foo" => 3, "bar" => 4}]}, |
| 58 | + %{"user_id" => "b", "bang" => [%{"foo" => 1, "bar" => 2}, %{"foo" => 4, "bar" => 4}]}, |
| 59 | + ] |
| 60 | + MangoDatabase.save_docs(@db_name, amdocs) |
| 61 | + |
| 62 | + q = %{ |
| 63 | + "bang" => %{"$allMatch" => %{"foo" => %{"$mod" => [2, 1]}, "bar" => %{"$mod" => [2, 0]}}} |
| 64 | + } |
| 65 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 66 | + |
| 67 | + assert length(docs) == 1 |
| 68 | + assert Enum.at(docs, 0)["user_id"] == "a" |
| 69 | + end |
| 70 | + |
| 71 | + test "empty all match" do |
| 72 | + amdocs = [ |
| 73 | + %{"bad_doc" => "a", "emptybang" => []}, |
| 74 | + ] |
| 75 | + MangoDatabase.save_docs(@db_name, amdocs) |
| 76 | + |
| 77 | + q = %{ |
| 78 | + "bang" => %{"emptybang" => %{"$allMatch" => %{"foo" => %{"$eq" => 2}}}} |
| 79 | + } |
| 80 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 81 | + |
| 82 | + assert Enum.empty?(docs) |
| 83 | + end |
| 84 | + |
| 85 | + test "in operator array" do |
| 86 | + q = %{ |
| 87 | + "manager" => true, "favorites" => %{"$in" => ["Ruby", "Python"]} |
| 88 | + } |
| 89 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 90 | + |
| 91 | + assert length(docs) == 6 |
| 92 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 93 | + assert user_ids == [2, 6, 7, 9, 11, 12] |
| 94 | + end |
| 95 | + |
| 96 | + test "nin operator array" do |
| 97 | + q = %{ |
| 98 | + "manager" => true, "favorites" => %{"$nin" => ["Erlang", "Python"]} |
| 99 | + } |
| 100 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 101 | + |
| 102 | + assert length(docs) == 4 |
| 103 | + for doc <- docs do |
| 104 | + if is_list(doc["favorites"]) do |
| 105 | + refute "Erlang" in doc["favorites"] |
| 106 | + refute "Python" in doc["favorites"] |
| 107 | + end |
| 108 | + end |
| 109 | + end |
| 110 | + |
| 111 | + test "regex" do |
| 112 | + q = %{ |
| 113 | + "age" => %{"$gt" => 40}, "location.state" => %{"$regex" => "(?i)new.*"} |
| 114 | + } |
| 115 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 116 | + |
| 117 | + assert length(docs) == 2 |
| 118 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 119 | + assert user_ids == [2, 10] |
| 120 | + end |
| 121 | + |
| 122 | + test "exists false" do |
| 123 | + q = %{ |
| 124 | + "age" => %{"$gt" => 0}, "twitter" => %{"$exists" => false} |
| 125 | + } |
| 126 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 127 | + |
| 128 | + assert length(docs) == 10 |
| 129 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 130 | + assert user_ids == [2, 3, 5, 6, 7, 8, 10, 11, 12, 14] |
| 131 | + |
| 132 | + for doc <- docs do |
| 133 | + refute "twitter" in doc |
| 134 | + end |
| 135 | + end |
| 136 | + |
| 137 | + test "eq null does not include missing" do |
| 138 | + q = %{"age" => %{"$gt" => 0}, "twitter" => nil} |
| 139 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 140 | + |
| 141 | + assert length(docs) == 1 |
| 142 | + assert Enum.at(docs, 0)["user_id"] == 9 |
| 143 | + assert Enum.at(docs, 0)["twitter"] == nil |
| 144 | + end |
| 145 | + |
| 146 | + test "ne includes null but not missing" do |
| 147 | + q = %{"twitter" => %{"$ne" => "notamatch"}} |
| 148 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 149 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 150 | + |
| 151 | + assert length(docs) == 5 |
| 152 | + assert user_ids == [0, 1, 4, 9, 13] |
| 153 | + |
| 154 | + for doc <- docs do |
| 155 | + assert Map.has_key?(doc, "twitter") |
| 156 | + end |
| 157 | + end |
| 158 | + |
| 159 | + test "lte includes null but not missing" do |
| 160 | + q = %{"twitter" => %{"$lte" => nil}} |
| 161 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 162 | + |
| 163 | + assert length(docs) == 1 |
| 164 | + assert Enum.at(docs, 0)["user_id"] == 9 |
| 165 | + assert Enum.at(docs, 0)["twitter"] == nil |
| 166 | + end |
| 167 | + |
| 168 | + test "lte at z except null excludes null and missing" do |
| 169 | + q = %{ |
| 170 | + "twitter" => %{ |
| 171 | + "$and" => [%{"$lte" => "@z"}, %{"$ne" => nil}] |
| 172 | + } |
| 173 | + } |
| 174 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 175 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 176 | + |
| 177 | + assert length(docs) == 4 |
| 178 | + assert user_ids == [0, 1, 4, 13] |
| 179 | + for doc <- docs do |
| 180 | + refute doc["twitter"] == nil |
| 181 | + end |
| 182 | + end |
| 183 | + |
| 184 | + test "range gte null includes null but not missing" do |
| 185 | + q = %{"twitter" => %{"$gte" => nil}} |
| 186 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 187 | + |
| 188 | + assert length(docs) == 5 |
| 189 | + for doc <- docs do |
| 190 | + assert Map.has_key?(doc, "twitter") |
| 191 | + end |
| 192 | + end |
| 193 | + |
| 194 | + test "exists false returns missing but not null" do |
| 195 | + q = %{"twitter" => %{"$exists" => false}} |
| 196 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 197 | + |
| 198 | + assert length(docs) == 10 |
| 199 | + for doc <- docs do |
| 200 | + refute Map.has_key?(doc, "twitter") |
| 201 | + end |
| 202 | + end |
| 203 | + |
| 204 | + test "beginsWith" do |
| 205 | + MangoDatabase.save_docs(@db_name, [ |
| 206 | + %{"user_id" => 99, "location" => %{"state" => ":Bar"}} |
| 207 | + ]) |
| 208 | + |
| 209 | + cases = [ |
| 210 | + %{prefix: "New", user_ids: [2, 10]}, |
| 211 | + # test characters that require escaping |
| 212 | + %{prefix: "New ", user_ids: [2, 10]}, |
| 213 | + %{prefix: ":", user_ids: [99]}, |
| 214 | + %{prefix: "Foo", user_ids: []}, |
| 215 | + %{prefix: "\"Foo", user_ids: []}, |
| 216 | + %{prefix: " New", user_ids: []} |
| 217 | + ] |
| 218 | + |
| 219 | + for case <- cases do |
| 220 | + selector = %{"location.state" => %{"$beginsWith" => case.prefix}} |
| 221 | + {:ok, docs} = MangoDatabase.find(@db_name, selector) |
| 222 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 223 | + |
| 224 | + assert length(docs) == length(case.user_ids) |
| 225 | + assert case.user_ids == user_ids |
| 226 | + end |
| 227 | + end |
| 228 | + |
| 229 | + # non-string prefixes should return an error |
| 230 | + test "beginsWith invalid prefix" do |
| 231 | + cases = [123, true, [], %{}] |
| 232 | + |
| 233 | + for prefix <- cases do |
| 234 | + q = %{"location.state" => %{"$beginsWith" => prefix}} |
| 235 | + {:error, response} = MangoDatabase.find(@db_name, q) |
| 236 | + assert response.status_code == 400 |
| 237 | + end |
| 238 | + end |
| 239 | +end |
| 240 | + |
| 241 | +defmodule OperatorJSONTests do |
| 242 | + use CouchTestCase |
| 243 | + |
| 244 | + @db_name "operator" |
| 245 | + |
| 246 | + setup do |
| 247 | + UserDocs.setup(@db_name) |
| 248 | + end |
| 249 | + |
| 250 | + # START: text indexes do not support range queries across type boundaries so only |
| 251 | + # test this for JSON indexes |
| 252 | + test "lt includes null but not missing" do |
| 253 | + q = %{"twitter" => %{"$lt" => 1}} |
| 254 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 255 | + |
| 256 | + assert length(docs) == 1 |
| 257 | + assert Enum.at(docs, 0)["user_id"] == 9 |
| 258 | + assert Enum.at(docs, 0)["twitter"] == nil |
| 259 | + end |
| 260 | + |
| 261 | + test "lte includes null but not missing" do |
| 262 | + q = %{"twitter" => %{"$lte" => 1}} |
| 263 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 264 | + |
| 265 | + assert length(docs) == 1 |
| 266 | + assert Enum.at(docs, 0)["user_id"] == 9 |
| 267 | + assert Enum.at(docs, 0)["twitter"] == nil |
| 268 | + end |
| 269 | + |
| 270 | + test "lte respects unicode collation" do |
| 271 | + q = %{"ordered" => %{"$lte" => "a"}} |
| 272 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 273 | + |
| 274 | + assert length(docs) == 6 |
| 275 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 276 | + assert user_ids == [7, 8, 9, 10, 11, 12] |
| 277 | + end |
| 278 | + |
| 279 | + test "gte respects unicode collation" do |
| 280 | + q = %{"ordered" => %{"$gte" => "a"}} |
| 281 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 282 | + |
| 283 | + assert length(docs) == 3 |
| 284 | + user_ids = Enum.sort(Enum.map(docs, fn doc -> doc["user_id"] end)) |
| 285 | + assert user_ids == [12, 13, 14] |
| 286 | + end |
| 287 | + |
| 288 | + # $keyMapMatch operator is only supported for JSON indexes |
| 289 | + test "keymap match" do |
| 290 | + amdocs = [ |
| 291 | + %{"foo" => %{"aa" => "bar", "bb" => "bang"}}, |
| 292 | + %{"foo" => %{"cc" => "bar", "bb" => "bang"}}, |
| 293 | + ] |
| 294 | + |
| 295 | + MangoDatabase.save_docs(@db_name, amdocs) |
| 296 | + |
| 297 | + q = %{"foo" => %{"$keyMapMatch" => %{"$eq" => "aa"}}} |
| 298 | + {:ok, docs} = MangoDatabase.find(@db_name, q) |
| 299 | + |
| 300 | + assert length(docs) == 1 |
| 301 | + end |
| 302 | +end |
| 303 | + |
| 304 | +defmodule OperatorAllDocsTests do |
| 305 | + use CouchTestCase |
| 306 | + |
| 307 | + @db_name "operator" |
| 308 | + |
| 309 | + setup do |
| 310 | + UserDocs.setup(@db_name, "special") |
| 311 | + end |
| 312 | + |
| 313 | + test "range id eq" do |
| 314 | + doc_id = "8e1c90c0-ac18-4832-8081-40d14325bde0" |
| 315 | + q = %{"_id" => doc_id} |
| 316 | + {:ok, explain} = MangoDatabase.find(@db_name, q, return_raw: true, explain: true) |
| 317 | + |
| 318 | + assert explain["mrargs"]["end_key"] == doc_id |
| 319 | + assert explain["mrargs"]["start_key"] == doc_id |
| 320 | + end |
| 321 | +end |
0 commit comments