Mongo查询改造:如何在Map类型names字段中搜索字符串?
Hey there! Since your Shop entity's name field is now a Map<String, String> for multi-language support, we need to adjust the MongoDB query to check all values in that map for your search keyword. Here's the modified solution:
Updated Query Method
Replace your existing findByName method with this one:
@RestResource(path = "searchByName") @Query("{'$and': [{'companyId': ?0}, {'$expr': {'$anyElementTrue': {'$map': {'input': {'$objectToArray': '$names'}, 'as': 'nameEntry', 'in': {'$regex': ['$$nameEntry.v', ?1, 'i']}}}}]}") Page<Shop> findByName(String companyId, String name, Pageable pageable);
Let's Break Down What This Does
I know that query looks a bit complex, so let's unpack it step by step:
$objectToArray: '$names': Converts yournamesmap into an array of key-value pairs. For example,{"EN": "Shop1", "SW": "affär"}becomes[{"k":"EN", "v":"Shop1"}, {"k":"SW", "v":"affär"}]— this lets us iterate over all the name values.$map: Loops through each entry in the converted array. We alias each entry asnameEntry.$regex: ['$$nameEntry.v', ?1, 'i']: Checks if the value of the current entry ($$nameEntry.v) matches your search keyword (?1). The'i'option makes the match case-insensitive, just like your original query.$anyElementTrue: Returnstrueif any of the name values in the map match the keyword. This ensures we grab any Shop where at least one language's name contains your search term.- We keep the original
companyId: ?0filter to restrict results to the specified company, just like before.
Performance Tip (Important!)
If you're dealing with a large number of Shop documents, regular expression searches across map values can get slow. To speed things up, add a text index to your names field:
import org.springframework.data.mongodb.core.index.TextIndexed; // Inside your Shop class @TextIndexed private Map<String, String> names;
With this index, you can use MongoDB's text search feature for faster, natural-language style searches. Here's the simplified query for that approach:
@RestResource(path = "searchByName") @Query("{'$and': [{'companyId': ?0}, {'$text': {'$search': ?1}}]}") Page<Shop> findByName(String companyId, String name, Pageable pageable);
Note: Text search works best for full-word matches or natural language queries. If you need prefix matching (like searching for 'sho' to find 'Shop1'), stick with the regex-based query we started with.
Example Test Case
If you search for 'sho', the query will match any Shop where names has a value like Shop1 (EN), Shoppee (JP), etc. Similarly, searching for 'aff' will pick up entries with affär (SW) or affiliate (EN).
内容的提问来源于stack exchange,提问作者Sandeepa

