Можете ли вы создавать динамические c запросы Firebase? - PullRequest
0 голосов
/ 09 февраля 2020

Я хочу запросить FireBase, но это не позволяет. Есть ли способ динамически запрашивать FireBase? Мне нужно обработать очень специфические c случаи - например, если пользователь выбирает «Не имеет значения», мне нужно удалить этот фильтр предпочтений из запроса, некоторые поля являются типами данных массива, а некоторые - строками, а некоторые - числами. , Мне нужно иметь возможность обрабатывать все типы типов данных и заполнять предложение 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
  })
})

Ошибка типа: 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")

, это работает. Таким образом, именно конкатенация вызывает проблему.

Есть ли обходной путь?

Ответы [ 2 ]

2 голосов
/ 09 февраля 2020

Похоже, вы пытаетесь создать запрос путем объединения CollectionReference и строки, которая не будет работать. Если вы console.log(query), вы увидите, что это строка, а не Query, и, поскольку в строке нет метода get(), который объясняет полученную ошибку.

Чтобы построить запрос с условием динамического c, вы должны следовать этому шаблону:

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 содержит.

0 голосов
/ 11 февраля 2020

Оливия, я постоянно использую вариации паттерна Фрэнка - я передаю массив, где каждая запись является объектом {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");
    });
};

Ключ в том, что «запрос» - это не метод, а объект запроса, у которого есть метод «где», который эффективно ПРИНИМАЕТ новое условие к объекту запроса - оно не перезаписывается, а расширяется.

используется:

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)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...