def search(request): """Find users by username and displayname. Uses the ES user's index. """ results = [] q = request.GET.get("q") if q: search = ProfileDocument.search().query( "simple_query_string", query=q, fields=["username", "name"], default_operator="AND", ) results = search.execute().hits # For now, we're just truncating results at 30 and not doing any # pagination. If somebody complains, we can add pagination or something. results = list(results[:30]) data = {"q": q, "results": results} return render(request, "community/search.html", data)
def search(request): """Find users by username and displayname. Uses the ES user's index. """ results = [] q = request.GET.get("q") if q: contributor_group_ids = list( Group.objects.filter( name__in=[ "Contributors", CONTRIBUTOR_GROUP, ] ).values_list("id", flat=True) ) search = ProfileDocument.search().query( "boosting", positive=Q( "simple_query_string", query=q, fields=["username", "name"], default_operator="AND", ), # reduce the scores of users not in the contributor groups: negative=Q( "bool", must_not=Q("terms", group_ids=contributor_group_ids), ), negative_boost=0.5, )[:30] results = search.execute().hits data = {"q": q, "results": results} return render(request, "community/search.html", data)
def top_contributors_questions(start=None, end=None, locale=None, product=None, count=10, page=1): """Get the top Support Forum contributors.""" search = AnswerDocument.search() search = ( search.filter( # filter out answers by the question author "script", script= "doc['creator_id'].value != doc['question_creator_id'].value", ).filter( # filter answers created between `start` and `end`, or within the last 90 days "range", created={ "gte": start or datetime.now() - timedelta(days=90), "lte": end }, ) # set the query size to 0 because we don't care about the results # we're just filtering for the aggregations defined below .extra(size=0)) if locale: search = search.filter("term", locale=locale) if product: search = search.filter("term", question_product_id=product.id) # our filters above aren't perfect, and don't only return answers from contributors # so we need to collect more buckets than `count`, so we can hopefully find `count` # number of contributors within search.aggs.bucket( # create buckets for the `count * 10` most active users "contributions", A("terms", field="creator_id", size=count * 10), ).bucket( # within each of those, create a bucket for the most recent answer, and extract its date "latest", A( "top_hits", sort={"created": { "order": "desc" }}, _source={"includes": "created"}, size=1, ), ) contribution_buckets = search.execute().aggregations.contributions.buckets if not contribution_buckets: return [], 0 user_ids = [bucket.key for bucket in contribution_buckets] contributor_group_ids = list( Group.objects.filter(name__in=CONTRIBUTOR_GROUPS).values_list( "id", flat=True)) # fetch all the users returned by the aggregation which are in the contributor groups user_hits = (ProfileDocument.search().query("terms", **{ "_id": user_ids }).query("terms", group_ids=contributor_group_ids).extra( size=len(user_ids)).execute().hits) users = {hit.meta.id: hit for hit in user_hits} total_contributors = len(user_hits) top_contributors = [] for bucket in contribution_buckets: if len(top_contributors) == page * count: # stop once we've collected enough contributors break user = users.get(bucket.key) if user is None: continue last_activity = datetime.fromisoformat( bucket.latest.hits.hits[0]._source.created) days_since_last_activity = (datetime.now(tz=timezone.utc) - last_activity).days top_contributors.append({ "count": bucket.doc_count, "term": bucket.key, "user": { "id": user.meta.id, "username": user.username, "display_name": user.name, "avatar": getattr(getattr(user, "avatar", None), "url", None), "days_since_last_activity": days_since_last_activity, }, }) return top_contributors[count * (page - 1):], total_contributors
def get_doc(self): return ProfileDocument.get(self.user_id)
def top_contributors_questions(start=None, end=None, locale=None, product=None, count=10): """Get the top Support Forum contributors.""" search = AnswerDocument.search() search = search.filter( # filter out answers by the question author "script", script="doc['creator_id'].value != doc['question_creator_id'].value", ).filter( # filter answers created between `start` and `end`, or within the last 90 days "range", created={"gte": start or datetime.now() - timedelta(days=90), "lte": end}, ) if locale: search = search.filter("term", locale=locale) if product: search = search.filter("term", question_product_id=product.id) search.aggs.bucket( # create buckets for the `count + 1` most active contributors "contributions", A("terms", field="creator_id", size=count + 1), ).bucket( # within each of those, create a bucket for the most recent answer, and extract its date "latest", A( "top_hits", sort={"created": {"order": "desc"}}, _source={"includes": "created"}, size=1, ), ) contribution_buckets = search.execute().aggregations.contributions.buckets total_contributors = len(contribution_buckets) if not total_contributors: return [], 0 user_ids = [bucket.key for bucket in contribution_buckets[:count]] users = ProfileDocument.mget(user_ids, missing="none") top_contributors = [] for loop_index, bucket in enumerate(contribution_buckets[:count]): user = users[loop_index] if user is None: continue if bucket.key != user.meta.id: raise RuntimeError("ES returned profiles out of order") last_activity = datetime.fromisoformat(bucket.latest.hits.hits[0]._source.created) days_since_last_activity = (datetime.now(tz=timezone.utc) - last_activity).days top_contributors.append( { "count": bucket.doc_count, "term": bucket.key, "user": { "id": user.meta.id, "username": user.username, "display_name": user.name, "avatar": getattr(user.avatar, "url", None), "days_since_last_activity": days_since_last_activity, }, } ) return top_contributors, total_contributors
def doc(self): return ProfileDocument.get(self.profile.pk)
def prepare(self): return ProfileDocument.prepare(self.profile)