Flutter firebase Query - database

I am attempting to queuing my Firestone database however whenever I add multiple .where clauses to my query the query fails. Might there an obvious reason for this? I've attached my query below.
Thanks in advance.
Not Working version (show ordered posts for today but the user logged in)
final Stream<QuerySnapshot> _todayStream = FirebaseFirestore.instance
.collection("Todo")
.where('uid', isEqualTo: user_database().getUID())
.where('date',
isLessThan: fetchDate().getToday())
.orderBy("date", descending: false)
.snapshots();
Working version (show all results for posts made today regardless of user logged in)
final Stream<QuerySnapshot> _todayStream = FirebaseFirestore.instance
.collection("Todo")
.where('date',
isLessThan: fetchDate().getToday())
.orderBy("date", descending: false)
.snapshots();
Working version (only show results posted by user logged in)
final Stream<QuerySnapshot> _todayStream = FirebaseFirestore.instance
.collection("Todo")
.where('uid', isEqualTo: user_database().getUID())
.snapshots();

Queries with multiple conditions need you to define an index yourself, something that isn't needed for queries with only a condition on a single field.
Check the log output of your app, because that contains a message with a direct link to create the necessary index, with all fields pre-populated.

Related

Advice on getting all Active Directory users via graph, then doing delta - code examples included

I'm trying to get all active directory users using the Graph API, and then getting a delta on a scheduled basis.
I don't understand how to get a full load, then get a delta link that can be used for future delta loads against this.
This code is used to get all users:
usersPage = await graphClient
.Users
.Request()
.GetAsync();
while (usersPage.NextPageRequest != null)
{
usersPage = await usersPage.NextPageRequest.GetAsync();
SaveUsersToDatabase(usersPage, sqlConnString, "insert");
}
The problem with this code is by the time I get to the last page, there is no delta link supplied.
To get a delta link, the following code is needed:
usersPage = await graphClient
.Users
.Delta()
.Request()
//.Filter($"Department eq 'Information Technology'")
//.Select("Department,DisplayName,Mail,Id,UserPrincipalName,Manager")
.GetAsync();
This code works, but it doesn't allow a full initial load, and each page returned has loads of duplicates from previous pages.
So it seems I can do a full load but with no delta link returned, or do a delta load but it will never get all users and contains lots of duplicates in subsequent pages.
So the issue/question is how do I do an initial load, get the delta link and then use the delta link in future delta loads?

Graph Active Directory Users API returning lots of duplicates

I'm using the Graph API in an azure function to get all users from active directory.
The code I am using shown below but pseudocode is
if there is no previous deltaLink, get all users, else use the deltalink.
Get the first page.
While the userPage.NextPageRequest is not null, get the next page.
save this page to a database.
first page of 200 users returns as expected.
After this I expect each subsequent page will have its limit of 200 users, until the last page but this is not what I am seeing.
Very often the next page has less than 200 users, 187 to be exact and they are mostly duplicates of what I got in the first page.
Am I doing something incorrect here to cause this behavior?
var scopes = new[] { "https://graph.microsoft.com/.default" };
IUserDeltaCollectionPage usersPage;
UserDeltaCollectionPage lastDeltaPage = new UserDeltaCollectionPage();
if (deltaLink == null)//get all users, there is no delta link from a previous full load.
{
usersPage = await graphClient
.Users
.Delta()
.Request()
.GetAsync();
SaveUsersToDatabase(usersPage, sqlConnString, "insert");
}
else//use delta query to look for updates since last load.
{
lastDeltaPage.InitializeNextPageRequest(graphClient, deltaLink.ToString());
usersPage = await lastDeltaPage.NextPageRequest.GetAsync();
SaveUsersToDatabase(usersPage, sqlConnString, "insert");
}
while (usersPage.NextPageRequest != null)
{
usersPage = await usersPage.NextPageRequest.GetAsync();
SaveUsersToDatabase(usersPage, sqlConnString, "insert");
}
if (usersPage.NextPageRequest == null)//get delta link if this is the last page
{
usersPage.AdditionalData.TryGetValue("#odata.deltaLink", out newDeltaLink);
}
FYI - SaveUsersToDatabase just serializes the usersPage to json and sends it to a database.
Regarding user count, you can check at Azure AD for the count of users and may be duplicates are there itself. Another way to check that is to pull the data by providing Top to validate the count
usersPage = await graphClient
.Users
.Delta()
.Request()
.Top(400)
.GetAsync();
However, I would suggest you to use PageIterator to iterate over all the users as shown below in that case, you don't need to check the nextLink.
List<User> userResult = new List<User>();
users = await graphClient
.Users
.Delta()
.Request()
.GetAsync();
var userIterator = PageIterator<User>
.CreatePageIterator(graphClient, users, user) =>
{
userResult.Add(users);
return true;
});
//get all users
await userIterator.IterateAsync();
Troubleshooting shows that only the delta query is returning the duplicates, and is also returning the same ones each time.
We are going to avoid delta query for the moment and may come back to it in future.
thanks for the advice all.

