searching textfield for each keyword - python

Right now my views.py function allows me to search each Django textfield for the ENTIRE search phrase. However if I would like to search for a title and author, such as, "biology John", my queryset will end up empty, and I am not sure how to break up the phrase and search for individual words.
def search(request):
query = request.GET.get('search')
if query:
results = Protocol.objects.filter(Q(title__contains=query) | Q(author__contains=query) | Q(description__contains=query) | Q(reagents__contains=query) | Q(protocol_steps__contains=query))
else:
results = ''
return render(request, 'protocat_app/search_protocols.html',{'results':results})

You definitely can set up haystack with solr/elasticsearch. But if you still need some database query, you can use the following:
import operator
def search(request):
terms = request.GET.get('search', '').split(' ')
q_list = []
for term in terms:
if term:
q_list.append(Q(title__contains=query))
q_list.append(Q(author__contains=query))
q_list.append(Q(description__contains=query))
q_list.append(Q(reagents__contains=query))
q_list.append(Q(protocol_steps__contains=query))
if q_list:
results = Protocol.objects.filter(reduce(operator.or_, q_list))
else:
results = ''
return render(request, 'protocat_app/search_protocols.html',{'results':results})
Hope this helps :)

I would suggest setting up a search server like Solr/Elastic Search.
Here you have just one case where you need to split the query among 2 fields. Maybe later you come up with a situation where you need to find among multiple indexed fields. Thus, Solr would be of great help.
You can read about Solr here.
Also, you can make use of django-haystack to make django interact with Solr and get the filtered results as per what the user searched.

Related

Django PostgreSQL - Fuzzy Searching on single words

I'm using Django's builtin trigram_similar lookup and TrigramSimilarity class to create a fuzzy searching using a PostgreSQL database. This way, I filter titles of articles. The problem is, this filters based on the whole title. I want to filter it also on parts on title.
Example:
Title of Article: "We are the world | This is the best article ever".
My search: "we"
In this example, my function returns nothing, but I want it to return this article. How can I do that?
This is the code I use:
def search(qs, term, fields=["title"], treshold=.3):
if len(fields) >= 2:
return qs.annotate(
similarity=Greatest(
*[TrigramSimilarity(field, term) for field in fields]
)
).filter(similarity__gte=treshold).order_by("-similarity")
return qs.filter(
**{"{}__trigram_similar".format(fields[0]): term}
)
I think the problem is that you are using trigram similarity with a two letter word. If you try to do this with a word that is three letters, maybe it will work?

Django QuerySet Lookup Fails When Testing for Value "Local"

I have a QuerySet which returns a list of user-defined tags. In some cases, I'd like to exclude any of the tags that start with the word "Local", but this seems to be causing me problems.
The following examples work when I'm testing for other values (like HVAC below):
queryset = queryset.exclude(tags__tag__tag_name__icontains = 'HVAC')
queryset = queryset.exclude(tags__tag__tag_name__istartswith = 'HVAC')
but when I try the same with "Local", it excludes everything, not just the values that contain or start with the word "Local". Both examples below exclude everything:
queryset = queryset.exclude(tags__tag__tag_name__icontains = 'Local')
queryset = queryset.exclude(tags__tag__tag_name__istartswith = 'Local')
As an additional note, the following does work, but it only excludes that exact value and I can't anticipate / list all of the values that start with "Local":
queryset = queryset.exclude(tags__tag__tag_name = 'Local 123')
My best guess is that "Local" is a reserved word in python? Any ideas on ways around this or is there something else I'm missing?
I don't know if this is exactly the right way to deal with this issue, but since per #WillemVanOnsem it seems like I was excluding all model objects that have at least one tag with 'Local' in it (when using both exclude and filter as far as I can tell), instead I ended up creating a new list of all values that don't contain "Local" and returning that list instead of the original queryset.
newQueryset = list()
for item in list(queryset):
if not 'local' in str(item['tags__tag__tag_name']).lower():
newQueryset.append(item)
return newQueryset

Filtering a queryset issue in view django

