【问题标题】:Can you create dynamic firebase queries?您可以创建动态 Firebase 查询吗?
【发布时间】:2020-02-09 13:44:53
【问题描述】:

我想像这样查询 firebase,但它不允许我这样做。有没有办法动态查询firebase?我需要处理非常具体的情况——例如,如果用户选择“无关紧要”,那么我需要从查询中删除该首选项过滤器,一些字段是数组数据类型,一些是字符串,一些是数字。我需要能够处理所有形式的数据类型并适当地填充 where 子句。手动检查每个字段并调用每个字段将对系统产生负担(有超过 20 个字段可供查询)。任何建议都会很棒!

for(ddp in docDataPreferences){
  if(docDataPreferences[ddp] == "Doesn't Matter"){
    allDoesntMatterPreferences.push(ddp);
  } else if(docDataPreferences[ddp] != "Doesn't Matter" && docDataPreferences[ddp] != '' && docDataPreferences[ddp] != undefined) {
    keysWithValues.push(ddp);
    whereClause += ".where('preferences."+ ddp + "', 'array-contains-any', " + JSON.stringify(docDataPreferences[ddp]) +")"
  }
}

var usersMatchesCollection = config.db.collection("Users");
var query = usersMatchesCollection + whereClause;
await query.get().then( function (matchesQuerySnapshot) {
  matchesQuerySnapshot.forEach(function(doc) {
      //...do something
  })
})

TypeError:query.get 不是函数。 (在 'query.get()'、'query.get' 未定义)]

我可以看到 where 子句打印正确,但我假设您不能连接对象和字符串。当我像这样手动添加返回的 whereClause 时:

var query = usersMatchesCollection.where('preferences.datingPrefDistance', '==', 22).where('preferences.datingPrefDrinking', '==', "No").where('preferences.datingPrefEducation', '==', "Doctorate").where('preferences.datingPrefKids', '==', "No").where('preferences.datingPrefMarried', '==', "No").where('preferences.datingPrefSmokingCig', '==', "No").where('preferences.datingPrefSmokingPot', '==', "No").where('preferences.prefDrinking', '==', "No").where('preferences.prefEducation', '==', "Doctorate").where('preferences.prefIdentifyAs', '==', "Male").where('preferences.prefKids', '==', "No").where('preferences.prefMarried', '==', "No").where('preferences.prefSmokingPot', '==', "No")

它有效。因此,导致问题的是串联。

有解决办法吗?

【问题讨论】:

  • 我不确定我是否理解您共享的代码存在什么问题。你能解释一下当你运行这段代码时什么不起作用吗?
  • @FrankvanPuffelen 抱歉,我应该把我遇到的错误放在帖子里。我回来了 "TypeError: query.get is not a function. (In 'query.get()', 'query.get' is undefined)]" 。我也更新了帖子。我认为问题是我无法连接 whereClase 和 usersMatchesCollection。所以我的问题是 - 如果我无法连接,我如何能够进行动态 Firebase 查询?

标签: javascript firebase google-cloud-firestore


【解决方案1】:

您似乎正在尝试通过连接 CollectionReference 和字符串来构造查询,但这是行不通的。如果你console.log(query),你会看到它是一个字符串,而不是Query,并且由于字符串上没有get() 方法,这就解释了你得到的错误。

要构建具有动态条件的查询,您应该遵循以下模式:

var usersMatchesCollection = config.db.collection("Users");
var query = usersMatchesCollection;

for(ddp in docDataPreferences){
  if(docDataPreferences[ddp] == "Doesn't Matter"){
    allDoesntMatterPreferences.push(ddp);
  } else if(docDataPreferences[ddp] != "Doesn't Matter" && docDataPreferences[ddp] != '' && docDataPreferences[ddp] != undefined) {
    keysWithValues.push(ddp);
    query = query.where('preferences.'+ ddp, 'array-contains-any', docDataPreferences[ddp]))
  }
}

await query.get().then( function (matchesQuerySnapshot) {
  matchesQuerySnapshot.forEach(function(doc) {
      //...do something
  })
})

