最近在使用MongoDB的时候,遇到了使用多个关键词进行模糊查询的场景。竹风使用的是mongoengine库。python
查了各类资料,最后总结出比较好用的方法。先上代码,后面进行详细说明。以下:正则表达式
1 #!/usr/bin/env python 2 #coding:utf-8 3 4 import re 5 import mongoengine 6 from mongoengine import * 7 8 mongoengine.register_connection('default', 'test', host='127.0.0.1') 9 10 class TestData(Document): 11 name = StringField() 12 content = StringField() 13 14 TestData.objects.get_or_create(name='天地玄黄',defaults={'content':'abc123'}) 15 TestData.objects.get_or_create(name='宇宙洪荒',defaults={'content':'ABC123'}) 16 TestData.objects.get_or_create(name='每天向上',defaults={'content':'Abc123'}) 17 18 def print_arr(obj): 19 print obj.name,obj.content 20 21 def fuzzy_query_by_contains(): 22 print "\n###使用mongoengine的contains进行查询" 23 print "#contains区分大小写:" 24 test_data_list = TestData.objects(content__contains='abc123') 25 map(print_arr,test_data_list) 26 27 print "#icontains不区分大小写:" 28 test_data_list = TestData.objects(content__icontains='abc123') 29 map(print_arr,test_data_list) 30 31 def fuzzy_query_by_Q(): 32 print "\n###使用Q来进行查询" 33 test_data_list = TestData.objects( 34 Q(name__icontains=u'天地') | Q(name__icontains=u'宇宙')) 35 map(print_arr,test_data_list) 36 37 def fuzzy_query_by_pymongo(): 38 print "\n###使用raw queries,New in version 0.4" 39 print "#单个查询条件" 40 search = { 41 '__raw__':{ 42 'content':{'$regex':'A\S+\d+'}, 43 }, 44 } 45 test_data_list = TestData.objects(**search) 46 map(print_arr,test_data_list) 47 48 print "#多个查询条件" 49 search = { 50 '__raw__':{ 51 'name':{'$in':[re.compile(u'每天'),re.compile(u'宇宙')]}, 52 }, 53 } 54 test_data_list = TestData.objects(**search) 55 map(print_arr,test_data_list) 56 57 if __name__ == '__main__': 58 fuzzy_query_by_contains() 59 fuzzy_query_by_Q() 60 fuzzy_query_by_pymongo()
先讨论一下fuzzy_query_by_contains方法,这里用的是mongoengine提供的contains操做。值得注意的是,contains区分大小写,而icontains不区分大小写。这种方式在针对一个关键词进行模糊查询的时候特别方便。mongodb
而后是fuzzy_query_by_Q方法,这里结合了contains和Q来进行组合查询。当使用Q()来进行组合查询时,必须使用位运算符(|和&),而不能使用or,and来进行逻辑运算。这种方式比较合适肯定关键词数目的状况。若是关键词的数目是不定的,这种方式就略显纠结了。spa
竹风在动态关键词模糊查询的问题也是纠结良久,差点就要对每一个关键词分别查询,而后取交集凑结果了。后来在文档中发现,mongoengine有__raw__这个参数,能够执行PyMongo的查询(version 0.4提供的新功能)。因而几经试验,fuzzy_query_by_pymongo方法就出炉了。
PyMongo支持正则表达式,提供了两种方法,一种是使用$regex,另外一种是使用re.compile()。
在例子中,对单个关键词进行模糊查询,对应的代码为:{'$regex':'A\S+\d+'}
接着就是对多个关键词进行查询,对应的代码为:{'$in':[re.compile(u'每天'),re.compile(u'宇宙')]}code
对代码进行一些修改,以便接受多个关键词,代码以下:对象
1 def fuzzy_query_by_pymongo(): 2 print "#多个查询条件" 3 keyword = u'每天 宇宙' 4 search = {'__raw__' : {'name':{'$in':map(re.compile,keyword.split())}}} 5 test_data_list = TestData.objects(**search) 6 map(print_arr,test_data_list)
顺带一提,例子中建立数据是用的get_or_create,会返回一个元组,第一个元素是建立or查询的对象,第二个元素是是否建立成功。文档中的推荐用法以下:blog
1 >>> a, created = User.objects.get_or_create(name='User A', defaults={'age': 30}) 2 >>> b, created = User.objects.get_or_create(name='User A', defaults={'age': 40}) 3 >>> a.name == b.name and a.age == b.age 4 True
最后是例子运行的结果,返回的结果顺序可能略有不一样,没必要在乎。utf-8
1 $ python mongodb_test.py 2 3 ###使用mongoengine的contains进行查询 4 #contains区分大小写: 5 天地玄黄 abc123 6 #icontains不区分大小写: 7 天地玄黄 abc123 8 宇宙洪荒 ABC123 9 每天向上 Abc123 10 11 ###使用Q来进行查询 12 天地玄黄 abc123 13 宇宙洪荒 ABC123 14 15 ###使用raw queries,New in version 0.4 16 #单个查询条件 17 宇宙洪荒 ABC123 18 每天向上 Abc123 19 #多个查询条件 20 宇宙洪荒 ABC123 21 每天向上 Abc123