从 rethinkdb 数据库获取数据,操作所述数据,然后使用操作文档更新数据库

Getting data from rethinkdb database, manipulating said data, then updating the database with the manipulated docs

我希望对结果进行获取,运行 一个函数,该函数将通过更新字段进行一些操作,然后将该文档放回数据库中。真的,我的问题是能够将多个数据库调用链接在一起。过去一周左右,我一直在为此苦苦挣扎。任何建议表示赞赏,谢谢。

以下是我迄今为止尝试过的方法,但我收到错误消息:

function geocode_cleanup(request, response, next) {
  r.table('dealer_locations').filter(r.row('geodata').match('No geodata found.'))
    .do(function(row) {
      var geodata = opencage_geocoder.geocode(row.Address, function(error, response) {
        if (error) {
          console.log("Error.");
          row.geodata = "No geodata found.";
          row.active = true;
        } else if (response.length == 0) {
          console.log("Empty response.");
        } else {
          console.log("Success.");
          console.log(response);
          var latitude = response[0].latitude;
          var longitude = response[0].longitude;
          row.geodata = r.point(longitude, latitude);
          row.active = true;
        }
      });
      return r.table('dealer_locations').update({
        geodata: geodata
      })
    }).run(conn, function(error, cursor) {
      response.setHeader("Content-Type", "application/json");
      if (error) {
        handleError(response, error);
      } else {
        cursor.toArray(function(error, results) {
          if (error) {
            handleError(response, error);
          } else {
            response.send(results);
          };
        });
      }
      next();
    })
};

此外,这给出了响应中返回的所需结果,但第二个数据库操作从未发生,因为我仍然在同一个数据库连接中,我认为:

function geocode_cleanup(request, response, next) {
    var conn = request._rdbConn;
    r.table('dealer_locations').filter({geodata: "No geodata found."}).run(conn, function(error, cursor) {
      if (error) {
        handleError(response, error);
      } else {
        cursor.toArray(function(error, results) {
          if (error) {
            handleError(response, error);
          } else {
            var i = 1;
            async.forEach(results, function(item, callback) {
            var address = (item.Address + " " + item.City).toString();
            opencage_geocoder.geocode(address, function(err, res) {
              if (err) {
                console.log(i);
                console.log("Error.");
                item.id = i;
                item.geodata = "No geodata found.";
                item.active = true;
                i++;
                callback();
              } else if (res.length == 0) {
                  console.log(i);
                  console.log("Empty response.");
                  i++;
                  callback();
                } else {
                    console.log(i);
                    console.log("Success.");
                    console.log(res);
                    var latitude = res[0].latitude;
                    console.log(i + " " + latitude);
                    var longitude = res[0].longitude;
                    console.log(i + " " + longitude);
                    item.id = i;
                    item.geodata = r.point(longitude, latitude);
                    item.active = true;
                    i++;
                    callback();
                }
              });
            }, function() {
              r.table('dealer_locations').insert(results, {
                conflict: "replace"
              }).run(request._rdbConn, function(error, results) {
                if (error) {
                  console.log("Data not inserted!");
                } else {
                  console.log("Data inserted!");
                }
              });
              console.log("Done!");
              response.send(results);
            });
          }
        })
      }
    })
  }

好的,我有一个建议。这会查询您感兴趣的文档,修改它们(在您的应用程序服务器上,而不是在数据库中),然后使用漂亮的 conflict: 'update' 选项重新插入它们。它还使用了 promises,因为我认为这样更简洁。

