Skip to content

Commit

Permalink
Merge pull request #2855 from bagerard/fix_text_indexes_multiple_fiel…
Browse files Browse the repository at this point in the history
…ds_order_independent

Fix text index on multiple fields , order independent
  • Loading branch information
bagerard authored Oct 2, 2024
2 parents 8551738 + f4598f8 commit 38df6f1
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 1 deletion.
1 change: 1 addition & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Changelog
Development
===========
- (Fill this out as you fix issues and develop your features).
- Fix Document.compare_indexes() not working correctly for text indexes on multiple fields #2612
- Add support for transaction through run_in_transaction (kudos to juannyG for this) #2569
Some considerations:
- make sure to read https://www.mongodb.com/docs/manual/core/transactions-in-applications/#callback-api-vs-core-api
Expand Down
10 changes: 10 additions & 0 deletions mongoengine/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,13 @@ def __get__(self, instance, owner):

def __set__(self, instance, value):
raise AttributeError("Can not set attribute LazyRegexCompiler")


class NonOrderedList(list):
"""Simple utility class to compare lists without considering order (useful in context of indexes)"""

def __eq__(self, other):
if isinstance(other, list):
# Compare sorted versions of the lists
return sorted(self) == sorted(other)
return False
7 changes: 6 additions & 1 deletion mongoengine/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
TopLevelDocumentMetaclass,
get_document,
)
from mongoengine.base.utils import NonOrderedList
from mongoengine.common import _import_class
from mongoengine.connection import (
DEFAULT_CONNECTION_NAME,
Expand Down Expand Up @@ -1043,9 +1044,13 @@ def compare_indexes(cls):
# Useful for text indexes (but not only)
index_type = info["key"][0][1]
text_index_fields = info.get("weights").keys()
existing.append([(key, index_type) for key in text_index_fields])
# Use NonOrderedList to avoid order comparison, see #2612
existing.append(
NonOrderedList([(key, index_type) for key in text_index_fields])
)
else:
existing.append(info["key"])

missing = [index for index in required if index not in existing]
extra = [index for index in existing if index not in required]

Expand Down
33 changes: 33 additions & 0 deletions tests/document/test_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ class BlogPost(InheritFrom):
for expected in expected_specs:
assert expected["fields"] in info

assert BlogPost.compare_indexes() == {"missing": [], "extra": []}

def _index_test_inheritance(self, InheritFrom):
class BlogPost(InheritFrom):
date = DateTimeField(db_field="addDate", default=datetime.now)
Expand Down Expand Up @@ -949,6 +951,8 @@ class MyDoc(Document):
]["key"]
assert info["provider_ids.foo_1_provider_ids.bar_1"]["sparse"]

assert MyDoc.compare_indexes() == {"missing": [], "extra": []}

def test_text_indexes(self):
class Book(Document):
title = DictField()
Expand All @@ -968,6 +972,8 @@ class Book(Document):
assert "ref_id_hashed" in indexes
assert ("ref_id", "hashed") in indexes["ref_id_hashed"]["key"]

assert Book.compare_indexes() == {"missing": [], "extra": []}

def test_indexes_after_database_drop(self):
"""
Test to ensure that indexes are not re-created on a collection
Expand Down Expand Up @@ -1045,6 +1051,8 @@ class TestChildDoc(TestDoc):
TestDoc.ensure_indexes()
TestChildDoc.ensure_indexes()

assert TestDoc.compare_indexes() == {"missing": [], "extra": []}

index_info = TestDoc._get_collection().index_information()
for key in index_info:
del index_info[key][
Expand Down Expand Up @@ -1082,9 +1090,34 @@ class TestDoc(Document):
TestDoc.drop_collection()
TestDoc.ensure_indexes()

assert TestDoc.compare_indexes() == {"missing": [], "extra": []}

index_info = TestDoc._get_collection().index_information()
assert "shard_1_1__cls_1_txt_1_1" in index_info

def test_compare_indexes_works_with_compound_text_indexes(self):
"""The order of the fields in case of text indexes don't matter
so it's important to ensure that the compare_indexes method works that way
https://github.com/MongoEngine/mongoengine/issues/2612
"""

class Sample1(Document):
a = StringField()
b = StringField()

meta = {"indexes": [{"fields": ["$a", "$b"]}]}

class Sample2(Document):
a = StringField()
b = StringField()

meta = {"indexes": [{"fields": ["$b", "$a"]}]}

Sample1.drop_collection()
Sample2.drop_collection()
assert Sample1.compare_indexes() == {"missing": [], "extra": []}
assert Sample2.compare_indexes() == {"missing": [], "extra": []}


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

0 comments on commit 38df6f1

Please sign in to comment.