Firestore Subcollection '!=' query - google-cloud-firestore

I am aware Firestore doesn't support '!=' queries, but I was wondering if anybody has run into a similar situation as below:
Here is my db structure:
Posts -> postId -> postDocument -> likedBy -> uid
What I'm looking to do is only show posts that don't have the current user's uid in the 'likedBy' subcollection. That itself isn't possible, but I'm struggling to find even a semi-decent work around.
Currently I get all the posts and do the check locally to display the correct ones. Is this possible with perhaps a magic cloud function something?

You might find success and use cases beyond this one by maintaining a user's feed and then only calling that at runtime. I utilize this method and have found I'm given a lot of freedom and Cloud Functions let me dictate what types of posts show and under what changes are new posts added to a user's feed.
The way I do it is I look for new posts via an onCreate cloud function and then look up who should see that post, etc. and add it to each of their feeds.
In your case I can see it being used by looking for new likes on a post. On new likes, it can remove the post from the user's feed.
An example of a function (edited for brevity) that I use to add posts to the user's follower's feeds. By grabbing using a collectionGroup query I can query the list of all users who follow the author of the post.
The schema looks like this:
Users (collection)
--- User1 (document)
------- Following (collection of people User1 is following)
----------- FollowingUser1 (document, contains a uid of "followed" user)
----------- FollowingUser2
and the Cloud Function:
exports.newReview = functions.firestore
.document('reviews/{reviewId}')
.onCreate((snap, context) => {
var reviewId = context.params.reviewId
var reviewData = snap.data()
var userFollowers = db.collectionGroup('following').where('uid', '==', userId)
var followingTransaction = db.runTransaction(transaction => {
return transaction.get(userFollowers).then(restDocs => {
reviewData['added_via'] = 'following'
restDocs.forEach(doc => {
var followerId = doc.ref.parent.parent.id
var followerRef = db.collection(`feeds/${followerId}/posts`).doc(reviewId)
transaction.set(followerRef, reviewData);
})
return true
});
});
return followingTransaction.then(values => {
console.log(reviewData)
var shouldPostToTwitter = reviewData.postToTwitter
return Promise.resolve()
}) .catch(error => {
console.log(error)
return Promise.reject(new Error("Error deleting"));
});
});
DB look something look this:
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL
});
const db = admin.firestore()

Related

Postgres leftJoinAndMapMany Query using TypeORM (property undefined)

I've been using PostgreSQL using TypeORM and I've got some problem at getting Follower table and check if the user is in that table.
Here's my code:
// queryTest.ts
data = await getRepository(User)
.createQueryBuilder('user')
.leftJoinAndMapMany('user.id', Post, 'post', 'post.userId = user.id')
.leftJoinAndMapMany('post.id', Follow, 'follow', 'follow.userId = user.id')
// Post author's follower
.where(new Brackets(qb => {
qb.where('post.publicScope = :follower', { follower: 'Follower' })
.andWhere('post.userId = follow.followeeId')
.andWhere(':myId = follow.userId', { myId: user.id })
}))
.getMany()
And when I try to send this query, I get { } (supposed to get some user, post, follower data)
I have User, Post, Follow entities and I'm trying to get this data:
my followee's post that he set as 'Follower' (suppose I'm following some celebrity and he posted something that is just for followers, and I'm trying to get that data)
OR I'm also thinking of makig 2 entities (Follower, Followee) that have ManyToMany relation. Could any body help me with this?
The problem is that you are mapping Post and Follow to the wrong property, the first param that you gave 'user.id' is wrong, this param must be the property that you want to save the Post or Follow in, so it should be something like this:
data = await getRepository(User)
.createQueryBuilder('user')
.leftJoinAndMapMany('user.post', Post, 'post', 'post.userId = user.id')
.leftJoinAndMapMany('post.follow', Follow, 'follow', 'follow.userId = user.id')
.where(new Brackets(qb => {
qb.where('post.publicScope = :follower', { follower: 'Follower' })
.andWhere('post.userId = follow.followeeId')
.andWhere(':myId = follow.userId', { myId: user.id })
}))
.getMany()
Note that you MUST create these properties (post, follow) in the User entity.
However, if you used OneToMany or ManyToOne or OneToOne relations in there, you shouldn't use leftJoinAndMapMany, just use leftJoinAndSelect

entity core - select only filtered child records

Have three entities, user, post, and likes. I need to return all posts for all users but also include if the current user already liked a specific post in it. Using paged query using the below lambda expression
var pagedData = await _context.Set<Post>()
.Include(post => post.currentUserLikes.Where(likes => likes.UserId == userId))
.OrderByDescending(post => post.CreatedAt)
.Skip((validFilter.PageNumber - 1) * validFilter.PageSize)
.Take(validFilter.PageSize)
.ProjectTo<PostExistingDto>(mapperConfig())
.ToListAsync();
I am getting all like for each post instead of current userId I am passing. Any idea how to achieve this requirement. Thank you

Query Firestore documents with role based security via Flutter

