Skip to content

Commit 38df6f1

Browse files
authored
Merge pull request #2855 from bagerard/fix_text_indexes_multiple_fields_order_independent
Fix text index on multiple fields , order independent
2 parents 8551738 + f4598f8 commit 38df6f1

File tree

4 files changed

+50
-1
lines changed

4 files changed

+50
-1
lines changed

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Changelog
77
Development
88
===========
99
- (Fill this out as you fix issues and develop your features).
10+
- Fix Document.compare_indexes() not working correctly for text indexes on multiple fields #2612
1011
- Add support for transaction through run_in_transaction (kudos to juannyG for this) #2569
1112
Some considerations:
1213
- make sure to read https://www.mongodb.com/docs/manual/core/transactions-in-applications/#callback-api-vs-core-api

mongoengine/base/utils.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,13 @@ def __get__(self, instance, owner):
2020

2121
def __set__(self, instance, value):
2222
raise AttributeError("Can not set attribute LazyRegexCompiler")
23+
24+
25+
class NonOrderedList(list):
26+
"""Simple utility class to compare lists without considering order (useful in context of indexes)"""
27+
28+
def __eq__(self, other):
29+
if isinstance(other, list):
30+
# Compare sorted versions of the lists
31+
return sorted(self) == sorted(other)
32+
return False

mongoengine/document.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
TopLevelDocumentMetaclass,
1515
get_document,
1616
)
17+
from mongoengine.base.utils import NonOrderedList
1718
from mongoengine.common import _import_class
1819
from mongoengine.connection import (
1920
DEFAULT_CONNECTION_NAME,
@@ -1043,9 +1044,13 @@ def compare_indexes(cls):
10431044
# Useful for text indexes (but not only)
10441045
index_type = info["key"][0][1]
10451046
text_index_fields = info.get("weights").keys()
1046-
existing.append([(key, index_type) for key in text_index_fields])
1047+
# Use NonOrderedList to avoid order comparison, see #2612
1048+
existing.append(
1049+
NonOrderedList([(key, index_type) for key in text_index_fields])
1050+
)
10471051
else:
10481052
existing.append(info["key"])
1053+
10491054
missing = [index for index in required if index not in existing]
10501055
extra = [index for index in existing if index not in required]
10511056

tests/document/test_indexes.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ class BlogPost(InheritFrom):
6666
for expected in expected_specs:
6767
assert expected["fields"] in info
6868

69+
assert BlogPost.compare_indexes() == {"missing": [], "extra": []}
70+
6971
def _index_test_inheritance(self, InheritFrom):
7072
class BlogPost(InheritFrom):
7173
date = DateTimeField(db_field="addDate", default=datetime.now)
@@ -949,6 +951,8 @@ class MyDoc(Document):
949951
]["key"]
950952
assert info["provider_ids.foo_1_provider_ids.bar_1"]["sparse"]
951953

954+
assert MyDoc.compare_indexes() == {"missing": [], "extra": []}
955+
952956
def test_text_indexes(self):
953957
class Book(Document):
954958
title = DictField()
@@ -968,6 +972,8 @@ class Book(Document):
968972
assert "ref_id_hashed" in indexes
969973
assert ("ref_id", "hashed") in indexes["ref_id_hashed"]["key"]
970974

975+
assert Book.compare_indexes() == {"missing": [], "extra": []}
976+
971977
def test_indexes_after_database_drop(self):
972978
"""
973979
Test to ensure that indexes are not re-created on a collection
@@ -1045,6 +1051,8 @@ class TestChildDoc(TestDoc):
10451051
TestDoc.ensure_indexes()
10461052
TestChildDoc.ensure_indexes()
10471053

1054+
assert TestDoc.compare_indexes() == {"missing": [], "extra": []}
1055+
10481056
index_info = TestDoc._get_collection().index_information()
10491057
for key in index_info:
10501058
del index_info[key][
@@ -1082,9 +1090,34 @@ class TestDoc(Document):
10821090
TestDoc.drop_collection()
10831091
TestDoc.ensure_indexes()
10841092

1093+
assert TestDoc.compare_indexes() == {"missing": [], "extra": []}
1094+
10851095
index_info = TestDoc._get_collection().index_information()
10861096
assert "shard_1_1__cls_1_txt_1_1" in index_info
10871097

1098+
def test_compare_indexes_works_with_compound_text_indexes(self):
1099+
"""The order of the fields in case of text indexes don't matter
1100+
so it's important to ensure that the compare_indexes method works that way
1101+
https://github.com/MongoEngine/mongoengine/issues/2612
1102+
"""
1103+
1104+
class Sample1(Document):
1105+
a = StringField()
1106+
b = StringField()
1107+
1108+
meta = {"indexes": [{"fields": ["$a", "$b"]}]}
1109+
1110+
class Sample2(Document):
1111+
a = StringField()
1112+
b = StringField()
1113+
1114+
meta = {"indexes": [{"fields": ["$b", "$a"]}]}
1115+
1116+
Sample1.drop_collection()
1117+
Sample2.drop_collection()
1118+
assert Sample1.compare_indexes() == {"missing": [], "extra": []}
1119+
assert Sample2.compare_indexes() == {"missing": [], "extra": []}
1120+
10881121

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

0 commit comments

Comments
 (0)