diff --git a/src/mango/test/07-text-custom-field-list-test.py b/src/mango/test/07-text-custom-field-list-test.py deleted file mode 100644 index 36b23a7f6dd..00000000000 --- a/src/mango/test/07-text-custom-field-list-test.py +++ /dev/null @@ -1,207 +0,0 @@ -# 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 mango -import unittest -import user_docs - - -@unittest.skipUnless(mango.has_text_service(), "requires text service") -class CustomFieldsTest(mango.UserDocsTextTests): - FIELDS = [ - {"name": "favorites.[]", "type": "string"}, - {"name": "manager", "type": "boolean"}, - {"name": "age", "type": "number"}, - # These two are to test the default analyzer for - # each field. - {"name": "location.state", "type": "string"}, - {"name": "location.address.street", "type": "string"}, - {"name": "name\\.first", "type": "string"}, - ] - - def test_basic(self): - docs = self.db.find({"age": 22}) - assert len(docs) == 1 - assert docs[0]["user_id"] == 9 - - def test_multi_field(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 - - def test_element_acess(self): - docs = self.db.find({"favorites.0": "Ruby"}) - assert len(docs) == 3 - for d in docs: - assert "Ruby" in d["favorites"] - - # This should throw an exception because we only index the array - # favorites.[], and not the string field favorites - def test_index_selection(self): - try: - self.db.find( - {"selector": {"$or": [{"favorites": "Ruby"}, {"favorites.0": "Ruby"}]}} - ) - except Exception as e: - assert e.response.status_code == 400 - - def test_in_with_array(self): - vals = ["Lisp", "Python"] - docs = self.db.find({"favorites": {"$in": vals}}) - assert len(docs) == 10 - - def test_in_with_array_not_explicit(self): - agelist = [22, 51] - statelist = ["New Hampshire"] - docs = self.db.find({"age": {"$in": agelist}}) - docs2 = self.db.find({"location.state": {"$in": statelist}}) - docs3 = self.db.find({"age": {"$in": statelist}}) - assert len(docs) == 2 - assert len(docs2) == 1 - assert len(docs3) == 0 - - # This should also throw an error because we only indexed - # favorites.[] of type string. For the following query to work, the - # user has to index favorites.[] of type number, and also - # favorites.[].Versions.Alpha of type string. - def test_in_different_types(self): - vals = ["Random Garbage", 52, {"Versions": {"Alpha": "Beta"}}] - try: - self.db.find({"favorites": {"$in": vals}}) - except Exception as e: - assert e.response.status_code == 400 - - def test_nin_with_array(self): - vals = ["Lisp", "Python"] - docs = self.db.find({"favorites": {"$nin": vals}}) - assert len(docs) == 5 - - def test_missing(self): - self.db.find({"location.state": "Nevada"}) - - def test_missing_type(self): - # Raises an exception - try: - self.db.find({"age": "foo"}) - raise Exception("Should have thrown an HTTPError") - except: - return - - def test_field_analyzer_is_keyword(self): - docs = self.db.find({"location.state": "New"}) - assert len(docs) == 0 - - docs = self.db.find({"location.state": "New Hampshire"}) - assert len(docs) == 1 - assert docs[0]["user_id"] == 10 - - # Since our FIELDS list only includes "name\\.first", we should - # get an error when we try to search for "name.first", since the index - # for that field does not exist. - def test_escaped_field(self): - docs = self.db.find({"name\\.first": "name dot first"}) - assert len(docs) == 1 - assert docs[0]["name.first"] == "name dot first" - - try: - self.db.find({"name.first": "name dot first"}) - raise Exception("Should have thrown an HTTPError") - except: - return - - def test_filtered_search_fields(self): - docs = self.db.find({"age": 22}, fields=["age", "location.state"]) - assert len(docs) == 1 - assert docs == [{"age": 22, "location": {"state": "Missouri"}}] - - docs = self.db.find({"age": 22}, fields=["age", "Random Garbage"]) - assert len(docs) == 1 - assert docs == [{"age": 22}] - - docs = self.db.find({"age": 22}, fields=["favorites"]) - assert len(docs) == 1 - assert docs == [{"favorites": ["Lisp", "Erlang", "Python"]}] - - docs = self.db.find({"age": 22}, fields=["favorites.[]"]) - assert len(docs) == 1 - assert docs == [{}] - - docs = self.db.find({"age": 22}, fields=["all_fields"]) - assert len(docs) == 1 - assert docs == [{}] - - def test_two_or(self): - docs = self.db.find( - { - "$or": [ - {"location.state": "New Hampshire"}, - {"location.state": "Don't Exist"}, - ] - } - ) - assert len(docs) == 1 - assert docs[0]["user_id"] == 10 - - def test_all_match(self): - docs = self.db.find({"favorites": {"$allMatch": {"$eq": "Erlang"}}}) - assert len(docs) == 1 - assert docs[0]["user_id"] == 10 - - -@unittest.skipUnless(mango.has_text_service(), "requires text service") -class CustomFieldsExistsTest(mango.UserDocsTextTests): - FIELDS = [ - {"name": "exists_field", "type": "string"}, - {"name": "exists_array.[]", "type": "string"}, - {"name": "exists_object.should", "type": "string"}, - {"name": "twitter", "type": "string"}, - ] - - def test_exists_field(self): - docs = self.db.find({"exists_field": {"$exists": True}}) - self.assertEqual(len(docs), 2) - for d in docs: - self.assertIn(d["user_id"], (7, 8)) - - docs = self.db.find({"exists_field": {"$exists": False}}) - self.assertEqual(len(docs), len(user_docs.DOCS) - 2) - for d in docs: - self.assertNotIn(d["user_id"], (7, 8)) - - def test_exists_array(self): - docs = self.db.find({"exists_array": {"$exists": True}}) - self.assertEqual(len(docs), 2) - for d in docs: - self.assertIn(d["user_id"], (9, 10)) - - docs = self.db.find({"exists_array": {"$exists": False}}) - self.assertEqual(len(docs), len(user_docs.DOCS) - 2) - for d in docs: - self.assertNotIn(d["user_id"], (9, 10)) - - def test_exists_object_member(self): - docs = self.db.find({"exists_object.should": {"$exists": True}}) - self.assertEqual(len(docs), 1) - self.assertEqual(docs[0]["user_id"], 11) - - docs = self.db.find({"exists_object.should": {"$exists": False}}) - self.assertEqual(len(docs), len(user_docs.DOCS) - 1) - for d in docs: - self.assertNotEqual(d["user_id"], 11) - - def test_exists_false_same_as_views(self): - docs = self.db.find({"twitter": {"$exists": False}}) - for d in docs: - self.assertNotIn(d["user_id"], (0, 1, 4, 13)) diff --git a/test/elixir/test/config/search.elixir b/test/elixir/test/config/search.elixir index 87715d4caf3..bb443183242 100644 --- a/test/elixir/test/config/search.elixir +++ b/test/elixir/test/config/search.elixir @@ -36,6 +36,29 @@ "ElemMatchTests": [ "elem match non object" ], + "CustomFieldsTest": [ + "basic", + "multi field", + "element access", + "index selection", + "in with array", + "in with array not explicit", + "in different types", + "nin with array", + "missing", + "missing type", + "field analyzer is keyword", + "escaped field", + "filtered search fields", + "two or", + "all match", + ], + "CustomFieldsExistsTest": [ + "exists field", + "exists array", + "exists object member", + "exists false same as views" + ], "LimitTests": [ "limit field" ] diff --git a/test/elixir/test/mango/07_text_custom_field_list_test.exs b/test/elixir/test/mango/07_text_custom_field_list_test.exs new file mode 100644 index 00000000000..3c8956685ae --- /dev/null +++ b/test/elixir/test/mango/07_text_custom_field_list_test.exs @@ -0,0 +1,280 @@ +# 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. + +defmodule CustomFieldsTest do + use CouchTestCase + + @db_name "custom-fields-test" + + @fields [ + %{"name" => "favorites.[]", "type" => "string"}, + %{"name" => "manager", "type" => "boolean"}, + %{"name" => "age", "type" => "number"}, + # These two are to test the default analyzer for + # each field. + %{"name" => "location.state", "type" => "string"}, + %{"name" => "location.address.street", "type" => "string"}, + %{"name" => "name\\.first", "type" => "string"}, + ] + + setup do + UserDocs.setup(@db_name, "text", false, fields: @fields) + end + + test "basic" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}) + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 9 + end + + test "multi field" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22, "manager" => true}) + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 9 + + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22, "manager" => false}) + assert Enum.empty?(docs) + end + + test "element access" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"favorites.0" => "Ruby"}) + assert length(docs) == 3 + assert Enum.all?(Enum.map(docs, & &1["favorites"]), fn fav -> Enum.member?(fav, "Ruby") end) + end + + # TODO + # The Python test asserts an exception because we only index the array + # favorites.[], and not the string field favorites + # + # When mango was new, it failed if it didn’t find an index. + # shortly after mango was added to CouchDB, + # a mechanism was added to fall back to the `_all_docs` index if no other matching + # index could be found. The Python test was possibly written before the change + # happened and we cannot port the test as it is. + + # test "index selection" do + # selector = %{ + # "selector" => %{"$or" => [%{"favorites" => "Ruby"}, %{"favorites.0" => "Ruby"}]} + # } + # {:error, resp} = MangoDatabase.find(@db_name, selector) + # assert resp.status_code == 400 + # end + + test "in with array" do + vals = ["Lisp", "Python"] + {:ok, docs} = MangoDatabase.find(@db_name, %{"favorites" => %{"$in" => vals}}) + assert length(docs) == 10 + end + + test "in with array not explicit" do + agelist = [22, 51] + statelist = ["New Hampshire"] + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => %{"$in" => agelist}}) + {:ok, docs2} = MangoDatabase.find(@db_name, %{"location.state" => %{"$in" => statelist}}) + {:ok, docs3} = MangoDatabase.find(@db_name, %{"age" => %{"$in" => statelist}}) + assert length(docs) == 2 + assert length(docs2) == 1 + assert Enum.empty?(docs3) + end + + # TODO + # The Python test asserts an exception because we only indexed + # favorites.[] of type string. For the following query to work, the + # user has to index favorites.[] of type number, and also + # favorites.[].Versions.Alpha of type string. + # + # When mango was new, it failed if it didn’t find an index. + # shortly after mango was added to CouchDB, + # a mechanism was added to fall back to the `_all_docs` index if no other matching + # index could be found. The Python test was possibly written before the change + # happened and we cannot port the test as it is. + + # test "in different types" do + # vals = ["Random Garbage", 52, %{"Versions" => %{"Alpha" => "Beta"}}] + # {:error, resp} = MangoDatabase.find(@db_name, %{"favorites" => %{"$in" => vals}}) + # assert resp.status_code == 400 + # end + + test "nin with array" do + vals = ["Lisp", "Python"] + {:ok, docs} = MangoDatabase.find(@db_name, %{"favorites" => %{"$nin" => vals}}) + assert length(docs) == 5 + end + + test "missing" do + MangoDatabase.find(@db_name, %{"location.state" => "Nevada"}) + end + + # TODO + # The Python test asserts an exception. + # + # When mango was new, it failed if it didn’t find an index. + # shortly after mango was added to CouchDB, + # a mechanism was added to fall back to the `_all_docs` index if no other matching + # index could be found. The Python test was possibly written before the change + # happened and we cannot port the test as it is. + + # test "missing type" do + # {:error, resp} = MangoDatabase.find(@db_name, %{"age" => "foo"}) + # assert resp.status_code == 400 + # end + + test "field analyzer is keyword" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"location.state" => "New"}) + assert Enum.empty?(docs) + + {:ok, docs} = MangoDatabase.find(@db_name, %{"location.state" => "New Hampshire"}) + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 10 + end + + + # TODO + # The Python test asserts an exception. + # Since our FIELDS list only includes "name\\.first", we should + # get an error when we try to search for "name.first", since the index + # for that field does not exist. + # + # When mango was new, it failed if it didn’t find an index. + # shortly after mango was added to CouchDB, + # a mechanism was added to fall back to the `_all_docs` index if no other matching + # index could be found. The Python test was possibly written before the change + # happened and we cannot port the test as it is. + + # test "escaped field" do + # {:ok, docs} = MangoDatabase.find(@db_name, %{"name\\.first" => "name dot first"}) + # assert length(docs) == 1 + # assert Enum.at(docs, 0)["name.first"] == "name dot first" + + # {:error, resp} = MangoDatabase.find(@db_name, %{"name.first" => "name dot first"}) + # assert resp.status_code == 400 + # end + + test "filtered search fields" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}, fields: ["age", "location.state"]) + assert length(docs) == 1 + assert docs == [%{"age" => 22, "location" => %{"state" => "Missouri"}}] + + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}, fields: ["age", "Random Garbage"]) + assert length(docs) == 1 + assert docs == [%{"age" => 22}] + + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}, fields: ["favorites"]) + assert length(docs) == 1 + assert docs == [%{"favorites" => ["Lisp", "Erlang", "Python"]}] + + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}, fields: ["favorites.[]"]) + assert length(docs) == 1 + assert docs == [%{}] + + {:ok, docs} = MangoDatabase.find(@db_name, %{"age" => 22}, fields: ["all_fields"]) + assert length(docs) == 1 + assert docs == [%{}] + end + + test "two or" do + {:ok, docs} = MangoDatabase.find(@db_name, + %{ + "$or" => [ + %{"location.state" => "New Hampshire"}, + %{"location.state" => "Don't Exist"}, + ] + } + ) + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 10 + end + + test "all match" do + {:ok, docs} = MangoDatabase.find(@db_name, %{"favorites" => %{"$allMatch" => %{"$eq" => "Erlang"}}}) + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 10 + end +end + +defmodule CustomFieldsExistsTest do + use CouchTestCase + + @db_name "custom-fields-exists-test" + + @fields [ + %{"name" => "exists_field", "type" => "string"}, + %{"name" => "exists_array.[]", "type" => "string"}, + %{"name" => "exists_object.should", "type" => "string"}, + %{"name" => "twitter", "type" => "string"} + ] + + setup do + UserDocs.setup(@db_name, "text", false, fields: @fields) + end + + test "exists field" do + selector = %{"exists_field" => %{"$exists" => true}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == 2 + Enum.each(docs, fn doc -> + assert doc["user_id"] in [7, 8] + end) + + selector = %{"exists_field" => %{"$exists" => false}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == UserDocs.len() - 2 + Enum.each(docs, fn doc -> + refute doc["user_id"] in [7, 8] + end) + end + + test "exists array" do + selector = %{"exists_array" => %{"$exists" => true}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == 2 + Enum.each(docs, fn doc -> + assert doc["user_id"] in [9, 10] + end) + + selector = %{"exists_array" => %{"$exists" => false}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == UserDocs.len() - 2 + Enum.each(docs, fn doc -> + refute doc["user_id"] in [9, 10] + end) + end + + test "exists object member" do + selector = %{"exists_object.should" => %{"$exists" => true}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == 1 + assert Enum.at(docs, 0)["user_id"] == 11 + + selector = %{"exists_object.should" => %{"$exists" => false}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + assert length(docs) == UserDocs.len() - 1 + Enum.each(docs, fn doc -> + refute doc["user_id"] in [11] + end) + end + + test "exists false same as views" do + selector = %{"twitter" => %{"$exists" => false}} + {:ok, docs} = MangoDatabase.find(@db_name, selector) + + Enum.each(docs, fn doc -> + refute doc["user_id"] in [0, 1, 4, 13] + end) + end +end diff --git a/test/elixir/test/support/user_docs.ex b/test/elixir/test/support/user_docs.ex index eb015cc64b5..90e107885d3 100644 --- a/test/elixir/test/support/user_docs.ex +++ b/test/elixir/test/support/user_docs.ex @@ -341,7 +341,7 @@ defmodule UserDocs do MangoDatabase.save_docs(db, @users_docs) end - def setup(db, index_type \\ "view", partitioned \\ false) do + def setup(db, index_type \\ "view", partitioned \\ false, options \\ []) do MangoDatabase.recreate(db, [partitioned: partitioned]) docs = @docs @@ -357,7 +357,7 @@ defmodule UserDocs do case index_type do "view" -> add_view_indexes(db) - "text" -> add_text_indexes(db) + "text" -> add_text_indexes(db, options) "special" -> :ok end @@ -395,7 +395,7 @@ defmodule UserDocs do end) end - defp add_text_indexes(db) do - MangoDatabase.create_text_index(db) + defp add_text_indexes(db, options) do + MangoDatabase.create_text_index(db, options) end end