浏览代码

HUE-2857 [search] Update selected document in the index

Romain Rigaux 10 年之前
父节点
当前提交
4da308aedb

+ 1 - 1
apps/search/src/search/models.py

@@ -812,7 +812,7 @@ def augment_solr_response(response, collection, query):
       doc[field] = escaped_value
 
     if not query.get('download'):
-      doc['showDetails'] = False
+      doc['externalLink'] = doc.get('doc-link') in ['hbase', 'hdfs'] and 'doc-link' in doc.keys()
       doc['details'] = []
 
   highlighted_fields = response.get('highlighting', {}).keys()

+ 32 - 6
apps/search/src/search/static/search/js/search.ko.js

@@ -1364,9 +1364,9 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
               var leafletmap = {};
               $.each(data.response.docs, function (index, item) {
                 var row = [];
-                var _showDetails = item.showDetails;
+                var _externalLink = item.externalLink;
                 var _details = item.details;
-                delete item["showDetails"];
+                delete item["externalLink"];
                 delete item["details"];
                 var fields = self.collection.template.fieldsSelected();
                 // Display selected fields or whole json document
@@ -1387,8 +1387,11 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
                 var doc = {
                   'id': item[self.collection.idField()],
                   'row': row,
-                  'showDetails': ko.observable(_showDetails),
+                  'showEdit': ko.observable(false),
+                  'hasChanged': ko.observable(false),
+                  'externalLink': ko.observable(_externalLink),
                   'details': ko.observableArray(_details),
+                  'showDetails': ko.observable(false),
                   'leafletmap': leafletmap
                 };
                 _docs.push(doc);
@@ -1487,10 +1490,16 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
     }, function (data) {
       if (data.status == 0) {
         $.each(data.doc.doc, function(key, val) {
-            doc['details'].push(ko.mapping.fromJS({
+          var _field = ko.mapping.fromJS({
               key: key,
-              value: val
-          }));
+              value: val,
+              hasChanged: false
+          });
+          _field.value.subscribe(function() {
+            doc.hasChanged(true);
+            _field.hasChanged(true);
+          });
+          doc['details'].push(_field);
         });
       }
       else if (data.status == 1) {
@@ -1508,6 +1517,23 @@ var SearchViewModel = function (collection_json, query_json, initial_json) {
     });
   };
 
+  self.updateDocument = function (doc) {
+    $.post("/search/update_document", {
+      collection: ko.mapping.toJSON(self.collection),
+      document: ko.mapping.toJSON(doc),
+      id: doc.id
+    }, function (data) {
+      if (data.status == 0) {
+        doc.showEdit(false);
+      }
+      else {
+        $(document).trigger("error", data.message);
+      }
+    }).fail(function (xhr, textStatus, errorThrown) {
+      $(document).trigger("error", xhr.responseText);
+    });
+  };
+
   self.showFieldAnalysis = function() {
     if (self.fieldAnalysesName()) {
       var analyse = self.getFieldAnalysis();

+ 12 - 1
apps/search/src/search/templates/search.mako

@@ -41,6 +41,7 @@ ${ commonheader(_('Search'), "search", user, "80px") | n,unicode }
     <a class="btn pointer" title="${ _('Player mode') }" rel="tooltip" data-placement="bottom" data-bind="click: function(){ $root.isEditing(false); $root.isPlayerMode(true); }">
       <i class="fa fa-expand"></i>
     </a>
+    &nbsp;&nbsp;
     <a class="btn pointer" title="${ _('Edit') }" rel="tooltip" data-placement="bottom" data-bind="click: toggleEditing, css: {'btn': true, 'btn-inverse': isEditing}">
       <i class="fa fa-pencil"></i>
     </a>
@@ -615,11 +616,21 @@ ${ dashboard.layout_skeleton() }
                   <!-- /ko -->
                   <!-- ko if: $data.details().length > 0 -->
                     <div class="document-details">
+                      <a href="javascript:void(0)" data-bind="click: function() { showEdit(true); }">
+                        <i class="fa fa-edit" data-bind="visible: ! showEdit()"></i>
+                      </a>
+                      <a href="javascript:void(0)" data-bind="click: $root.updateDocument">
+                        <i class="fa fa-save" data-bind="visible: showEdit"></i>
+                      </a>
+                      <i class="fa fa-external-link" data-bind="visible: externalLink"></i>
                       <table>
                         <tbody data-bind="foreach: details">
                           <tr>
                              <th style="text-align: left; white-space: nowrap; vertical-align:top; padding-right:20px" data-bind="text: key"></th>
-                             <td width="100%" data-bind="text: value"></td>
+                             <td width="100%">
+                               <span data-bind="text: value, visible: ! $parent.showEdit()"></span>
+                               <input data-bind="value: value, visible: $parent.showEdit" class="input-xxlarge"></input>
+                             </td>
                           </tr>
                         </tbody>
                       </table>

+ 1 - 0
apps/search/src/search/urls.py

@@ -33,6 +33,7 @@ urlpatterns = patterns('search.views',
   url(r'^index/fields/dynamic$', 'index_fields_dynamic', name='index_fields_dynamic'),
   url(r'^template/new_facet$', 'new_facet', name='new_facet'),
   url(r'^get_document$', 'get_document', name='get_document'),
+  url(r'^update_document$', 'update_document', name='update_document'),
   url(r'^get_range_facet$', 'get_range_facet', name='get_range_facet'),
   url(r'^get_timeline$', 'get_timeline', name='get_timeline'),
   url(r'^get_collection$', 'get_collection', name='get_collection'),

+ 31 - 0
apps/search/src/search/views.py

@@ -348,6 +348,37 @@ def get_document(request):
   return JsonResponse(result)
 
 
+@allow_viewer_only
+def update_document(request):
+  result = {'status': -1, 'message': 'Error'}
+
+  try:
+    collection = json.loads(request.POST.get('collection', '{}'))
+    document = json.loads(request.POST.get('document', '{}'))
+    doc_id = request.POST.get('id')
+
+    if document['hasChanged']:
+      edits = {
+          "id": doc_id,
+      }
+      version = None # If there is a version, use it to avoid potential concurrent update conflicts
+
+      for field in document['details']:
+        if field['hasChanged']:
+          edits[field['key']] = {"set": field['value']}
+#        if field['key'] == '_version_': # Commented until HUE-2870
+#          version = field['value']
+
+      if SolrApi(SOLR_URL.get(), request.user).update(collection['name'], json.dumps([edits]), content_type='json', version=version):
+        result['status'] = 0
+        result['message'] = _('Document successfully updated.')
+
+  except Exception, e:
+    result['message'] = force_unicode(e)
+
+  return JsonResponse(result)
+
+
 @allow_viewer_only
 def get_stats(request):
   result = {'status': -1, 'message': 'Error'}

+ 10 - 9
desktop/libs/libsolr/src/libsolr/api.py

@@ -668,24 +668,25 @@ class SolrApi(object):
     except RestException, e:
       raise PopupException(e, title=_('Error while accessing Solr'))
 
-  def update(self, collection_or_core_name, data, content_type='csv'):
+  def update(self, collection_or_core_name, data, content_type='csv', version=None):
     try:
       if content_type == 'csv':
-        params = self._get_params() + (
-          ('wt', 'json'),
-          ('overwrite', 'true'),
-        )
         content_type = 'application/csv'
       elif content_type == 'json':
-        params = self._get_params() + (
-          ('wt', 'json'),
-          ('overwrite', 'true'),
-        )
         content_type = 'application/json'
       else:
         LOG.error("Could not update index for %s. Unsupported content type %s. Allowed content types: csv" % (collection_or_core_name, content_type))
         return False
 
+      params = self._get_params() + (
+          ('wt', 'json'),
+          ('overwrite', 'true'),
+      )
+      if version is not None:
+        params += (
+          ('_version_', version),
+          ('versions', 'true')
+        )
       self._root.post('%s/update' % collection_or_core_name, contenttype=content_type, params=params, data=data)
       return True
     except RestException, e: