Android Firestore запрашивает определенное значение в массиве объектов - PullRequest
0 голосов
/ 03 апреля 2020

Это моя структура базы данных firestore:

Ожидаемый результат: получить все задания, где в массиве опыта, lang значение - "Swift".

Так что согласно этому я должен получить первые 2 документа. 3-й документ не имеет опыта «Свифт».

Query jobs = db.collection("Jobs").whereArrayContains("experience.lang","Swift");
jobs.get().addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
            @Override
            public void onSuccess(QuerySnapshot queryDocumentSnapshots) {
               //Always the queryDocumentSnapshots size is 0
            }
        });

Перепробовал большинство ответов, но ни один не сработал. Есть ли способ запросить данные в этой структуре? Документы доступны только для обычного массива . Недоступно для массива пользовательских объектов.

Ответы [ 2 ]

4 голосов
/ 04 апреля 2020

На самом деле такой запрос можно выполнить при наличии структуры базы данных, подобной вашей. Я скопировал вашу схему, и вот 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) .

2 голосов
/ 03 апреля 2020

С вашей текущей структурой документа невозможно выполнить запрос, который вы хотите. Firestore не разрешает запросы для отдельных полей объектов в полях списка.

Что вам нужно сделать, это создать дополнительное поле в вашем документе, к которому можно выполнять запросы. Например, вы можете создать поле списка с только списком строковых языков, которые являются частью документа. При этом вы можете использовать запрос, содержащий массив, чтобы найти документы, в которых язык упоминается хотя бы один раз.

Для документа, показанного на снимке экрана, у вас будет поле списка с названием "languages" со значениями ["Swift", "Kotlin"].

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