diff --git a/src/mango/test/24-text-paginated-test.py b/src/mango/test/24-text-paginated-test.py deleted file mode 100644 index bd420766c54..00000000000 --- a/src/mango/test/24-text-paginated-test.py +++ /dev/null @@ -1,108 +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 time - - -@unittest.skipUnless(mango.has_text_service(), "requires text service") -class PaginatedResultsTest(mango.DbPerClass): - # Great enough to make faster systems busy while running the - # query. - NUM_DOCS = 10000 - UPDATES = 25 - - def setUp(self): - super().setUp(db_per_test=True) - self.db.create_text_index( - analyzer="keyword", - default_field={}, - selector={}, - fields=[ - {"name": "_id", "type": "string"}, - {"name": "name", "type": "string"}, - ], - index_array_lengths=True, - ) - docs = [ - {"_id": f"{doc_id:08X}", "name": mango.random_string(32)} - for doc_id in range(self.NUM_DOCS) - ] - self.db.save_docs(docs) - - def test_query_with_lot_of_results(self): - # 200 is the maximum for `text` searches. - docs = self.db.find(selector={"_id": {"$lte": f"{1000:08X}"}}, limit=200) - assert len(docs) == 200 - - def do_query(self, delay, find_args): - time.sleep(delay) - return self.db.find(*find_args) - - def do_updates(self, pause, doc_id): - for i in range(self.UPDATES): - doc = self.db.open_doc(doc_id) - updated_doc = { - "_id": doc_id, - "_rev": doc["_rev"], - "update": i, - "name": "foobar", - } - self.db.save_doc(updated_doc) - time.sleep(pause) - - def test_no_duplicates_on_interleaved_updates(self): - # Give ~500 ms head start for the updates before running the - # query. - query = mango.Concurrently(self.do_query, (0.5, ({"name": "foobar"},))) - # Keep updating the target document in every 200 ms. - updates = mango.Concurrently(self.do_updates, (0.2, f"{2:08X}")) - docs = query.get_result() - updates.join() - assert len(docs) == 1 - - def test_no_duplicates_on_interleaved_updates_heavy(self): - query = mango.Concurrently(self.do_query, (0.5, ({"name": "foobar"},))) - updates = [ - mango.Concurrently(self.do_updates, (0.05, f"{2:08X}")), - mango.Concurrently(self.do_updates, (0.2, f"{3:08X}")), - mango.Concurrently(self.do_updates, (0.3, f"{4:08X}")), - mango.Concurrently(self.do_updates, (0.15, f"{5:08X}")), - mango.Concurrently(self.do_updates, (0.1, f"{6:08X}")), - ] - docs = query.get_result() - for ref in updates: - ref.join() - ids = list(map(lambda d: d["_id"], docs)) - assert sorted(ids) == [ - f"{2:08X}", - f"{3:08X}", - f"{4:08X}", - f"{5:08X}", - f"{6:08X}", - ] - - def test_no_duplicates_on_interleaved_updates_with_limit_skip(self): - query = mango.Concurrently(self.do_query, (0.5, ({"name": "foobar"}, 1, 3))) - updates = [ - mango.Concurrently(self.do_updates, (0.05, f"{2:08X}")), - mango.Concurrently(self.do_updates, (0.2, f"{3:08X}")), - mango.Concurrently(self.do_updates, (0.3, f"{4:08X}")), - mango.Concurrently(self.do_updates, (0.15, f"{5:08X}")), - mango.Concurrently(self.do_updates, (0.1, f"{6:08X}")), - ] - docs = query.get_result() - for ref in updates: - ref.join() - assert len(docs) == 1 diff --git a/test/elixir/test/config/search.elixir b/test/elixir/test/config/search.elixir index 87715d4caf3..33bedfae5ba 100644 --- a/test/elixir/test/config/search.elixir +++ b/test/elixir/test/config/search.elixir @@ -38,5 +38,11 @@ ], "LimitTests": [ "limit field" + ], + "PaginatedResultsTest": [ + "query with lot of results", + "no duplicates on interleaved updates", + "no duplicates on interleaved updates heavy", + "no duplicates on interleaved updates with limit skip" ] } diff --git a/test/elixir/test/mango/24_text_paginated_test.exs b/test/elixir/test/mango/24_text_paginated_test.exs new file mode 100644 index 00000000000..681c3f0248d --- /dev/null +++ b/test/elixir/test/mango/24_text_paginated_test.exs @@ -0,0 +1,127 @@ +# 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 PaginatedResultsTest do + alias Couch.Test.Utils + use CouchTestCase + + @db_name "paginated-results" + # Great enough to make faster systems busy while running the + # query. + @num_docs 9_999 + @updates 25 + + setup do + MangoDatabase.recreate(@db_name) + fields = [ + %{"name" => "_id", "type" => "string"}, + %{"name" => "name", "type" => "string"} + ] + MangoDatabase.create_text_index( + @db_name, + analyzer: "keyword", + default_field: %{}, + selector: %{}, + fields: fields, + index_array_lengths: true + ) + + docs = + 0..@num_docs + |> Enum.map(fn doc_id -> + %{ + "_id" => hex8(doc_id), + "name" => Utils.random_name("db") + } + end) + + MangoDatabase.save_docs(@db_name, docs) + :ok + end + + defp hex8(i) do + :io_lib.format("~8.16.0B", [i]) |> IO.iodata_to_binary() + end + + defp do_query(delay, selector, opts \\ []) do + Task.async(fn -> + Process.sleep(delay) + MangoDatabase.find(@db_name, selector, opts) + end) + end + + defp do_updates(pause, doc_id) do + Task.async(fn -> + for i <- 0..@updates do + doc = MangoDatabase.open_doc(@db_name, doc_id) + updated_doc = %{ + "_id" => doc_id, + "_rev" => doc["_rev"], + "update" => i, + "name" => "foobar" + } + + MangoDatabase.save_doc(@db_name, updated_doc) + Process.sleep(pause) + end + end) + end + + test "query with lot of results" do + selector = %{"_id" => %{"$lte": hex8(1000)}} + # 200 is the maximum for `text` searches. + {:ok, docs} = MangoDatabase.find(@db_name, selector, limit: 200) + assert length(docs) == 200 + end + + test "no duplicates on interleaved updates" do + # Give ~500 ms head start for the updates before running the + # query. + query = do_query(500, %{"name" => "foobar"}) + # Keep updating the target document in every 200 ms. + do_updates(200, hex8(2)) + + {:ok, docs} = Task.await(query, :infinity) + assert length(docs) == 1 + end + + test "no duplicates on interleaved updates heavy" do + query = do_query(500, %{"name" => "foobar"}) + do_updates(50, hex8(2)) + do_updates(200, hex8(3)) + do_updates(300, hex8(4)) + do_updates(150, hex8(5)) + do_updates(100, hex8(6)) + + {:ok, docs} = Task.await(query, :infinity) + ids = Enum.sort(Enum.map(docs, fn doc -> doc["_id"] end)) + assert ids == [ + hex8(2), + hex8(3), + hex8(4), + hex8(5), + hex8(6) + ] + end + + test "no duplicates on interleaved updates with limit skip" do + query = do_query(500, %{"name" => "foobar"}, limit: 1, skip: 3) + do_updates(50, hex8(2)) + do_updates(200, hex8(3)) + do_updates(300, hex8(4)) + do_updates(150, hex8(5)) + do_updates(100, hex8(6)) + + {:ok, docs} = Task.await(query, :infinity) + assert length(docs) == 1 + end +end diff --git a/test/elixir/test/support/mango_database.ex b/test/elixir/test/support/mango_database.ex index 1b5b4ab63a8..d0412c851e1 100644 --- a/test/elixir/test/support/mango_database.ex +++ b/test/elixir/test/support/mango_database.ex @@ -84,6 +84,11 @@ defmodule MangoDatabase do end end + def open_doc(db, docid) do + resp = Couch.get("/#{db}/#{docid}") + resp.body + end + def create_index(db, fields, options \\ []) do index = %{ "fields" => fields,