在对话流中从 firebase 数据库中检索值

Retrieve values from firebase database in conversation flow

在我的对话流中调用特定意图后,我试图从我的 firebase 数据库中获取信息。 我正在尝试制作一个函数,该函数采用用户 ID 参数,然后将获得该用户的高分,然后将用户高分返回给他们。

app.intent('get-highscore', (conv) => {
  var thisUsersHighestscore = fetchHighscoreByUserId(conv.user.id);

  conv.ask('your highest score is ${thisUsersHighestScore}, say continue to keep playing.');

});



function fetchHighscoreByUserId(userId){
  var highscoresRef = database.ref("highscores");
  var thisUsersHighscore;

  highscoresRef.on('value',function(snap){

    var allHighscores= snap.val();
    thisUsersHighscore = allHighscores.users.userId.highscore;

  });
  return thisUsersHighscore;

}

数据库中的数据示例:

 "highscores" : {
    "users" : {
      "1539261356999999924819020" : {
        "highscore" : 2,
        "nickname" : "default"
      },
      "15393362381293223232222738" : {
        "highscore" : 78,
        "nickname" : "quiz master"
      },
      "15393365724084067696560" : {
        "highscore" : "32",
        "nickname" : "cutie pie"
      },
      "45343453535534534353" : {
        "highscore" : 1,
        "nickname" : "friendly man"
      }
    }
  }

它似乎从未为 thisUsersHighScore in 我的函数设置任何值。

您在这里遇到了很多问题 - 包括您如何使用 Firebase、如何在 Google 上使用 Actions 以及如何使用 Javascript。其中一些问题只是您可以更好、更有效地做事,而其他问题则导致了实际问题。

访问 JavaScript

中的结构中的值

第一个问题是allHighscores.users.userId.highscore表示"In an object named 'allHighscores', get the property named 'users', from the result of that, get the property named 'userId'"。但是没有 属性 命名为 "userId" - 只有一堆以数字命名的属性。

您可能想要更像 allHighscores.users[userId].highscore 的东西,这意味着 "In an object named 'allHighscores', get the property named 'users', fromt he result of that, get the property named by the value of 'userId'"。

但是如果这个有几千条或者几十万条记录,这会占用很多内存。并且会花费很多时间从 Firebase 获取。如果您直接从 Firebase 获取那条记录不是更好吗?

两个 Firebase 问题

从上面看,您应该只从 Firebase 获取一条记录,而不是整个 table,然后搜索您想要的一条记录。在 firebase 中,这意味着您获得对所需数据路径的引用,然后请求该值。

要指定你想要的路径,你可以做类似的事情

var userRef = database.ref("highscores/users").child(userId);
var userScoreRef = userRef.child( "highscore" );

(当然,您可以将它们放在一个语句中。为了清楚起见,我将它们分解成这样。)

但是,一旦您有了引用,您就想读取该引用中的数据。你这里有两个问题。

  1. 您正在使用 on() 方法,该方法获取一次值,但随后还会设置一个回调,以便在每次分数更新时调用。你可能不需要后者,所以你可以使用 once() 方法一次获取值。

  2. 你有一个回调函数设置来获取值(这很好,因为这是一个异步操作,这是 Javascript 中处理异步操作的传统方式),但您 return 在该回调 之外 计算一个值。所以你总是 returning 一个空值。

这些表明您还需要使 fetchHighScoreByUserId() 成为一个异步函数,而我们现在必须这样做的方法是 return 一个 Promise。当异步函数完成时,此 Promise 将解析为实际值。幸运的是,Firebase 库可以 return 一个 Promise,我们可以在响应中作为 .then() 子句的一部分获取它的值,因此我们可以简化很多事情。 (我强烈建议您阅读 Javascript 中的 Promises 以及如何使用它们。)它可能看起来像这样:

return userScoreRef.once("value")
  .then( function(scoreSnapshot){
    var score = scoreSnapshot.val();
    return score;
  } );

Google

上的异步函数和操作

在 Intent Handler 中,您遇到了与上述类似的问题。对 fetchHighScoreByUserId() 的调用是异步的,因此当您从功能。 AoG 需要知道等待异步调用完成。它怎么做到的?再次承诺!

AoG Intent Handlers 必须 return Promise 如果涉及异步调用。

由于修改后的 fetchHighScoreByUserId() return 是一个 Promise,我们将利用它。我们还将在 Promise 链的 .then() 部分设置我们的响应。它可能看起来像这样:

app.intent('get-highscore', (conv) => {
  return fetchHighscoreByUserId(conv.user.id)
    .then( function(highScore){
      conv.ask(`Your highest score is ${highScore}. Do you want to play again?`);
    } );
});

这里有两个旁白:

  1. 如果你想像那样使用 ${highScore},你需要使用反引号“`”来定义字符串。

  2. 短语 "Say continue if you want to play again." 是一个非常糟糕的语音用户界面。更好的是直接问他们是否想再玩