function geocode_cleanup(request, response, next) {
    r.table('dealer_locations')
        .filter(r.row('geodata').match('No geodata found.'))
        .run(conn).then(function(cursor) {
            var to_update = [];
            return cursor.toArray().then(function getGeocodes(rows) {
                return rows.map(function getGeocode(row) {
                    row.geodata = opencage_geocoder.geocode(row.Address, function(error, response) {
                        if (error) {
                            console.log("Error.");
                            row.geodata = "No geodata found.";
                            row.active = true;
                        } else if (response.length == 0) {
                            console.log("Empty response.");
                        } else {
                            console.log("Success.");
                            console.log(response);
                            var latitude = response[0].latitude;
                            var longitude = response[0].longitude;
                            row.geodata = r.point(longitude, latitude);
                            row.active = true;
                        }
                    });
                    return row;
                });
            });
        }).then(function doneGeocoding(modified_rows){
            return r.table('dealer_locations')
                .insert(modified_rows, {conflict: "update", return_changes: true})('changes')
                .coerceTo('array')
                .run(conn);
        }).then(function finishResponse(changes){
            response.setHeader("Content-Type", "application/json");
            response.send(results);
            next();
        }).catch(function(err) {
            // handle errors here
        });
};

注意买者,我没有运行这个,所以可能有语法错误之类的东西

这里有一个可能的解决方案,它使用 promises 来稍微组织一下代码。

// Guarantee support for promises and provide the `promisify` function
var Promise = require('bluebird');
// Promisify the geocode function to make it easier to use
var geocode = Promise.promisify(opencage_geocoder.geocode);

function geocode_cleanup(request, response, next) {
  var conn = request._rdbConn;
  r
    .table('dealer_locations')
    .filter(r.row('geodata').match('No geodata found.'))
    .coerceTo('array')
    .run(conn)
    .then(function(rows) {
      // This promise will be resolve when all rows have been geocoded and updated
      // We map the rows into an array of promises, which is what Promise.all takes
      return Promise.all(rows.map(function (row) {
        return geocode(row.Address)
          .then(function (response) {
            console.log("Success.");
            var latitude = response[0].latitude;
            var longitude = response[0].longitude;
            row.geodata = r.point(longitude, latitude);
            row.active = true;
            // Return the row
            return row;
          });
        });
      }));
    })
    .then(function (rows) {
      // Now that all `dealer_locations` have been updated, re-query them
      return r
        .table('dealer_locations')
        .insert(rows, {conflict: "update", return_changes: true})
        .run(conn);
    })
    .then(function (results) {
      // Send the response;
      response.setHeader("Content-Type", "application/json");
      response.send(results);
      return;
    })
    .catch(function (err) {
      return handleError(null, error);
    })
};

我注意到您的代码存在一些问题:

1.使用 do

 r.table('dealer_locations').filter(r.row('geodata').match('No geodata found.'))
    .do(function(row) {
      var geodata = opencage_geocoder.geocode ...
    })

在此代码段中,您使用了 inside of do 的 JS 函数。你不能那样做。请记住,do 内部发生的事情发生在 RethinkDB 服务器中(而不是在您的 Node.js 服务器中)。您的 RethinkDB 服务器不知道您的 opencage_geocoder 函数,因此这不会起作用。

任何 do returns 必须是有效的 ReQL 查询或 ReQL 表达式。您不能在其中执行任意 JavaScript。

如果你想 运行 JavaScript 查询结果,你必须 .run 查询,然后在回调中做任何你想做的事情,或者 .then 功能。届时,该代码将在 JavaScript 中执行,而不是在您的 RethinkDB 服务器中执行。

2。使用 update

return r.table('dealer_locations').update({
  geodata: geodata
})

update 方法只能更新单个文档。您不能将一组文档传递给它。在这种情况下,您需要做 r.table().get().update() 才能使其工作,因为当您 update 某事时,您必须引用单个文档。

如果您有一系列要更新的文档,可以使用 forEach 方法。

r.table('hello')
 .merge({
   'new_property': 'hello!'
 })
 .forEach(function (row)  {
   // Insert that property into the document
   return r.table('hello').get(row.id).update(row);
 })

您也可以这样做(您已经在这样做):

r.table('hello')
 .merge({
   'new_property': 'hello!'
 })
 .do(function (rows)  {
   // Insert that property into the document
   return r.table('hello')
     .insert(rows, {conflict: "update", return_changes: true});
 })