def add_items_update_then_create(self, content_type_pk, objs): config = self.backend.config ids_and_objs = {} for obj in objs: obj._autocomplete_ = (ADD([ SearchVector(Value(text), weight=weight, config=config) for text, weight in obj._autocomplete_ ]) if obj._autocomplete_ else EMPTY_VECTOR) obj._body_ = (ADD([ SearchVector(Value(text), weight=weight, config=config) for text, weight in obj._body_ ]) if obj._body_ else EMPTY_VECTOR) ids_and_objs[obj._object_id_] = obj index_entries_for_ct = self.entries.filter( content_type_id=content_type_pk) indexed_ids = frozenset( index_entries_for_ct.filter( object_id__in=ids_and_objs).values_list('object_id', flat=True)) for indexed_id in indexed_ids: obj = ids_and_objs[indexed_id] index_entries_for_ct.filter(object_id=obj._object_id_) \ .update(autocomplete=obj._autocomplete_, body=obj._body_) to_be_created = [] for object_id in ids_and_objs: if object_id not in indexed_ids: obj = ids_and_objs[object_id] to_be_created.append( IndexEntry(content_type_id=content_type_pk, object_id=object_id, autocomplete=obj._autocomplete_, body=obj._body_)) self.entries.bulk_create(to_be_created)
def search(self, config, start, stop): # TODO: Handle MatchAll nested inside other search query classes. if isinstance(self.query, MatchAll): return self.queryset[start:stop] search_query = self.build_database_query(config=config) queryset = self.queryset query = queryset.query if self.fields is None: vector = F('index_entries__body_search') else: vector = ADD( SearchVector(field, config=search_query.config, weight=get_weight(self.get_boost(field))) for field in self.fields) vector = vector.resolve_expression(query) search_query = search_query.resolve_expression(query) lookup = IndexEntry._meta.get_field('body_search').get_lookup('exact')( vector, search_query) query.where.add(lookup, 'AND') if self.order_by_relevance: # Due to a Django bug, arrays are not automatically converted here. converted_weights = '{' + ','.join(map(str, WEIGHTS_VALUES)) + '}' queryset = queryset.order_by( SearchRank(vector, search_query, weights=converted_weights).desc(), '-pk') elif not queryset.query.order_by: # Adds a default ordering to avoid issue #3729. queryset = queryset.order_by('-pk') return queryset[start:stop]
def search(self, config, start, stop): # TODO: Handle MatchAll nested inside other search query classes. if isinstance(self.query, MatchAll): return self.queryset[start:stop] search_query = self.build_database_query(config=config) queryset = self.queryset query = queryset.query if self.fields is None: vector = F('index_entries__body_search') else: vector = ADD( SearchVector(field, config=search_query.config, weight=get_weight(self.get_boost(field))) for field in self.fields) vector = vector.resolve_expression(query) search_query = search_query.resolve_expression(query) lookup = IndexEntry._meta.get_field('body_search').get_lookup('exact')( vector, search_query) query.where.add(lookup, 'AND') if self.order_by_relevance: # Due to a Django bug, arrays are not automatically converted here. converted_weights = '{' + ','.join(map(str, WEIGHTS_VALUES)) + '}' queryset = queryset.order_by(SearchRank(vector, search_query, weights=converted_weights).desc(), '-pk') elif not queryset.query.order_by: # Adds a default ordering to avoid issue #3729. queryset = queryset.order_by('-pk') return queryset[start:stop]
def build_tsrank(self, vector, query, config=None, boost=1.0): if isinstance(query, (Phrase, PlainText, Not)): rank_expression = SearchRank(vector, self.build_tsquery(query, config=config), weights=self.sql_weights) if boost != 1.0: rank_expression *= boost return rank_expression elif isinstance(query, Boost): boost *= query.boost return self.build_tsrank(vector, query.subquery, config=config, boost=boost) elif isinstance(query, And): return MUL( 1 + self.build_tsrank(vector, subquery, config=config, boost=boost) for subquery in query.subqueries) - 1 elif isinstance(query, Or): return ADD( self.build_tsrank(vector, subquery, config=config, boost=boost) for subquery in query.subqueries) / (len(query.subqueries) or 1) raise NotImplementedError( '`%s` is not supported by the PostgreSQL search backend.' % query.__class__.__name__)
def search(self, config, start, stop, score_field=None): # TODO: Handle MatchAll nested inside other search query classes. if isinstance(self.query, MatchAll): return self.queryset[start:stop] search_query = self.build_database_query(config=config) if self.fields is None: vector = F('index_entries__autocomplete') if not self.is_autocomplete: vector = vector._combine(F('index_entries__body'), '||', False) else: vector = ADD( SearchVector(field_lookup, config=search_query.config, weight=get_weight(search_field.boost)) for field_lookup, search_field in self.search_fields.items() if not self.is_autocomplete or search_field.partial_match) rank_expression = SearchRank(vector, search_query, weights=self.sql_weights) queryset = self.queryset.annotate(_vector_=vector).filter( _vector_=search_query) if self.order_by_relevance: queryset = queryset.order_by(rank_expression.desc(), '-pk') elif not queryset.query.order_by: # Adds a default ordering to avoid issue #3729. queryset = queryset.order_by('-pk') rank_expression = F('pk') if score_field is not None: queryset = queryset.annotate(**{score_field: rank_expression}) return queryset[start:stop]
def get_fields_vector(self, search_query): return ADD( SearchVector(field_lookup, config=search_query.config, weight=get_weight(search_field.boost)) for field_lookup, search_field in self.search_fields.items() if search_field.partial_match)
def get_fields_vector(self, search_query): return ADD( SearchVector( field_lookup, config=search_query.config, weight='D', ) for field_lookup, search_field in self.search_fields.items())
def add_items_update_then_create(self, content_type_pk, objs, config): ids_and_objs = {} for obj in objs: obj._search_vector = (ADD([ SearchVector(Value(text), weight=weight, config=config) for text, weight in obj._body_ ]) if obj._body_ else SearchVector(Value(''))) ids_and_objs[obj._object_id] = obj index_entries_for_ct = self.index_entries.filter( content_type_id=content_type_pk) indexed_ids = frozenset( index_entries_for_ct.filter( object_id__in=ids_and_objs).values_list('object_id', flat=True)) for indexed_id in indexed_ids: obj = ids_and_objs[indexed_id] index_entries_for_ct.filter(object_id=obj._object_id) \ .update(body_search=obj._search_vector) to_be_created = [] for object_id in ids_and_objs: if object_id not in indexed_ids: to_be_created.append( IndexEntry( content_type_id=content_type_pk, object_id=object_id, body_search=ids_and_objs[object_id]._search_vector, )) self.index_entries.bulk_create(to_be_created)
def as_vector(self, texts): """ Converts an array of strings into a SearchVector that can be indexed. """ if not texts: return EMPTY_VECTOR return ADD([ SearchVector(Value(text, output_field=TextField()), weight=weight, config=self.search_config) for text, weight in texts ])
def as_vector(self, texts, for_autocomplete=False): """ Converts an array of strings into a SearchVector that can be indexed. """ if not texts: return EMPTY_VECTOR search_config = self.autocomplete_config if for_autocomplete else self.config return ADD([ SearchVector(Value(text, output_field=TextField()), weight=weight, config=search_config) for text, weight in texts ])