从 LiveData 触发第二个查询并合并结果(Firestore)?
Trigger a second query from LiveData and merge results (Firestore)?
我有两个 collections:用户和书籍。无论 Users OR Books 是否更新,我都需要获取它们的结果,然后将结果合并到 LinkedHashMap 中以用作 listView 菜单。
我认为 MediatorLiveData 是可行的方法,但如果我将用户查询和图书查询放入,那么我会从两个 LiveData object 之一中得到 null,因为只有一个或另一个着火。我想也许如果其中一个触发,那么也许我在 MediatorLiveData 的每个 addSource() 中都有一个查询 运行,但我不确定这是否是正确的方法。
我关于 MediatorLiveData 的 post 在这里:
Using MediatorLiveData to merge to LiveData (Firestore) QuerySnapshot streams is producing weird results
我的两个查询和LiveDataobject如下:
//getUsers query using FirebaseQueryLiveData class
private Query getUsersQuery() {
FirebaseAuth mAuth = FirebaseAuth.getInstance();
adminID = mAuth.getUid();
query = FirebaseFirestore.getInstance().collection("admins")
.document(adminID)
.collection("users")
return query;
}
private FirebaseQueryLiveData usersLiveData = new FirebaseQueryLiveData(getUsersQuery());
//getBooks query using FirebaseQueryLiveData class
private Query getBooksQuery () {
FirebaseGroupID firebaseGroupID = new FirebaseGroupID(getApplication());
groupID = firebaseGroupID.getGroupID();
query = FirebaseFirestore.getInstance().collection("books")
.whereEqualTo("groupID", groupID)
return query;
}
private FirebaseQueryLiveData booksLiveData = new FirebaseQueryLiveData(getBooksQuery());
不知何故,当用户更新时,我也需要获取书籍的数据,然后合并它们,但是如果书籍更新,我也需要这样做,然后获取用户的数据并合并它们。
如有任何想法,我们将不胜感激。
额外Note/Observation
好的,所以我没有完全排除 MediatorLiveData object。当然,它允许我在同一个方法中监听两个不同的 LiveData object,但是,我不想直接合并它们两个,因为我需要单独对每个 liveData object 进行操作。举个例子:usersLiveData 触发是因为我们创建或修改了一个用户,然后我需要查询书籍,获取结果并合并用户和书籍等。
下面是我目前的 MediatorLiveData:
//MediatorLiveData merge two LiveData QuerySnapshot streams
private MediatorLiveData<QuerySnapshot> usersBooksLiveDataMerger() {
final MediatorLiveData<QuerySnapshot> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
mediatorLiveData.setValue(querySnapshot);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
mediatorLiveData.setValue(querySnapshot);
}
});
return mediatorLiveData;
}
现在它正在 returning 其他 LiveData 源的空结果。相反,我需要查询然后合并。关于如何做到这一点的任何想法?关于这件事的内容并不多。
我尝试将查询放在使用 Transformations.map() 调用的函数中,但由于它是异步调用,因此在查询完成之前调用了 return 语句。
这是我对函数的尝试:
private class ListenUsersGetBooks implements Function<QuerySnapshot, LinkedHashMap<User, List<Book>>> {
@Override
public LinkedHashMap<User, List<Book>> apply(final QuerySnapshot input) {
userBookList = new LinkedHashMap<>();
getBooksQuery().get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
List<User> users = input.toObjects(User.class);
List<Book> books = task.getResult().toObjects(Book.class);
Log.d(TAG, "USERLIST! " + users);
Log.d(TAG, "BOOKLIST! " + books);
for (User user : users) {
bookList = new ArrayList<>();
for (Book book : books) {
if (user.getUserID().equals(book.getUserID())
&& book.getBookAssigned()) {
bookList.add(book);
}
else if (user.getAllBookID().equals(book.getBookID())) {
bookList.add(book);
}
}
userBookList.put(user, bookList);
}
Log.d(TAG,"OBSERVE userBookList: " + userBookList);
}
});
return userBookList;
}
}
这就是我对 Sam 的建议的看法。
我向 MyResult class 添加了 getter 和 setter 方法,因为它不允许我访问观察者中的成员变量,否则:
public class MyResult {
QuerySnapshot usersSnapshot;
QuerySnapshot booksSnapshot;
//default constructor
public MyResult() {
}
public QuerySnapshot getUsersSnapshot() {
return usersSnapshot;
}
public void setUsersSnapshot(QuerySnapshot usersSnapshot) {
this.usersSnapshot = usersSnapshot;
}
public QuerySnapshot getBooksSnapshot() {
return booksSnapshot;
}
public void setBooksSnapshot(QuerySnapshot booksSnapshot) {
this.booksSnapshot = booksSnapshot;
}
public boolean isComplete() {
return (usersSnapshot != null && booksSnapshot != null);
}
}
这是 MediatorLiveData 和获取方法。我将 MyResult class 初始化更改为 = new MyResult();认为使用 mediatorLiveData.getValue() 存在问题;作为初始化和获取方法。
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = new MyResult();
current.setUsersSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = new MyResult();
current.setBooksSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
public MediatorLiveData<MyResult> getUsersBooksLiveDataMerger() {
return usersBooksLiveDataMerger();
}
最后是观察者:
mainViewModel.getUsersBooksLiveDataMerger().observe(this, new Observer<MainViewModel.MyResult>() {
@Override
public void onChanged(@Nullable MainViewModel.MyResult myResult) {
if (myResult == null || !myResult.isComplete()) {
// Ignore, this means only one of the queries has fininshed
Log.d(TAG, "OBSERVE BLAH!!!!");
return;
}
// If you get to here, you know all the queries are ready!
// ...
List<Book> books;
List<User> users;
books = myResult.getBooksSnapshot().toObjects(Book.class);
users = myResult.getUsersSnapshot().toObjects(User.class);
Log.d(TAG, "OBSERVE MERGE users: " + users);
Log.d(TAG, "OBSERVE MERGE books: " + books);
}
});
请注意:我确实在 mediatorLiveData 中做了空检查,只是出于测试目的将其取出。
如果只触发我的用户,我需要以某种方式触发我的图书查询,如果只触发我的图书,我需要触发我的用户查询...我觉得在 MediatorLiveData 之前需要执行一个步骤所以我们可以确保一个 liveData 触发另一个查询。这有意义吗?
这是您可以执行的操作的简单版本,我希望它有意义。
您接近 MediatorLiveData
。您可能想使用这样的自定义对象而不是 MediatorLiveData<QuerySnapshot>
:
class MyResult {
public QuerySnapshot usersSnapshot;
public QuerySnapshot booksSnapshot;
public MyResult() {}
boolean isComplete() {
return (usersSnapshot != null && booksSnapshot != null);
}
}
然后在你的观察者中,做这样的事情:
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = mediatorLiveData.getValue();
current.usersSnapshot = querySnapshot;
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = mediatorLiveData.getValue();
current.booksSnapshot = querySnapshot;
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
然后当您观察合并的实时数据时:
usersBooksLiveDataMerger().observe(new Observer<MyResult>() {
@Override
public void onChanged(@Nullable MyResult result) {
if (result == null || !result.isComplete()) {
// Ignore, this means only one of the queries has fininshed
return;
}
// If you get to here, you know all the queries are ready!
// ...
}
});
我想我解决了。我们在每个 mediatorLiveData.addSource() 方法中声明了一个新的 MyResult 对象。这意味着我们为每个 QuerySnapshot 获取一个新对象,因此我们永远不会让它们相互合并。
这是对 MediatorLiveData 的更新:
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
final MyResult current = new MyResult();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
current.setUsersSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
current.setBooksSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
现在我在 Activity 的观察者中获取用户和书籍!现在我唯一需要做的就是将数据转换(合并数据)到 LinkedHashMap 中,但我想我明白了。谢谢山姆!
您可以使用我的 LiveDataZipExtensions https://gist.github.com/Benjiko99/d2e5406aab0a4a775ea747956ae16624
大大简化使用
有了它们,您不必创建对象来保存合并结果。
用法示例
val firstNameLD = MutableLiveData<String>().apply { value = "John" }
val lastNameLD = MutableLiveData<String>().apply { value = "Smith" }
// The map function will get called once all zipped LiveData are present
val fullNameLD = zip(firstNameLD, lastNameLD).map { (firstName, lastName) ->
"$firstName $lastName"
}
我有两个 collections:用户和书籍。无论 Users OR Books 是否更新,我都需要获取它们的结果,然后将结果合并到 LinkedHashMap 中以用作 listView 菜单。
我认为 MediatorLiveData 是可行的方法,但如果我将用户查询和图书查询放入,那么我会从两个 LiveData object 之一中得到 null,因为只有一个或另一个着火。我想也许如果其中一个触发,那么也许我在 MediatorLiveData 的每个 addSource() 中都有一个查询 运行,但我不确定这是否是正确的方法。
我关于 MediatorLiveData 的 post 在这里: Using MediatorLiveData to merge to LiveData (Firestore) QuerySnapshot streams is producing weird results
我的两个查询和LiveDataobject如下:
//getUsers query using FirebaseQueryLiveData class
private Query getUsersQuery() {
FirebaseAuth mAuth = FirebaseAuth.getInstance();
adminID = mAuth.getUid();
query = FirebaseFirestore.getInstance().collection("admins")
.document(adminID)
.collection("users")
return query;
}
private FirebaseQueryLiveData usersLiveData = new FirebaseQueryLiveData(getUsersQuery());
//getBooks query using FirebaseQueryLiveData class
private Query getBooksQuery () {
FirebaseGroupID firebaseGroupID = new FirebaseGroupID(getApplication());
groupID = firebaseGroupID.getGroupID();
query = FirebaseFirestore.getInstance().collection("books")
.whereEqualTo("groupID", groupID)
return query;
}
private FirebaseQueryLiveData booksLiveData = new FirebaseQueryLiveData(getBooksQuery());
不知何故,当用户更新时,我也需要获取书籍的数据,然后合并它们,但是如果书籍更新,我也需要这样做,然后获取用户的数据并合并它们。
如有任何想法,我们将不胜感激。
额外Note/Observation 好的,所以我没有完全排除 MediatorLiveData object。当然,它允许我在同一个方法中监听两个不同的 LiveData object,但是,我不想直接合并它们两个,因为我需要单独对每个 liveData object 进行操作。举个例子:usersLiveData 触发是因为我们创建或修改了一个用户,然后我需要查询书籍,获取结果并合并用户和书籍等。
下面是我目前的 MediatorLiveData:
//MediatorLiveData merge two LiveData QuerySnapshot streams
private MediatorLiveData<QuerySnapshot> usersBooksLiveDataMerger() {
final MediatorLiveData<QuerySnapshot> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
mediatorLiveData.setValue(querySnapshot);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
mediatorLiveData.setValue(querySnapshot);
}
});
return mediatorLiveData;
}
现在它正在 returning 其他 LiveData 源的空结果。相反,我需要查询然后合并。关于如何做到这一点的任何想法?关于这件事的内容并不多。
我尝试将查询放在使用 Transformations.map() 调用的函数中,但由于它是异步调用,因此在查询完成之前调用了 return 语句。
这是我对函数的尝试:
private class ListenUsersGetBooks implements Function<QuerySnapshot, LinkedHashMap<User, List<Book>>> {
@Override
public LinkedHashMap<User, List<Book>> apply(final QuerySnapshot input) {
userBookList = new LinkedHashMap<>();
getBooksQuery().get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
@Override
public void onComplete(@NonNull Task<QuerySnapshot> task) {
List<User> users = input.toObjects(User.class);
List<Book> books = task.getResult().toObjects(Book.class);
Log.d(TAG, "USERLIST! " + users);
Log.d(TAG, "BOOKLIST! " + books);
for (User user : users) {
bookList = new ArrayList<>();
for (Book book : books) {
if (user.getUserID().equals(book.getUserID())
&& book.getBookAssigned()) {
bookList.add(book);
}
else if (user.getAllBookID().equals(book.getBookID())) {
bookList.add(book);
}
}
userBookList.put(user, bookList);
}
Log.d(TAG,"OBSERVE userBookList: " + userBookList);
}
});
return userBookList;
}
}
这就是我对 Sam 的建议的看法。
我向 MyResult class 添加了 getter 和 setter 方法,因为它不允许我访问观察者中的成员变量,否则:
public class MyResult {
QuerySnapshot usersSnapshot;
QuerySnapshot booksSnapshot;
//default constructor
public MyResult() {
}
public QuerySnapshot getUsersSnapshot() {
return usersSnapshot;
}
public void setUsersSnapshot(QuerySnapshot usersSnapshot) {
this.usersSnapshot = usersSnapshot;
}
public QuerySnapshot getBooksSnapshot() {
return booksSnapshot;
}
public void setBooksSnapshot(QuerySnapshot booksSnapshot) {
this.booksSnapshot = booksSnapshot;
}
public boolean isComplete() {
return (usersSnapshot != null && booksSnapshot != null);
}
}
这是 MediatorLiveData 和获取方法。我将 MyResult class 初始化更改为 = new MyResult();认为使用 mediatorLiveData.getValue() 存在问题;作为初始化和获取方法。
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = new MyResult();
current.setUsersSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = new MyResult();
current.setBooksSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
public MediatorLiveData<MyResult> getUsersBooksLiveDataMerger() {
return usersBooksLiveDataMerger();
}
最后是观察者:
mainViewModel.getUsersBooksLiveDataMerger().observe(this, new Observer<MainViewModel.MyResult>() {
@Override
public void onChanged(@Nullable MainViewModel.MyResult myResult) {
if (myResult == null || !myResult.isComplete()) {
// Ignore, this means only one of the queries has fininshed
Log.d(TAG, "OBSERVE BLAH!!!!");
return;
}
// If you get to here, you know all the queries are ready!
// ...
List<Book> books;
List<User> users;
books = myResult.getBooksSnapshot().toObjects(Book.class);
users = myResult.getUsersSnapshot().toObjects(User.class);
Log.d(TAG, "OBSERVE MERGE users: " + users);
Log.d(TAG, "OBSERVE MERGE books: " + books);
}
});
请注意:我确实在 mediatorLiveData 中做了空检查,只是出于测试目的将其取出。
如果只触发我的用户,我需要以某种方式触发我的图书查询,如果只触发我的图书,我需要触发我的用户查询...我觉得在 MediatorLiveData 之前需要执行一个步骤所以我们可以确保一个 liveData 触发另一个查询。这有意义吗?
这是您可以执行的操作的简单版本,我希望它有意义。
您接近 MediatorLiveData
。您可能想使用这样的自定义对象而不是 MediatorLiveData<QuerySnapshot>
:
class MyResult {
public QuerySnapshot usersSnapshot;
public QuerySnapshot booksSnapshot;
public MyResult() {}
boolean isComplete() {
return (usersSnapshot != null && booksSnapshot != null);
}
}
然后在你的观察者中,做这样的事情:
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = mediatorLiveData.getValue();
current.usersSnapshot = querySnapshot;
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
MyResult current = mediatorLiveData.getValue();
current.booksSnapshot = querySnapshot;
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
然后当您观察合并的实时数据时:
usersBooksLiveDataMerger().observe(new Observer<MyResult>() {
@Override
public void onChanged(@Nullable MyResult result) {
if (result == null || !result.isComplete()) {
// Ignore, this means only one of the queries has fininshed
return;
}
// If you get to here, you know all the queries are ready!
// ...
}
});
我想我解决了。我们在每个 mediatorLiveData.addSource() 方法中声明了一个新的 MyResult 对象。这意味着我们为每个 QuerySnapshot 获取一个新对象,因此我们永远不会让它们相互合并。
这是对 MediatorLiveData 的更新:
private MediatorLiveData<MyResult> usersBooksLiveDataMerger() {
final MediatorLiveData<MyResult> mediatorLiveData = new MediatorLiveData<>();
final MyResult current = new MyResult();
mediatorLiveData.addSource(usersLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
current.setUsersSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
mediatorLiveData.addSource(booksLiveData, new Observer<QuerySnapshot>() {
@Override
public void onChanged(@Nullable QuerySnapshot querySnapshot) {
current.setBooksSnapshot(querySnapshot);
mediatorLiveData.setValue(current);
}
});
return mediatorLiveData;
}
现在我在 Activity 的观察者中获取用户和书籍!现在我唯一需要做的就是将数据转换(合并数据)到 LinkedHashMap 中,但我想我明白了。谢谢山姆!
您可以使用我的 LiveDataZipExtensions https://gist.github.com/Benjiko99/d2e5406aab0a4a775ea747956ae16624
大大简化使用有了它们,您不必创建对象来保存合并结果。
用法示例
val firstNameLD = MutableLiveData<String>().apply { value = "John" }
val lastNameLD = MutableLiveData<String>().apply { value = "Smith" }
// The map function will get called once all zipped LiveData are present
val fullNameLD = zip(firstNameLD, lastNameLD).map { (firstName, lastName) ->
"$firstName $lastName"
}