Skip to content

Add ability to configure explain command options #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Dec 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,16 @@ To run explain on a command, first instantiate an ``ExplainableCollection`` from
collection = client.db.products
explain = ExplainableCollection(collection)

If you wish to configure the options for the explain command itself, pass
them to the ``ExplainableCollection`` constructor like so::

explain = ExplainableCollection(collection, verbosity="queryPlanner",
comment="I'm a comment")

For more information see the documentation for the explain_ command.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe there needs to be a blank line after the code block.


.. _explain: https://docs.mongodb.com/master/reference/command/explain/#dbcmd.explain.

Now you are ready to explain some commands. Remember that explaining a command does not execute it::

result = explain.update_one({"quantity": 1057, "category": "apparel"}, {"$set": {"reorder": True}})
Expand Down
8 changes: 6 additions & 2 deletions pymongoexplain/explainable_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,18 @@


class ExplainableCollection():
def __init__(self, collection):
def __init__(self, collection, verbosity=None, comment=None):
self.collection = collection
self.last_cmd_payload = None
self.verbosity = verbosity or "queryPlanner"
self.comment = comment

def _explain_command(self, command):
command_son = command.get_SON()
explain_command = SON([("explain", command_son)])
explain_command["verbosity"] = "queryPlanner"
explain_command["verbosity"] = self.verbosity
if self.comment:
explain_command["comment"] = self.comment
self.last_cmd_payload = command_son
return self.collection.database.command(explain_command)

Expand Down
22 changes: 21 additions & 1 deletion test/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# See the License for the specific language governing permissions and
# limitations under the License.


import unittest
import subprocess
import os
Expand All @@ -28,6 +27,7 @@
class CommandLogger(monitoring.CommandListener):
def __init__(self):
self.cmd_payload = {}

def started(self, event):
self.cmd_payload = event.command

Expand All @@ -37,6 +37,7 @@ def succeeded(self, event):
def failed(self, event):
pass


class TestExplainableCollection(unittest.TestCase):
def setUp(self) -> None:
self.logger = CommandLogger()
Expand Down Expand Up @@ -217,6 +218,25 @@ def test_imports(self):
from pymongoexplain import ExplainableCollection
self.assertEqual(ExplainableCollection, ExplainCollection)

def test_verbosity(self):
res = self.explain.find({})
self.assertNotIn("executionStats", res)
self.assertNotIn("allPlansExecution", res.get("executionStats", []))
self.explain = ExplainCollection(self.collection, verbosity="executionStats")
res = self.explain.find({})
self.assertIn("executionStats", res)
self.assertNotIn("allPlansExecution", res["executionStats"])
self.explain = ExplainCollection(self.collection, verbosity="allPlansExecution")
res = self.explain.find({})
self.assertIn("executionStats", res)
self.assertIn("allPlansExecution", res["executionStats"])

def test_comment(self):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of checking the explain result, this test should probably assert that we sent the comment field with the explain comment using a CommandListener.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

self.explain.find({})
self.assertNotIn("comment", self.logger.cmd_payload)
self.explain = ExplainCollection(self.collection, comment="comment")
self.explain.find({})
self.assertIn("comment", self.logger.cmd_payload)

if __name__ == '__main__':
unittest.main()