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 8 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
9 changes: 9 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ 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
26 changes: 24 additions & 2 deletions test/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,15 @@ def test_delete_many(self):
last_cmd_payload = self.explain.last_cmd_payload
self._compare_command_dicts(last_cmd_payload, last_logger_payload)

@unittest.skip("Travis does not have replica sets set up yet")
Copy link
Collaborator

Choose a reason for hiding this comment

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

This is no longer true.

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.

def test_watch(self):
res = self.explain.watch()
self.assertIn("queryPlanner", res["stages"][0]["$cursor"])
self.collection.watch(pipeline=[{"$project": {"tags": 1}}],
batch_size=10, full_document="updateLookup")
batch_size=10, full_document="updateLookup")
Copy link
Collaborator

Choose a reason for hiding this comment

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

This change seems off batch_size should align with pipeline.

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.

last_logger_payload = self.logger.cmd_payload
res = self.explain.watch(pipeline=[{"$project": {"tags": 1}}],
batch_size=10, full_document="updateLookup")
batch_size=10, full_document="updateLookup")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same here.

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.assertIn("queryPlanner", res["stages"][0]["$cursor"])
last_cmd_payload = self.explain.last_cmd_payload
self._compare_command_dicts(last_cmd_payload, last_logger_payload)
Expand Down Expand Up @@ -217,6 +218,27 @@ 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="hey, "
"I'm a "
"comment")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please fix the string format here. 3 short strings on new lines makes this harder to read. How about:

self.explain = ExplainCollection(self.collection, comment="comment")

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.assertIn("comment", self.logger.cmd_payload)

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