From 1e4e6d45cc6f1304fd76015a0c6136e73ad32268 Mon Sep 17 00:00:00 2001 From: electimon Date: Fri, 26 Jul 2024 12:29:03 -0400 Subject: [PATCH 1/5] add search() Signed-off-by: electimon --- couchdb/client.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/couchdb/client.py b/couchdb/client.py index 6125e831..2be4a0bc 100644 --- a/couchdb/client.py +++ b/couchdb/client.py @@ -1144,6 +1144,23 @@ def changes(self, **opts): else: _, _, data = self.resource.get_json('_changes', **opts) return data + + def search(self, name, wrapper=None, **options): + """Execute a predefined search query based on index. + + :param name: the name of the index; for custom views, use the format + ``design_docid/indexname``, that is, the document ID of the + design document and the name of the index, separated by a + slash + :param wrapper: an optional callable that should be used to wrap the + result rows + :param options: optional query string parameters + :return: the view results + :rtype: `ViewResults` + """ + path = _path_from_name(name, '_search') + return PermanentView(self.resource(*path), '/'.join(path), + wrapper=wrapper)(**options) def _doc_resource(base, doc_id): From 295ba02b65010fb89abf992cbce64691b8ab2751 Mon Sep 17 00:00:00 2001 From: electimon Date: Fri, 26 Jul 2024 12:31:18 -0400 Subject: [PATCH 2/5] encode query --- couchdb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchdb/client.py b/couchdb/client.py index 2be4a0bc..88229f67 100644 --- a/couchdb/client.py +++ b/couchdb/client.py @@ -1288,7 +1288,7 @@ def _encode_view_options(options): """ retval = {} for name, value in options.items(): - if name in ('key', 'startkey', 'endkey') \ + if name in ('key', 'startkey', 'endkey', 'query', 'q') \ or not isinstance(value, util.strbase): value = json.encode(value) retval[name] = value From 71f68ff1d7c091a3423288f23f30fe626d19fdc9 Mon Sep 17 00:00:00 2001 From: electimon Date: Fri, 26 Jul 2024 12:43:23 -0400 Subject: [PATCH 3/5] Revert "encode query" This reverts commit 295ba02b65010fb89abf992cbce64691b8ab2751. --- couchdb/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/couchdb/client.py b/couchdb/client.py index 88229f67..2be4a0bc 100644 --- a/couchdb/client.py +++ b/couchdb/client.py @@ -1288,7 +1288,7 @@ def _encode_view_options(options): """ retval = {} for name, value in options.items(): - if name in ('key', 'startkey', 'endkey', 'query', 'q') \ + if name in ('key', 'startkey', 'endkey') \ or not isinstance(value, util.strbase): value = json.encode(value) retval[name] = value From f6e718d4e865e54d510bdbe834010a13ae564349 Mon Sep 17 00:00:00 2001 From: electimon Date: Fri, 26 Jul 2024 17:15:31 -0400 Subject: [PATCH 4/5] Add fields to Row() Signed-off-by: electimon --- couchdb/client.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/couchdb/client.py b/couchdb/client.py index 2be4a0bc..52ab25d2 100644 --- a/couchdb/client.py +++ b/couchdb/client.py @@ -1471,6 +1471,9 @@ def doc(self): if doc: return Document(doc) + @property + def fields(self): + return self.get('fields') class Indexes(object): """Manage indexes in CouchDB 2.0.0 and later. From db14e2b67164e9e026b9f1befad155de3adedf7a Mon Sep 17 00:00:00 2001 From: electimon Date: Sun, 9 Feb 2025 03:28:15 +0000 Subject: [PATCH 5/5] nouveau, and fixed config() Signed-off-by: electimon --- couchdb/client.py | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/couchdb/client.py b/couchdb/client.py index 52ab25d2..29bcdd03 100644 --- a/couchdb/client.py +++ b/couchdb/client.py @@ -141,7 +141,8 @@ def __getitem__(self, name): :rtype: `Database` :raise ResourceNotFound: if no database with that name exists """ - db = Database(self.resource(name), name) + uses_nouveau = self.config().get('nouveau', {}).get('enable', False) + db = Database(self.resource(name), name, uses_nouveau=uses_nouveau) db.resource.head() # actually make a request to the database return db @@ -154,7 +155,7 @@ def config(self): :rtype: `dict` """ - status, headers, data = self.resource.get_json('_config') + status, headers, data = self.resource('_node', '_local').get_json('_config') return data def version(self): @@ -371,7 +372,7 @@ class Database(object): >>> db.resource.session.disable_ssl_verification() """ - def __init__(self, url, name=None, session=None): + def __init__(self, url, name=None, session=None, uses_nouveau=False): if isinstance(url, util.strbase): if not url.startswith('http'): url = DEFAULT_BASE_URL + url @@ -379,6 +380,7 @@ def __init__(self, url, name=None, session=None): else: self.resource = url self._name = name + self.uses_nouveau = uses_nouveau def __repr__(self): return '<%s %r>' % (type(self).__name__, self.name) @@ -1158,10 +1160,12 @@ def search(self, name, wrapper=None, **options): :return: the view results :rtype: `ViewResults` """ - path = _path_from_name(name, '_search') + if self.uses_nouveau: + path = _path_from_name(name, '_nouveau') + else: + path = _path_from_name(name, '_search') return PermanentView(self.resource(*path), '/'.join(path), - wrapper=wrapper)(**options) - + wrapper=wrapper, uses_nouveau=self.uses_nouveau)(**options) def _doc_resource(base, doc_id): """Return the resource for the given document id. @@ -1216,12 +1220,13 @@ def rev(self): class View(object): """Abstract representation of a view or query.""" - def __init__(self, url, wrapper=None, session=None): + def __init__(self, url, wrapper=None, session=None, uses_nouveau=False): if isinstance(url, util.strbase): self.resource = http.Resource(url, session) else: self.resource = url self.wrapper = wrapper + self.uses_nouveau = uses_nouveau def __call__(self, **options): return ViewResults(self, options) @@ -1236,8 +1241,8 @@ def _exec(self, options): class PermanentView(View): """Representation of a permanent view on the server.""" - def __init__(self, uri, name, wrapper=None, session=None): - View.__init__(self, uri, wrapper=wrapper, session=session) + def __init__(self, uri, name, wrapper=None, session=None, uses_nouveau=False): + View.__init__(self, uri, wrapper=wrapper, session=session, uses_nouveau=uses_nouveau) self.name = name def __repr__(self): @@ -1252,8 +1257,8 @@ class TemporaryView(View): """Representation of a temporary view.""" def __init__(self, uri, map_fun, reduce_fun=None, - language='javascript', wrapper=None, session=None): - View.__init__(self, uri, wrapper=wrapper, session=session) + language='javascript', wrapper=None, session=None, uses_nouveau=False): + View.__init__(self, uri, wrapper=wrapper, session=session, uses_nouveau=uses_nouveau) if isinstance(map_fun, FunctionType): map_fun = getsource(map_fun).rstrip('\n\r') self.map_fun = dedent(map_fun.lstrip('\n\r')) @@ -1380,8 +1385,12 @@ def __len__(self): def _fetch(self): data = self.view._exec(self.options) wrapper = self.view.wrapper or Row - self._rows = [wrapper(row) for row in data['rows']] - self._total_rows = data.get('total_rows') + if self.view.uses_nouveau: + self._rows = [wrapper(row) for row in data['hits']] + self._total_rows = data.get('total_hits') + else: + self._rows = [wrapper(row) for row in data['rows']] + self._total_rows = data.get('total_rows') self._offset = data.get('offset', 0) self._update_seq = data.get('update_seq')