实际上,当拥有像您这样的数据库结构时,可以执行这样的查询。我已经复制了您的架构,这里是 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");
所以在这个用例中不需要复制数据。我也写了一篇关于同一主题的文章
编辑:
在您的方法中,仅当 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
这是因为所有三个文档都包含一个或另一个对象。
我为什么要在文档中谈论复制数据,因为文档有限制。因此,在您可以将多少数据放入文档时存在一些限制。根据usage and limits的官方文档:
文档的最大大小:1 MiB(1,048,576 字节)
如您所见,单个文档中的数据总量限制为 1 MiB。所以存储重复数据只会增加变化达到极限。
如果我将“exprience”和“swift”的空数据发送为“lang”会被查询吗?
不,不会工作。
编辑2:
whereArrayContainsAny() 方法最多可处理 10 个对象。如果你有 30 个,那么你应该将 10 个对象中的每个 query.get() 保存到一个 Task 对象中,然后将 pass them one by one to the 保存到任务的 whenAllSuccess(Task... tasks) 中。
你也可以pass them directly as a list 到Tasks 的whenAllSuccess(Collection> tasks) 方法。