На самом деле такой запрос можно выполнить при наличии структуры базы данных, подобной вашей. Я скопировал вашу схему, и вот document1 , document2 и document3 .
Обратите внимание, что вы не можете запрашивать с использованием частичных (неполных) данных , Для запроса вы используете только свойство lang
, что не правильно. Вам следует использовать объект, который содержит оба свойства, lang
и years
.
. На первый взгляд, на скриншоте массив experience
представляет собой список HashMap
объектов. Но здесь начинается самое приятное: этот список можно просто отобразить в список пользовательских объектов. Давайте попробуем отобразить каждый объект из массива на объект типа Experience
. Модель содержит только два свойства:
public class Experience {
public String lang, years;
public Experience() {}
public Experience(String lang, String years) {
this.lang = lang;
this.years = years;
}
}
Я не знаю, как вы назвали класс, представляющий документ, но я назвал его просто Job
. Для простоты я использовал только два свойства:
public class Job {
public String name;
public List<Experience> experience;
//Other prooerties
public Job() {}
}
Теперь, чтобы выполнить поиск всех документов, которые содержат в массиве объект с lang
, установленным на Swift
, пожалуйста, следуйте следующие шаги. Сначала создайте новый объект класса Experience
:
Experience firstExperience = new Experience("Swift", "1");
Теперь вы можете сделать запрос следующим образом:
CollectionReference jobsRef = rootRef.collection("Jobs");
jobsRef.whereArrayContains("experience", firstExperience).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Job job = document.toObject(Job.class);
Log.d(TAG, job.name);
}
} else {
Log.d(TAG, task.getException().getMessage());
}
}
});
Результатом в logcat будет имя document1 и document2 :
firstJob
secondJob
И это потому, что только эти два документа содержат в массиве объект, для которого lang
имеет значение Swift
.
Вы также можете достичь того же результата при использовании карты:
Map<String, Object> firstExperience = new HashMap<>();
firstExperience.put("lang", "Swift");
firstExperience.put("years", "1");
Так что в этом сценарии использования нет необходимости дублировать данные. Я также написал статью на ту же тему c
Редактировать:
В вашем подходе он дает результат, только если expreience равен "1", а lang "Swift", верно?
Это верно, он ищет только один элемент. Однако, если вам нужно запросить больше:
Experience firstExperience = new Experience("Swift", "1");
Experience secondExperience = new Experience("Swift", "4");
//Up to ten
Мы используем другой подход, который на самом деле очень прост. Я говорю о методе Query whereArrayContainsAny () :
Создает и возвращает новый запрос с дополнительным фильтром, в котором документы должны содержать указанное поле, значение должно быть массивом и что массив должен содержать хотя бы одно значение из предоставленного списка.
И в коде должно выглядеть так:
jobsRef.whereArrayContainsAny("experience", Arrays.asList(firstExperience, secondExperience)).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Job job = document.toObject(Job.class);
Log.d(TAG, job.name);
}
} else {
Log.d(TAG, task.getException().getMessage());
}
}
});
Результат в logcat будет:
firstJob
secondJob
thirdJob
И это потому, что все три документа содержат один или другой объект.
Почему я говорю о дублировании данных в документе, потому что у документов есть ограничения. Таким образом, существуют некоторые ограничения в отношении того, сколько данных вы можете поместить в документ. В соответствии с официальной документацией относительно использования и ограничений :
Максимальный размер документа: 1 МБ (1 048 576 байт)
Как вы можете видеть , вы ограничены 1 МБ данных в одном документе. Таким образом, хранение дублированных данных только увеличит изменение, чтобы достичь предела.
Если я отправлю нулевые данные "exprience" и "swift" как "lang", будут ли они запрашиваться?
Нет, не будет работать.
Edit2:
whereArrayContainsAny()
метод работает с максимум 10 объектами. Если у вас есть 30, то вы должны сохранить каждый query.get()
из 10 объектов в Task объекте, а затем передать их по одному в в Tasks когда whenAllSuccess (Task ... задачи) .
Вы также можете передать их непосредственно как список методу Tasks whenAllSuccess (Collection> tasks) .