1
+ # -*- coding: utf-8 -*-
1
2
import logging
2
3
3
4
from elasticsearch_dsl import FacetedSearch , TermsFacet
4
5
from elasticsearch_dsl .query import Bool , SimpleQueryString
5
6
7
+ from readthedocs .search .documents import (
8
+ DomainDocument ,
9
+ PageDocument ,
10
+ ProjectDocument ,
11
+ )
12
+ from readthedocs .search .signals import (
13
+ before_domain_search ,
14
+ before_file_search ,
15
+ before_project_search ,
16
+ )
17
+
6
18
from readthedocs .core .utils .extend import SettingsOverrideObject
7
- from readthedocs .search .documents import PageDocument , ProjectDocument
8
19
9
20
log = logging .getLogger (__name__ )
10
21
11
22
23
+ ALL_FACETS = ['project' , 'version' , 'doc_type' , 'language' , 'index' ]
24
+
25
+
12
26
class RTDFacetedSearch (FacetedSearch ):
13
27
14
28
def __init__ (self , user , ** kwargs ):
29
+ self .user = user
30
+ self .filter_by_user = kwargs .pop ('filter_by_user' , None )
31
+ for facet in self .facets :
32
+ if facet in kwargs :
33
+ kwargs .setdefault ('filters' , {})[facet ] = kwargs .pop (facet )
34
+
35
+ # Don't pass along unnecessary filters
36
+ for f in ALL_FACETS :
37
+ if f in kwargs :
38
+ del kwargs [f ]
39
+ super (RTDFacetedSearch , self ).__init__ (** kwargs )
40
+
41
+ def search (self ):
15
42
"""
16
43
Pass in a user in order to filter search results by privacy.
17
44
@@ -20,48 +47,36 @@ def __init__(self, user, **kwargs):
20
47
The `self.user` attribute isn't currently used on the .org,
21
48
but is used on the .com
22
49
"""
23
- self .user = user
24
- self .filter_by_user = kwargs .pop ('filter_by_user' , None )
25
- super ().__init__ (** kwargs )
50
+ s = super ().search ()
51
+ s = s .source (exclude = ['content' , 'headers' ])
52
+ resp = self .signal .send (sender = self , user = self .user , search = s )
53
+ if resp :
54
+ # Signal return a search object
55
+ try :
56
+ s = resp [0 ][1 ]
57
+ except AttributeError :
58
+ log .exception (
59
+ 'Failed to return a search object from search signals'
60
+ )
61
+ # Return 25 results
62
+ return s [:25 ]
26
63
27
64
def query (self , search , query ):
28
65
"""
29
66
Add query part to ``search`` when needed.
30
67
31
- Also does HTML encoding of results to avoid XSS issues.
68
+ Also:
69
+
70
+ * Adds SimpleQueryString instead of default query.
71
+ * Adds HTML encoding of results to avoid XSS issues.
32
72
"""
33
- search = super ().query (search , query )
34
73
search = search .highlight_options (encoder = 'html' , number_of_fragments = 3 )
35
- search = search .source (exclude = ['content' , 'headers' ])
36
- return search
37
-
38
-
39
- class ProjectSearchBase (RTDFacetedSearch ):
40
- facets = {'language' : TermsFacet (field = 'language' )}
41
- doc_types = [ProjectDocument ]
42
- index = ProjectDocument ._doc_type .index
43
- fields = ('name^10' , 'slug^5' , 'description' )
44
-
45
-
46
- class PageSearchBase (RTDFacetedSearch ):
47
- facets = {
48
- 'project' : TermsFacet (field = 'project' ),
49
- 'version' : TermsFacet (field = 'version' )
50
- }
51
- doc_types = [PageDocument ]
52
- index = PageDocument ._doc_type .index
53
- fields = ['title^10' , 'headers^5' , 'content' ]
54
-
55
- def query (self , search , query ):
56
- """Use a custom SimpleQueryString instead of default query."""
57
-
58
- search = super ().query (search , query )
59
74
60
75
all_queries = []
61
76
62
77
# need to search for both 'and' and 'or' operations
63
78
# the score of and should be higher as it satisfies both or and and
64
- for operator in ['AND ' , 'OR ' ]:
79
+ for operator in ['and ' , 'or ' ]:
65
80
query_string = SimpleQueryString (
66
81
query = query , fields = self .fields , default_operator = operator
67
82
)
@@ -74,23 +89,50 @@ def query(self, search, query):
74
89
return search
75
90
76
91
77
- class PageSearch (SettingsOverrideObject ):
78
-
79
- """
80
- Allow this class to be overridden based on CLASS_OVERRIDES setting.
81
-
82
- This is primary used on the .com to adjust how we filter our search queries
83
- """
92
+ class DomainSearch (RTDFacetedSearch ):
93
+ facets = {
94
+ 'project' : TermsFacet (field = 'project' ),
95
+ 'version' : TermsFacet (field = 'version' ),
96
+ 'doc_type' : TermsFacet (field = 'doc_type' ),
97
+ }
98
+ signal = before_domain_search
99
+ doc_types = [DomainDocument ]
100
+ index = DomainDocument ._doc_type .index
101
+ fields = ('display_name^5' , 'name' )
84
102
85
- _default_class = PageSearchBase
86
103
104
+ class ProjectSearch (RTDFacetedSearch ):
105
+ facets = {
106
+ 'language' : TermsFacet (field = 'language' )
107
+ }
108
+ signal = before_project_search
109
+ doc_types = [ProjectDocument ]
110
+ index = ProjectDocument ._doc_type .index
111
+ fields = ('name^10' , 'slug^5' , 'description' )
87
112
88
- class ProjectSearch (SettingsOverrideObject ):
89
113
90
- """
91
- Allow this class to be overridden based on CLASS_OVERRIDES setting.
114
+ class PageSearchBase (RTDFacetedSearch ):
115
+ facets = {
116
+ 'project' : TermsFacet (field = 'project' ),
117
+ 'version' : TermsFacet (field = 'version' )
118
+ }
119
+ doc_types = [PageDocument ]
120
+ index = PageDocument ._doc_type .index
121
+ fields = ['title^10' , 'headers^5' , 'content' ]
92
122
93
- This is primary used on the .com to adjust how we filter our search queries
94
- """
95
123
96
- _default_class = ProjectSearchBase
124
+ class AllSearch (RTDFacetedSearch ):
125
+ facets = {
126
+ 'project' : TermsFacet (field = 'project' ),
127
+ 'version' : TermsFacet (field = 'version' ),
128
+ 'language' : TermsFacet (field = 'language' ),
129
+ 'doc_type' : TermsFacet (field = 'doc_type' ),
130
+ 'index' : TermsFacet (field = '_index' ),
131
+ }
132
+ signal = before_file_search
133
+ doc_types = [DomainDocument , PageDocument , ProjectDocument ]
134
+ index = [DomainDocument ._doc_type .index ,
135
+ PageDocument ._doc_type .index ,
136
+ ProjectDocument ._doc_type .index ]
137
+ fields = ('title^10' , 'headers^5' , 'content' , 'name^20' ,
138
+ 'slug^5' , 'description' , 'display_name^5' )
0 commit comments