这里的症结在于query = query.where 不断更改,将每个条件的查询替换为另一个查询,添加新条件。然后,处理完所有条件后,就可以执行最终查询了。

我试图确保代码与您的匹配,但该行中可能有一两个语法错误,因为我不得不猜测 docDataPreferences 包含什么。

【讨论】:

  • 我没有看到这将如何让我创建适当的查询字符串(就像你提到的那样)它将覆盖之前的查询。我想查询所有字段一次,所以我不会调用 20 次。
  • 您不是在构建查询字符串,而是构建具有多个条件的单个查询。如果您无法通过阅读代码看到它是如何工作的,我建议您尝试一下。在调试器中单步执行代码,看看query 在循环之前、期间和之后发生了什么。
  • 您没有添加到“查询”中,而是覆盖了它。所以唯一会运行的查询将是 docDataPreferences 中的最后一项。查询需要像这样编译:var query = usersMatchesCollection.where('preferences.datingPrefDistance', '==', 22).where('preferences.datingPrefDrinking', '==', "No").where('preferences.datingPrefEducation', '==', "Doctorate")... 除非我完全失去它。
  • Firestore 中的查询遵循构建器模式。每次调用 where 时,它都会返回一个新查询,其中包含上一个查询的所有条件以及添加到其中的新条件。请尝试一下,因为这是了解这种方法是否有效的最佳方式。
  • 谢谢,我找不到任何关于构建器模式的信息,这行得通!
【解决方案2】:

Olivia,我经常使用 Frank 模式的变体——我传入一个数组,其中每个条目都是一个对象 {fieldref: string, opstr: string, value: value}。我的代码 .reduce 通过数组传递结果扩展查询,如下所示:

/********************************************************
 * @function collectRecordsByFilter returns an array of documents from Firestore
 * 
 * @param table a properly formatted string representing the requested collection
 * - always an ODD number of elements
 * @param filterArray an array of argument object, each of the form:
 *        {
 *          fieldRef: string, // field name, or dotted field name
 *          opstr: string // operation string representation
 *          value: value // value for query operation
 *        }
 * @param ref (optional) allows "table" parameter to reference a sub-collection
 * of an existing document reference (I use a LOT of structered collections)
 * 
 * The array is assumed to be sorted in the correct order -
 * i.e. filterArray[0] is added first; filterArray[length-1] last
 * returns data as an array of objects (not dissimilar to Redux State objects)
 * with both the documentID and documentReference added as fields.
 */
export const collectRecordsByFilter = (table, filterArray, ref = null) => {
  const db = ref ? ref : firebase.firestore();

  //assumes filterArray is in processing order
  let query = db.collection(table);
  query = filterArray.reduce((accQuery, filter) => {
    return accQuery.where(filter.fieldRef, filter.opStr, filter.value);
  }, query);
  return query
    .get() //get the resulting filtered query results
    .then(querySnapshot => {
      return Promise.resolve(
        querySnapshot.docs.map(doc => {
          return {
            ...doc.data(),
            Id: doc.id,
            ref: doc.ref
          };
        })
      );
    })
    .catch(err => {
      return Promise.reject(err + ":collectRecordsByFilter");
    });
};

关键是“查询”不是一个方法,而是一个查询对象,它有一个“where”方法,可以有效地将新条件附加到查询对象 - 它不是被覆盖,而是扩展。

使用中:

export const fetchTourByDateAndArtist = (dateString, artist) => {
  //for each artist, only *one* tour bridges any particular data (except the default)
  const filterArray = [
    { fieldRef: "dateStart", opStr: "<=", value: dateString }
  ];
  return collectRecordsByFilter("Tours", filterArray, artist.ref).then(
    tours => {
      return Promise.resolve(
        tours.find(tour => {
          return moment(tour.dateEnd).isSameOrAfter(dateString);
        })
      );
    }
  );
};

这种方法为我抽象了 Firestore,并允许大量重复使用。

特蕾西·霍尔 (LeadDreamer)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 2010-10-24
    • 1970-01-01
    • 2020-01-15
    • 1970-01-01
    • 2018-12-15
    • 2018-05-05
    相关资源
    最近更新 更多