I apologise in advance as im still new to django and I've inherited this projects.
i currently have this views.py
def home(request):
if request.user.is_authenticated():
location = request.GET.get('location', request.user.profile.location)
users = User.objects.filter(profile__location=location)
print (users)
matches = Match.objects.get_matches_with_percent(request.user)
print (matches)
I get a list of users from matching app in "matches = " like this from the print (matches) [[<User: lucy>, '79.00%'], [<User: newuser>, '70.71%'],......
and i get a list of users from the "users =" like this from print (users) [<User: jonsnow>, <User: newuser>]......
how do i filter matches down to the users that come through, i tried adding a .filter(User=users) on the end of "matches =" however i got this error "'list' object has no attribute 'filter'".
I really hope this makes some sense i do apologise if I've explained my self very badly here, and thank you in advance for any help.
You could use a list comprehension. They're fairly quick, execution time-wise. The following should do the trick:
matches = [match for match in matches if match[0] in users]
OR
user_ids = users.values_list('id', flat=True)
matches = [match for match in matches if match[0].id in user_ids]
get_matches_with_percent is not a django method, but a custom method in your models manager. When searching the internet I assume that this is your code, and specifically your method. It does not return a QuerySet but a regular list. You will have to update that method if you want to be able to filter the QuerySet instead.

Combining querysets obtained from a loop

Let's say I have a list of people that can be "followed".
I'd like to iterate through all the people that a certain user is following and grab posts from all of those users in the form of a queryset.
I understand that I can combine querysets by using chain or |, but I'm a bit confused when it comes to combining querysets that I might grab from looping through everyone being followed.
following = UserFollows.objects.filter(user_id = user.id)
for follow in following.iterator():
UserPost.objects.filter(user=follow.user) #what do I do with this?
How would I combine those if I cant explicitly name them to chain or '|'?
You can do something like this:
following = UserFollows.objects.filter(user__id = user.id).select_related('user')
users_ids = [follow.user.id for follow in following]
posts = UserPost.objects.filter(user__id__in=users_ids)
but look that it is quite expensive operation so it's good to add select_related() method to fetch users in one query. I think you should also consider to cache the users_ids list before get it from database.
Have you tried something like
following = UserFollows.objects.filter(user_id = user.id)
q = UserPost.objects.filter(user=following[0].user)
for follow in following[1:]:
q = q | UserPost.objects.filter(user=follow.user)

How to implement Django like 'contains' filter query with Google App Engine?

Is there some way to implement Django like contains or icontains filters with Google App Engine as well? I am using app engine patch.
I have used the below query earlier to match the start of a string but now the requirement is to find a string pattern within Datastore.
Employee.all().filter('name >=',value).filter('name <',unicode(value) + u'\ufffd')
Basically the requirement is to implement a search functionality with all the strings containing the search string as a result.
Please suggest.
Thanks in advance.
What you need to do is create a string list property with the different string permutations and query on that list.
Take a look at appengine.ext.search.SearchableModel for an example implementation.
You can also check out nonrel-search.
No, a true substring search isn't really possible in the datastore.
The best you could do would be to add a keyword index and do the "begins with" type query from your question on that, but even then for an entity like "foo bar baz" you could match when searching for "ba" but not "ar b".
This sounds like a great job for BigQuery, which is in preview right now with a wait list to get access.
Recently I ran into the same problem and made my own model manager with an overriden filter function that also understands __contains. I'm assuming you're using Django.
class UserFeedManager(models.Manager):
def filter(self, *args, **kwargs):
keywordargs = {}
for (arg, val) in kwargs.items():
if '__contains' in arg:
contains.append((arg.split('__')[0], val))
else:
keywordargs[arg] = val
result = []
if len(contains) > 0:
for entry in super(UserFeedManager, self).filter(*args, **keywordargs):
if all(map(lambda (attr, val): val in entry.__getattribute__(attr),
contains)):
result.append(entry)
else:
result = super(UserFeedManager, self).filter(*args, **keywordargs)
return result
Essentially it builds a list of arguments that have a __contains, then fetches the whole result and filters it to only hold the results that pass all criteria.
querySet = Entity.objects.all()
ids = []
for i,obj in enumerate(querySet.iterator()):
str_to_search =obj.name
if str_to_search.find('blahblah') != -1:
ids.append(obj.id)
querySet = querySet.filter(id__in = ids)

Categories