Unqiue usernames in firebase with no work arounds

I've looked at other answers but I'm still confused. Basically I have a firebase auth system and am trying to enforce unique usernames.
So far, I have 2 collections, a users collection and a username collection. Inside the users collection the documents contain user info. Inside the usernames collection each document id is the username and inside the documents are the userId.
To prevent duplicate usernames on user signup I was thinking to make a query to usernames collection and check if the username exists, from there either display an error to the user or continue with sign up.
code:
// Check if user exists
const usernamesRef = collection(db, 'usernames');
const usernameExists = query(usernamesRef, where('usernames', '==', username));
if(usernameExists) {
// handle error
} else {
createUserWithEmailAndPassword(auth, email, password)
//
}
My question is that is there any way to circumvent this, or is there a better way to do this. I've never done something like this before so I'm looking for input / suggestions. Thanks.
see if you need this cloud function
https://bigcodenerd.org/enforce-cloud-firestore-unique-field-values/

Firestore function to listen for updates

I am a beginner in React native and firestore, and using these to build a kind of social media app, and I have a weird problem(I think I structured the db the wrong way). I want to have a feed, with all posts, no following-based, no nothing. The first time I structured my posts in db like this: users(collection)->user(doc)->thisUserPosts(collection inside doc) - but I couldn't find a way to fetch through all the thisUserPosts from all user(doc) and display them properly.
So I re-structured the db like this:
2 main collection, posts and users. Completely different. In users collection, only docs of users and their data(name, age, etc). In the other, their posts(name, media, desc, AND userId - where userId == the person who created it. userId field from posts collection docs should exist in users collection).
This second approach works just fine. In feed, I only fetch posts. But the problem arrises when I try to open the post(need to have this feature). I need to be able to display on react-navigation header the name of the user, yet I only have details of the post and only userId, which is to no good use.
So I came up with a solution : add a userName field in the posts collection doc, next to userId and simply display that. Now here's the catch: I need to figure a way(in firestore I think) to listen to updates from users collection docs, in case a user updates his name/username(I don't want to showcase the old name). And I don't know if that's possible inside firestore or how. Or is it better to find a different db structure?
TLDR: Need a function in firestore to listen to updates from other collection OR restructuring the db.
If you are fetching posts of a single user then you can just set a listener for his document.
Make sure that document has no sensitive information that must not be shared with others and is limited to the owner only.
If you are fetching posts from multiple users then you can use in operator:
db.collection("users").where("userID", "in", ["user_id1", "user_id2"])
.onSnapshot((snapshot) => {
console.log(snapshot.docs.map(user => user.data()))
});
If I assume you will be updating the new name in all the user's posts then you can set the listener on the posts document itself but that won't be nice in case all 30 posts fetched are from same user. That'll end up costing 30 reads just to update the same name.
Edit:
A simple example of reading a user's posts and listening updates on the user name:
const userID = "my_user_id"
// fetching user's 30 posts
const postsRef = firebase.firebase().collection("posts").where("userID", "==", userID).limit(30)
const postsSnapshot = await postsRef.get()
const postsData = postsSnapshot.docs.map(post => post.data())
// Array of posts data objects
// listening to change in user's name
firebase.firestore().collection("users").doc("user_id")
.onSnapshot((doc) => {
console.log("data: ", doc.data());
const newUsername = doc.data().username
const updatedPostsData = postsData.map(post => {
return ({...post, username: newUsername})
})
});

What's the fastest client-side method to determine if an Azure mobile service table is empty?

I'm working on a UWP app that needs to check to see if a mobile service table has any records.
I realize I could do something like this and check to see if any records are returned:
records = await MyTable.Select(x => x).ToCollectionAsync();
However, is there a way to do this that won't attempt to download every record into a collection? What would be great is if something like this were allowed, but it isn't:
setupOfferors = await setupOfferorsTable.Select(so => so).FirstOrDefault();
Thanks!
There's no built-in method for you to check if the Azure mobile service table is empty. But you could try to apply the specified take clause to the source query and execute this query to see if it will return data.
For example:
await setupOfferorsTable.Select(so => so).Take(1).ToCollectionAsync();

Resources