| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| |
| import json |
| import mango |
| import unittest |
| import user_docs |
| import math |
| from hypothesis import given, assume, example |
| import hypothesis.strategies as st |
| |
| |
| @unittest.skipIf(mango.has_text_service(), "text service exists") |
| class TextIndexCheckTests(mango.DbPerClass): |
| def test_create_text_index(self): |
| body = json.dumps({"index": {}, "type": "text"}) |
| resp = self.db.sess.post(self.db.path("_index"), data=body) |
| assert resp.status_code == 503, resp |
| |
| |
| @unittest.skipUnless(mango.has_text_service(), "requires text service") |
| class BasicTextTests(mango.UserDocsTextTests): |
| def test_simple(self): |
| docs = self.db.find({"$text": "Stephanie"}) |
| assert len(docs) == 1 |
| assert docs[0]["name"]["first"] == "Stephanie" |
| |
| def test_with_integer(self): |
| docs = self.db.find({"name.first": "Stephanie", "age": 48}) |
| assert len(docs) == 1 |
| assert docs[0]["name"]["first"] == "Stephanie" |
| assert docs[0]["age"] == 48 |
| |
| def test_with_boolean(self): |
| docs = self.db.find({"name.first": "Stephanie", "manager": False}) |
| assert len(docs) == 1 |
| assert docs[0]["name"]["first"] == "Stephanie" |
| assert docs[0]["manager"] == False |
| |
| def test_with_array(self): |
| faves = ["Ruby", "C", "Python"] |
| docs = self.db.find({"name.first": "Stephanie", "favorites": faves}) |
| assert docs[0]["name"]["first"] == "Stephanie" |
| assert docs[0]["favorites"] == faves |
| |
| def test_array_ref(self): |
| docs = self.db.find({"favorites.1": "Python"}) |
| assert len(docs) == 4 |
| for d in docs: |
| assert "Python" in d["favorites"] |
| |
| # Nested Level |
| docs = self.db.find({"favorites.0.2": "Python"}) |
| assert len(docs) == 1 |
| for d in docs: |
| assert "Python" in d["favorites"][0][2] |
| |
| def test_number_ref(self): |
| docs = self.db.find({"11111": "number_field"}) |
| assert len(docs) == 1 |
| assert docs[0]["11111"] == "number_field" |
| |
| docs = self.db.find({"22222.33333": "nested_number_field"}) |
| assert len(docs) == 1 |
| assert docs[0]["22222"]["33333"] == "nested_number_field" |
| |
| def test_lt(self): |
| docs = self.db.find({"age": {"$lt": 22}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"age": {"$lt": 23}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": {"$lt": 33}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (1, 9) |
| |
| docs = self.db.find({"age": {"$lt": 34}}) |
| assert len(docs) == 3 |
| for d in docs: |
| assert d["user_id"] in (1, 7, 9) |
| |
| docs = self.db.find({"company": {"$lt": "Dreamia"}}) |
| assert len(docs) == 1 |
| assert docs[0]["company"] == "Affluex" |
| |
| docs = self.db.find({"foo": {"$lt": "bar car apple"}}) |
| assert len(docs) == 0 |
| |
| def test_lte(self): |
| docs = self.db.find({"age": {"$lte": 21}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"age": {"$lte": 22}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": {"$lte": 33}}) |
| assert len(docs) == 3 |
| for d in docs: |
| assert d["user_id"] in (1, 7, 9) |
| |
| docs = self.db.find({"company": {"$lte": "Dreamia"}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (0, 11) |
| |
| docs = self.db.find({"foo": {"$lte": "bar car apple"}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 14 |
| |
| def test_eq(self): |
| docs = self.db.find({"age": 21}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"age": 22}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": {"$eq": 22}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": 33}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 7 |
| |
| def test_ne(self): |
| docs = self.db.find({"age": {"$ne": 22}}) |
| assert len(docs) == len(user_docs.DOCS) - 1 |
| for d in docs: |
| assert d["age"] != 22 |
| |
| docs = self.db.find({"$not": {"age": 22}}) |
| assert len(docs) == len(user_docs.DOCS) - 1 |
| for d in docs: |
| assert d["age"] != 22 |
| |
| def test_gt(self): |
| docs = self.db.find({"age": {"$gt": 77}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (3, 13) |
| |
| docs = self.db.find({"age": {"$gt": 78}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 3 |
| |
| docs = self.db.find({"age": {"$gt": 79}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"company": {"$gt": "Zialactic"}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"foo": {"$gt": "bar car apple"}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"foo": {"$gt": "bar car"}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 14 |
| |
| def test_gte(self): |
| docs = self.db.find({"age": {"$gte": 77}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (3, 13) |
| |
| docs = self.db.find({"age": {"$gte": 78}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (3, 13) |
| |
| docs = self.db.find({"age": {"$gte": 79}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 3 |
| |
| docs = self.db.find({"age": {"$gte": 80}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"company": {"$gte": "Zialactic"}}) |
| assert len(docs) == 1 |
| assert docs[0]["company"] == "Zialactic" |
| |
| docs = self.db.find({"foo": {"$gte": "bar car apple"}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 14 |
| |
| def test_and(self): |
| docs = self.db.find({"age": 22, "manager": True}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": 22, "manager": False}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"$and": [{"age": 22}, {"manager": True}]}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"$and": [{"age": 22}, {"manager": False}]}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"$text": "Ramona", "age": 22}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"$and": [{"$text": "Ramona"}, {"age": 22}]}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"$and": [{"$text": "Ramona"}, {"$text": "Floyd"}]}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| def test_or(self): |
| docs = self.db.find({"$or": [{"age": 22}, {"age": 33}]}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (7, 9) |
| |
| q = {"$or": [{"$text": "Ramona"}, {"$text": "Stephanie"}]} |
| docs = self.db.find(q) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (0, 9) |
| |
| q = {"$or": [{"$text": "Ramona"}, {"age": 22}]} |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| def test_and_or(self): |
| q = {"age": 22, "$or": [{"manager": False}, {"location.state": "Missouri"}]} |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| q = {"$or": [{"age": 22}, {"age": 43, "manager": True}]} |
| docs = self.db.find(q) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (9, 10) |
| |
| q = {"$or": [{"$text": "Ramona"}, {"age": 43, "manager": True}]} |
| docs = self.db.find(q) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (9, 10) |
| |
| def test_nor(self): |
| docs = self.db.find({"$nor": [{"age": 22}, {"age": 33}]}) |
| assert len(docs) == 13 |
| for d in docs: |
| assert d["user_id"] not in (7, 9) |
| |
| def test_in_with_value(self): |
| docs = self.db.find({"age": {"$in": [1, 5]}}) |
| assert len(docs) == 0 |
| |
| docs = self.db.find({"age": {"$in": [1, 5, 22]}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 9 |
| |
| docs = self.db.find({"age": {"$in": [1, 5, 22, 31]}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (1, 9) |
| |
| docs = self.db.find({"age": {"$in": [22, 31]}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (1, 9) |
| |
| # Limits on boolean clauses? |
| docs = self.db.find({"age": {"$in": list(range(1000))}}) |
| assert len(docs) == 15 |
| |
| def test_in_with_array(self): |
| vals = ["Random Garbage", 52, {"Versions": {"Alpha": "Beta"}}] |
| docs = self.db.find({"favorites": {"$in": vals}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 1 |
| |
| vals = ["Lisp", "Python"] |
| docs = self.db.find({"favorites": {"$in": vals}}) |
| assert len(docs) == 10 |
| |
| vals = [{"val1": 1, "val2": "val2"}] |
| docs = self.db.find({"test_in": {"$in": vals}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 2 |
| |
| def test_nin_with_value(self): |
| docs = self.db.find({"age": {"$nin": [1, 5]}}) |
| assert len(docs) == len(user_docs.DOCS) |
| |
| docs = self.db.find({"age": {"$nin": [1, 5, 22]}}) |
| assert len(docs) == len(user_docs.DOCS) - 1 |
| for d in docs: |
| assert d["user_id"] != 9 |
| |
| docs = self.db.find({"age": {"$nin": [1, 5, 22, 31]}}) |
| assert len(docs) == len(user_docs.DOCS) - 2 |
| for d in docs: |
| assert d["user_id"] not in (1, 9) |
| |
| docs = self.db.find({"age": {"$nin": [22, 31]}}) |
| assert len(docs) == len(user_docs.DOCS) - 2 |
| for d in docs: |
| assert d["user_id"] not in (1, 9) |
| |
| # Limits on boolean clauses? |
| docs = self.db.find({"age": {"$nin": list(range(1000))}}) |
| assert len(docs) == 0 |
| |
| def test_nin_with_array(self): |
| vals = ["Random Garbage", 52, {"Versions": {"Alpha": "Beta"}}] |
| docs = self.db.find({"favorites": {"$nin": vals}}) |
| assert len(docs) == len(user_docs.DOCS) - 1 |
| for d in docs: |
| assert d["user_id"] != 1 |
| |
| vals = ["Lisp", "Python"] |
| docs = self.db.find({"favorites": {"$nin": vals}}) |
| assert len(docs) == 5 |
| |
| vals = [{"val1": 1, "val2": "val2"}] |
| docs = self.db.find({"test_in": {"$nin": vals}}) |
| assert len(docs) == 0 |
| |
| def test_all(self): |
| vals = ["Ruby", "C", "Python", {"Versions": {"Alpha": "Beta"}}] |
| docs = self.db.find({"favorites": {"$all": vals}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 1 |
| |
| # This matches where favorites either contains |
| # the nested array, or is the nested array. This is |
| # notably different than the non-nested array in that |
| # it does not match a re-ordered version of the array. |
| # The fact that user_id 14 isn't included demonstrates |
| # this behavior. |
| vals = [["Lisp", "Erlang", "Python"]] |
| docs = self.db.find({"favorites": {"$all": vals}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (3, 9) |
| |
| def test_exists_field(self): |
| docs = self.db.find({"exists_field": {"$exists": True}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (7, 8) |
| |
| docs = self.db.find({"exists_field": {"$exists": False}}) |
| assert len(docs) == len(user_docs.DOCS) - 2 |
| for d in docs: |
| assert d["user_id"] not in (7, 8) |
| |
| def test_exists_array(self): |
| docs = self.db.find({"exists_array": {"$exists": True}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (9, 10) |
| |
| docs = self.db.find({"exists_array": {"$exists": False}}) |
| assert len(docs) == len(user_docs.DOCS) - 2 |
| for d in docs: |
| assert d["user_id"] not in (9, 10) |
| |
| def test_exists_object(self): |
| docs = self.db.find({"exists_object": {"$exists": True}}) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (11, 12) |
| |
| docs = self.db.find({"exists_object": {"$exists": False}}) |
| assert len(docs) == len(user_docs.DOCS) - 2 |
| for d in docs: |
| assert d["user_id"] not in (11, 12) |
| |
| def test_exists_object_member(self): |
| docs = self.db.find({"exists_object.should": {"$exists": True}}) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 11 |
| |
| docs = self.db.find({"exists_object.should": {"$exists": False}}) |
| assert len(docs) == len(user_docs.DOCS) - 1 |
| for d in docs: |
| assert d["user_id"] != 11 |
| |
| def test_exists_and(self): |
| q = { |
| "$and": [ |
| {"manager": {"$exists": True}}, |
| {"exists_object.should": {"$exists": True}}, |
| ] |
| } |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 11 |
| |
| q = { |
| "$and": [ |
| {"manager": {"$exists": False}}, |
| {"exists_object.should": {"$exists": True}}, |
| ] |
| } |
| docs = self.db.find(q) |
| assert len(docs) == 0 |
| |
| # Translates to manager exists or exists_object.should doesn't |
| # exist, which will match all docs |
| q = {"$not": q} |
| docs = self.db.find(q) |
| assert len(docs) == len(user_docs.DOCS) |
| |
| def test_value_chars(self): |
| q = {"complex_field_value": '+-(){}[]^~&&*||"\\/?:!'} |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| |
| def test_regex(self): |
| docs = self.db.find( |
| {"age": {"$gt": 40}, "location.state": {"$regex": "(?i)new.*"}} |
| ) |
| assert len(docs) == 2 |
| assert docs[0]["user_id"] == 2 |
| assert docs[1]["user_id"] == 10 |
| |
| # test lucene syntax in $text |
| |
| |
| @unittest.skipUnless(mango.has_text_service(), "requires text service") |
| class ElemMatchTests(mango.FriendDocsTextTests): |
| def test_elem_match_non_object(self): |
| q = {"bestfriends": {"$elemMatch": {"$eq": "Wolverine", "$eq": "Cyclops"}}} |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 1) |
| self.assertEqual(docs[0]["bestfriends"], ["Wolverine", "Cyclops"]) |
| |
| q = {"results": {"$elemMatch": {"$gte": 80, "$lt": 85}}} |
| |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 1) |
| self.assertEqual(docs[0]["results"], [82, 85, 88]) |
| |
| def test_elem_match(self): |
| q = {"friends": {"$elemMatch": {"name.first": "Vargas"}}} |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 2) |
| for d in docs: |
| self.assertIn(d["user_id"], (0, 1)) |
| |
| q = {"friends": {"$elemMatch": {"name.first": "Ochoa", "name.last": "Burch"}}} |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 1) |
| self.assertEqual(docs[0]["user_id"], 4) |
| |
| # Check that we can do logic in elemMatch |
| q = {"friends": {"$elemMatch": {"name.first": "Ochoa", "type": "work"}}} |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 2) |
| for d in docs: |
| self.assertIn(d["user_id"], (1, 15)) |
| |
| q = { |
| "friends": { |
| "$elemMatch": { |
| "name.first": "Ochoa", |
| "$or": [{"type": "work"}, {"type": "personal"}], |
| } |
| } |
| } |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 3) |
| for d in docs: |
| self.assertIn(d["user_id"], (1, 4, 15)) |
| |
| # Same as last, but using $in |
| q = { |
| "friends": { |
| "$elemMatch": { |
| "name.first": "Ochoa", |
| "type": {"$in": ["work", "personal"]}, |
| } |
| } |
| } |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 3) |
| for d in docs: |
| self.assertIn(d["user_id"], (1, 4, 15)) |
| |
| q = { |
| "$and": [ |
| {"friends": {"$elemMatch": {"id": 0, "name": {"$exists": True}}}}, |
| { |
| "friends": { |
| "$elemMatch": { |
| "$or": [ |
| {"name": {"first": "Campos", "last": "Freeman"}}, |
| { |
| "name": { |
| "$in": [ |
| {"first": "Gibbs", "last": "Mccarty"}, |
| {"first": "Wilkins", "last": "Chang"}, |
| ] |
| } |
| }, |
| ] |
| } |
| } |
| }, |
| ] |
| } |
| docs = self.db.find(q) |
| self.assertEqual(len(docs), 3) |
| for d in docs: |
| self.assertIn(d["user_id"], (10, 11, 12)) |
| |
| |
| @unittest.skipUnless(mango.has_text_service(), "requires text service") |
| class AllMatchTests(mango.FriendDocsTextTests): |
| def test_all_match(self): |
| q = {"friends": {"$allMatch": {"type": "personal"}}} |
| docs = self.db.find(q) |
| assert len(docs) == 2 |
| for d in docs: |
| assert d["user_id"] in (8, 5) |
| |
| # Check that we can do logic in allMatch |
| q = { |
| "friends": { |
| "$allMatch": { |
| "name.first": "Ochoa", |
| "$or": [{"type": "work"}, {"type": "personal"}], |
| } |
| } |
| } |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 15 |
| |
| # Same as last, but using $in |
| q = { |
| "friends": { |
| "$allMatch": { |
| "name.first": "Ochoa", |
| "type": {"$in": ["work", "personal"]}, |
| } |
| } |
| } |
| docs = self.db.find(q) |
| assert len(docs) == 1 |
| assert docs[0]["user_id"] == 15 |
| |
| |
| # Test numeric strings for $text |
| @unittest.skipUnless(mango.has_text_service(), "requires text service") |
| class NumStringTests(mango.DbPerClass): |
| @classmethod |
| def setUpClass(klass): |
| super(NumStringTests, klass).setUpClass() |
| klass.db.recreate() |
| if mango.has_text_service(): |
| klass.db.create_text_index() |
| |
| # not available for python 2.7.x |
| def isFinite(num): |
| not (math.isinf(num) or math.isnan(num)) |
| |
| @given(f=st.floats().filter(isFinite).map(str) | st.floats().map(lambda f: f.hex())) |
| @example("NaN") |
| @example("Infinity") |
| def test_floating_point_val(self, f): |
| doc = {"number_string": f} |
| self.db.save_doc(doc) |
| q = {"$text": f} |
| docs = self.db.find(q) |
| if len(docs) == 1: |
| assert docs[0]["number_string"] == f |
| if len(docs) == 2: |
| if docs[0]["number_string"] != f: |
| assert docs[1]["number_string"] == f |
| q = {"number_string": f} |
| docs = self.db.find(q) |
| if len(docs) == 1: |
| assert docs[0]["number_string"] == f |
| if len(docs) == 2: |
| if docs[0]["number_string"] != f: |
| assert docs[1]["number_string"] == f |