I´ve a role based data model on Firestore according to googles suggestion here: https://firebase.google.com/docs/firestore/solutions/role-based-access
Security rules are set up correctly and work fine. But now I´ve the problem on how to query for the roles.
This is my data model (one sample document):
id: "1234-5678-91234",
roles:
userId_1:"owner",
userId_2:"editor
title: "This is a sample document"
And this is my Firestore Query in Flutter which gets all documents for a specific user by its ID if the user has assigned the role "owner" for the document:
return firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "owner")
.snapshots().map((snapshot) {
return snapshot.documents.map((catalog) {
return SomeDocumentObject(...);
}).toList();
});
My problem now is, that I need some kind of "OR" clause - which does not exist as far as I know. The query above only retrieves documents for users with role "owner" but I need a query that also retrieves the document if the userId is associated with the role "editor".
I´ve tried "arrayContains:" which also doesn´t seem to work (cause it´s a map).
I´ve read about solutions with two independent queries which doesn´t sound like a good solution due to a lot of overhead.
Maybe someone of you have a hint for me? :)
Thanks & best,
Michael
Firestore doesn't currently have any logical OR operations. You'll have to perform two queries, one for each condition, and merge the results of both queries in the client app.
This is the final solution using RxDart, Observables and .combineLatest() - maybe it helps someone out there:
#override
Stream<List<Catalog>> catalogs(User user) {
// Retrieve all catalogs where user is owner
Observable<QuerySnapshot> ownerCatalogs = Observable(firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "owner")
.snapshots());
// Retrieve all catalogs where user is editor
Observable<QuerySnapshot> editorCatalogs = Observable(firestore
.collection(path)
.where("roles.${user.firebaseUserId}", isEqualTo: "editor")
.snapshots());
// Convert merged stream to list of catalogs
return Observable.combineLatest([ownerCatalogs, editorCatalogs],
(List<QuerySnapshot> snapshotList) {
List<Catalog> catalogs = [];
snapshotList.forEach((snapshot) {
snapshot.documents.forEach((DocumentSnapshot catalog) {
catalogs.add(Catalog(
id: catalog.documentID,
title: catalog.data['title'],
roles: catalog.data['roles'],
));
});
});
return catalogs;
}).asBroadcastStream();
}

Cloud functions context params return undefined

I am using cloud functions to listen on new document created on Firestore.
functions.firestore.document('users/{userId}')
.onCreate((snapshot, context) => {
console.log('params', context.params.userId);
});
The logs show undefined instead of the wildcarded param.
This start happening from 15th dec 2018 at midnight.
Is this a bug related to an update of firestore/cloud functions ?
and how we can bypass this issue?
There seems to be a bug in the Firebase Functions SDK or platform currently (15 December 2018).
Work-around:
Update The proper way to access the parent document ID is through change.after.ref.parent.parent.id or snapshot.ref.parent.parent.id. Note the .parent.parent.
If you are expecting parameters with the document IDs, you can probably work around the problem by using the data provided in the first argument to you function.
Here is an example with an onCreate triggered function:
export const myCreateTriggeredFn = firestore
.document("user/{userId}/friends/{friendId}")
.onCreate((snapshot, context) => {
let { userId, friendId } = context.params;
if (typeof userId !== "string" || typeof friendId !== "string") {
console.warn(`Invalid params, expected 'userId' and 'friendId'`, context.params);
userId = snapshot.ref.parent.parent.id;
friendId = snapshot.id;
}
// Continue your logic here...
});
And for an onWrite triggered function:
export const myChangeTriggeredFn = firestore
.document("user/{userId}/friends/{friendId}")
.onWrite((change, context) => {
let { userId, friendId } = context.params;
if (typeof userId !== "string" || typeof friendId !== "string") {
console.warn(`Invalid params, expected 'userId' and 'friendId'`, context.params);
userId = change.after.ref.parent.parent.id;
friendId = change.after.id;
}
// Continue your logic here...
});
For the sake of completeness and to highlight the bug, both examples shows how you would normally extract the IDs from the context.params and then the added work-around to extract the IDs from the snapshot/change objects.
I'm the Google employee working on the incident. There is a known compatibility issue for customers using the SDK when Firestore was in a private alpha and have not upgraded.
Could affected customers who are running their code with an SDK version newer than 0.6.2 respond? If you are running version 0.6.1 you can upgrade to 0.6.2 with no code changes to fix.

Facebook api search in Asp.mvc 3 app

EDITED
I'm searching users in facebook using graph api in my asp.net mvc 3 application.
public void AsyncSearch(ICollection<JSonObject> result, string query, string objectType)
{
var fbClient = new FacebookClient(FacebookTokens.AccessToken);
var searchUri = string.Format("/search?q={0}&type={1}, query, objectType);
var tempResult = (JsonObject)fbClient.Get(searchUri);
var elements = (JsonArray)tempResult.Values.ToArray()[0];
elements.ForEach(element =>
{
result.Add(element);
});
var next = (JsonObject)tempResult.Values.ToList()[1];
while (next.Keys.Contains("next"))
{
tempResult = (JsonObject)fbClient.Get((string)next["next"]);
elements = (JsonArray)tempResult.Values.ToArray()[0];
elements.ForEach(element =>
{
result.Add(element);
});
next = (JsonObject)tempResult.Values.ToList()[1];
}
}
But result contains at most 600 objects(each search returns different number of objects).
I think, if i put, for example, "anna" in query parameter - result must be over 10000.
Why is that? Is there any way to retrieve all users by some keyword?
For performance concerns Facebook will paginate their results. If you look at the end of the JSON object, there should be a pageing object that has next and previous links in it. So, to get all results you will need to run multiple queries and aggregate them up on your side.