Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoEngine query list for objects having properties starting with prefixes specified in a list

I need to query Mongo database for elements that have a certain property beginning with any prefix in the list. Now I have a piece of code like this:

query = mymodel(terms__term__in=query_terms)

and this matches objects that have an item on a list "terms" that has StringField "term" explicitly occurring on a list "query_terms". What I want to achieve is having objects that have an item on a list "terms" that has StringField "term" beginning with any prefix that occurs on a list "query_terms". Is it possible to do it in one query and without storing every possible prefix of "term" in database? EDIT: Solution below works great but now I have to find objects with terms starting with every prefix on a list. I changed

query = reduce(lambda q1, q2: q1.__or__(q2), 
           map(lambda prefix: Q(terms__term__startswith=prefix)))

to

query = reduce(lambda q1, q2: q1.__and__(q2), 
           map(lambda prefix: Q(terms__term__startswith=prefix)))

but this does not work. I end up getting the following error:

InvalidQueryError: Duplicate query conditions: terms__term__startswith

Any ideas?

like image 735
k_wisniewski Avatar asked Apr 27 '12 10:04

k_wisniewski


2 Answers

If your querying a term for it's value, you can filter the values that begin with a perfix like so:

MyModel.objects.filter(terms__term__startswith='foo')

If you need to filter for multiple prefixes you'll have to create Q objects for that:

MyModel.objects.filter(Q(terms__term__startswith='foo') | Q(terms__term__startswith='bar'))

If you need to create the query dynamically:

prefixes = ['foo', 'bar', 'baz']
query = reduce(lambda q1, q2: q1.__or__(q2), 
               map(lambda prefix: Q(terms__term__startswith=prefix), prefixes))
MyModel.objects.filter(query)
like image 137
Filip Dupanović Avatar answered Sep 28 '22 03:09

Filip Dupanović


You can use a regular expression like ^(prefix1 | prefix2 etc):

prefixes = [....]
regex = '^(%s)' % '|'.join(map(re.escape, prefixes))
docs = your_collection.find({'term': {'$regex': regex}})

upd: didn't notice this question is about mongoengine. The above is for pure pymongo, don't know if ME allows this.

like image 38
georg Avatar answered Sep 28 '22 04:09

georg