Skip to content
This repository was archived by the owner on Aug 30, 2024. It is now read-only.

Commit c99cc85

Browse files
committed
Merge pull request #172 from cloudant/159-search-index-management
Cloudant Search index management
2 parents d30a5be + 4b2a9b5 commit c99cc85

File tree

5 files changed

+566
-18
lines changed

5 files changed

+566
-18
lines changed

CHANGES.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
2.1.0 (Unreleased)
22
==================
33
- [NEW] Added support for Cloudant Search execution.
4-
4+
- [NEW] Added support for Cloudant Search index management.
55

66
2.0.3 (2016-06-03)
77
==================

src/cloudant/design_document.py

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,25 @@
1515
"""
1616
API module/class for interacting with a design document in a database.
1717
"""
18-
from ._2to3 import iteritems_
18+
from ._2to3 import iteritems_, STRTYPE
19+
from ._common_util import QUERY_LANGUAGE, codify
1920
from .document import Document
2021
from .view import View, QueryIndexView
2122
from .error import CloudantArgumentError, CloudantException
22-
from ._common_util import QUERY_LANGUAGE
2323

2424
class DesignDocument(Document):
2525
"""
2626
Encapsulates a specialized version of a
2727
:class:`~cloudant.document.Document`. A DesignDocument object is
2828
instantiated with a reference to a database and
29-
provides an API to view management, list and show
30-
functions, search indexes, etc. When instantiating a DesignDocument or
29+
provides an API to view management, index management, list and show
30+
functions, etc. When instantiating a DesignDocument or
3131
when setting the document id (``_id``) field, the value must start with
3232
``_design/``. If it does not, then ``_design/`` will be prepended to
3333
the provided document id value.
3434
35-
Note: Currently only the view management API exists. Remaining design
36-
document functionality will be added later.
35+
Note: Currently only the view management and search index management API
36+
exists. Remaining design document functionality will be added later.
3737
3838
:param database: A database instance used by the DesignDocument. Can be
3939
either a ``CouchDatabase`` or ``CloudantDatabase`` instance.
@@ -45,6 +45,7 @@ def __init__(self, database, document_id=None):
4545
document_id = '_design/{0}'.format(document_id)
4646
super(DesignDocument, self).__init__(database, document_id)
4747
self.setdefault('views', dict())
48+
self.setdefault('indexes', dict())
4849

4950
@property
5051
def views(self):
@@ -56,6 +57,17 @@ def views(self):
5657
"""
5758
return self.get('views')
5859

60+
@property
61+
def indexes(self):
62+
"""
63+
Provides an accessor property to the indexes dictionary in the
64+
locally cached DesignDocument.
65+
66+
:returns: Dictionary containing index names and index objects
67+
as key/value
68+
"""
69+
return self.get('indexes')
70+
5971
def add_view(self, view_name, map_func, reduce_func=None, **kwargs):
6072
"""
6173
Appends a MapReduce view to the locally cached DesignDocument View
@@ -79,6 +91,26 @@ def add_view(self, view_name, map_func, reduce_func=None, **kwargs):
7991
view = View(self, view_name, map_func, reduce_func, **kwargs)
8092
self.views.__setitem__(view_name, view)
8193

94+
def add_search_index(self, index_name, search_func, analyzer=None):
95+
"""
96+
Appends a Cloudant search index to the locally cached DesignDocument
97+
indexes dictionary.
98+
99+
:param str index_name: Name used to identify the search index.
100+
:param str search_func: Javascript search index function.
101+
:param analyzer: Optional analyzer for this search index.
102+
"""
103+
if self.get_index(index_name) is not None:
104+
msg = ('An index with name {0} already exists in this design doc'
105+
.format(index_name))
106+
raise CloudantArgumentError(msg)
107+
if analyzer is not None:
108+
search = {'index': codify(search_func), 'analyzer': analyzer}
109+
else:
110+
search = {'index': codify(search_func)}
111+
112+
self.indexes.__setitem__(index_name, search)
113+
82114
def update_view(self, view_name, map_func, reduce_func=None, **kwargs):
83115
"""
84116
Modifies/overwrites an existing MapReduce view definition in the
@@ -104,6 +136,27 @@ def update_view(self, view_name, map_func, reduce_func=None, **kwargs):
104136
view = View(self, view_name, map_func, reduce_func, **kwargs)
105137
self.views.__setitem__(view_name, view)
106138

139+
def update_search_index(self, index_name, search_func, analyzer=None):
140+
"""
141+
Modifies/overwrites an existing Cloudant search index in the
142+
locally cached DesignDocument indexes dictionary.
143+
144+
:param str index_name: Name used to identify the search index.
145+
:param str search_func: Javascript search index function.
146+
:param analyzer: Optional analyzer for this search index.
147+
"""
148+
search = self.get_index(index_name)
149+
if search is None:
150+
msg = ('An index with name {0} does not exist in this design doc'
151+
.format(index_name))
152+
raise CloudantArgumentError(msg)
153+
if analyzer is not None:
154+
search = {'index': codify(search_func), 'analyzer': analyzer}
155+
else:
156+
search = {'index': codify(search_func)}
157+
158+
self.indexes.__setitem__(index_name, search)
159+
107160
def delete_view(self, view_name):
108161
"""
109162
Removes an existing MapReduce view definition from the locally cached
@@ -123,6 +176,19 @@ def delete_view(self, view_name):
123176

124177
self.views.__delitem__(view_name)
125178

179+
def delete_index(self, index_name):
180+
"""
181+
Removes an existing index in the locally cached DesignDocument
182+
indexes dictionary.
183+
184+
:param str index_name: Name used to identify the index.
185+
"""
186+
index = self.get_index(index_name)
187+
if index is None:
188+
return
189+
190+
self.indexes.__delitem__(index_name)
191+
126192
def fetch(self):
127193
"""
128194
Retrieves the remote design document content and populates the locally
@@ -154,6 +220,11 @@ def fetch(self):
154220
**view_def
155221
)
156222

223+
if not self.indexes:
224+
# Ensure indexes dict exists in locally cached DesignDocument.
225+
self.setdefault('indexes', dict())
226+
227+
# pylint: disable-msg=too-many-branches
157228
def save(self):
158229
"""
159230
Saves changes made to the locally cached DesignDocument object's data
@@ -180,11 +251,36 @@ def save(self):
180251
# Ensure empty views dict is not saved remotely.
181252
self.__delitem__('views')
182253

254+
if self.indexes:
255+
if self.get('language', None) != QUERY_LANGUAGE:
256+
for index_name, search in self.iterindexes():
257+
# Check the instance of the javascript search function
258+
if not isinstance(search['index'], STRTYPE):
259+
msg = (
260+
'Function for search index {0} must '
261+
'be of type string.'
262+
).format(index_name)
263+
raise CloudantException(msg)
264+
else:
265+
for index_name, index in self.iterindexes():
266+
if not isinstance(index['index'], dict):
267+
msg = (
268+
'Definition for query text index {0} must '
269+
'be of type dict.'
270+
).format(index_name)
271+
raise CloudantException(msg)
272+
else:
273+
# Ensure empty indexes dict is not saved remotely.
274+
self.__delitem__('indexes')
275+
183276
super(DesignDocument, self).save()
184277

185278
if not self.views:
186279
# Ensure views dict exists in locally cached DesignDocument.
187280
self.setdefault('views', dict())
281+
if not self.indexes:
282+
# Ensure indexes dict exists in locally cached DesignDocument.
283+
self.setdefault('indexes', dict())
188284

189285
def __setitem__(self, key, value):
190286
"""
@@ -216,6 +312,24 @@ def iterviews(self):
216312
for view_name, view in iteritems_(self.views):
217313
yield view_name, view
218314

315+
def iterindexes(self):
316+
"""
317+
Provides a way to iterate over the locally cached DesignDocument
318+
indexes dictionary.
319+
320+
For example:
321+
322+
.. code-block:: python
323+
324+
for index_name, search_func in ddoc.iterindexes():
325+
# Perform search index processing
326+
327+
:returns: Iterable containing index name and associated
328+
index object
329+
"""
330+
for index_name, search_func in iteritems_(self.indexes):
331+
yield index_name, search_func
332+
219333
def list_views(self):
220334
"""
221335
Retrieves a list of available View objects in the locally cached
@@ -225,6 +339,15 @@ def list_views(self):
225339
"""
226340
return list(self.views.keys())
227341

342+
def list_indexes(self):
343+
"""
344+
Retrieves a list of available indexes in the locally cached
345+
DesignDocument.
346+
347+
:returns: List of index names
348+
"""
349+
return list(self.indexes.keys())
350+
228351
def get_view(self, view_name):
229352
"""
230353
Retrieves a specific View from the locally cached DesignDocument by
@@ -236,6 +359,17 @@ def get_view(self, view_name):
236359
"""
237360
return self.views.get(view_name)
238361

362+
def get_index(self, index_name):
363+
"""
364+
Retrieves a specific index from the locally cached DesignDocument
365+
indexes dictionary by name.
366+
367+
:param str index_name: Name used to identify the index.
368+
369+
:returns: Index dictionary for the specified index name
370+
"""
371+
return self.indexes.get(index_name)
372+
239373
def info(self):
240374
"""
241375
Retrieves the design document view information data, returns dictionary

tests/unit/database_tests.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -253,8 +253,8 @@ def test_retrieve_design_document(self):
253253
"""
254254
# Get an empty design document object that does not exist remotely
255255
local_ddoc = self.db.get_design_document('_design/ddoc01')
256-
self.assertEqual(local_ddoc, {'_id': '_design/ddoc01', 'views': {}})
257-
256+
self.assertEqual(local_ddoc, {'_id': '_design/ddoc01', 'indexes': {},
257+
'views': {}})
258258
# Add the design document to the database
259259
map_func = 'function(doc) {\n emit(doc._id, 1); \n}'
260260
local_ddoc.add_view('view01', map_func)
@@ -856,6 +856,7 @@ def test_create_json_index(self):
856856
self.assertEqual(ddoc,
857857
{'_id': index.design_document_id,
858858
'_rev': ddoc['_rev'],
859+
'indexes': {},
859860
'language': 'query',
860861
'views': {index.name: {'map': {'fields': {'name': 'asc',
861862
'age': 'asc'}},

0 commit comments

Comments
 (0)