First of all I know NOSQL systems do not have JOIN. But I know there is a way for getting a datas with another table values.
About my app:
This app is basic social network app. People can share photos. These posts saving firebase database 'realtime database' not cloud firestore than it has a timeline page. this page shows shared posts. I need to show posts with publisher information.
Users login with firebase authentication and I have users table called kullanici like this.
I have Posts table like this.
I need to delete displayName in Posts table than get isim in kullanici table.
my needed data is: Post with kullanici->{posts.{postid}.userid}->isim.
my working code is below:
return (dispatch) => {
firebase.database().ref(`/posts`).endAt("tarih").limitToLast(2)
.on('value', snapshot => {
dispatch({type: STUDENT_LIST_DATA_SUCCESS, payload: snapshot.val()});
});
};
my returned data is below:
Object {
"-L9tnfvm6jQiKLc378c6": Object {
"aciklama": "",
"baslik": "Ensar mı Ahmet mi",
"displayName": "HasanRiza",
"image": "https://hasan-riza-uzuner.s3.amazonaws.com/1523535677033.png",
"like": 244,
"tarih": 1523535757133,
"userid": "fD7IfKAhXogFwHtfKYiF7LMtXNp1",
},
"-LYHPJgR4sywTzpcxX7A": Object {
"aciklama": "Ev",
"baslik": "Nasıl",
"displayName": "HasanRiza",
"image": "https://hasan-riza-uzuner.s3.us-east-2.amazonaws.com/1549718296409.png",
"like": 1,
"tarih": 1549718342522,
"userid": "fD7IfKAhXogFwHtfKYiF7LMtXNp1",
},
}
var promises = [];
group_ids.forEach(function (group_id) {
promises.push(firebase.firestore().collection('Group').doc(group_id).get())
});
Promise.all(promises).then(function(docs) {
docs.forEach((groupDoc) => {
group.name = groupDoc._data['name'];
group.city = groupDoc._data['city'];
group.state = groupDoc._data['state'];
groups.push(group);
});
console.log(groups);
});
Related
In my app, users create posts and I'd like to show trending posts by the number of views, comments, etc in a specific date range. To do that I thought I can create a custom event as below:
await FirebaseAnalytics.instance.logEvent(
name: "trending_contents",
parameters: {
"content_type": EnumToString.convertToString(type),
"content_id": contentModel.externalId,
"action_type": "post",
"point": 3,
},
);
I wonder if it is possible to use Google Analytics Data API to get trending posts by a specific date range? Or is there any better way to get trending posts instead of google analytics data API?
I finally found a solution on how to use Google Analytics Data API to manage trending content. If anyone is looking for a solution for a similar need, here is what I've done so far:
I send a custom event in specific situations such as when the user views the content etc. as below. If you use parameters' names according to predefined dimensions & metrics (see API Dimensions & Metrics), it will be easy to prepare a custom report (at least it was for me...). Later, I use contentType and contentId as dimensions and eventValue as a metric in the custom report.
await FirebaseAnalytics.instance.logEvent(
name: "trending_contents",
parameters: {
"content_type": EnumToString.convertToString(event.type),
"content_id": contentId,
"action_type": "view",
"value": 1,
},
);
Lastly, I created a scheduled cloud function that runs every 6 hours and populates firebase collection according to custom report results. This report gives contentIds in a specific date range ordered by the sum of values that I sent in a custom event
P.S. you need to create a service account in Google Cloud Console, then generate JSON credentials for it and add the file to your project (see credentialsJsonPath variable below). Then you need to add its email address to google analytics 'Property Access Management' section to access analytics data. To see Google Analytics Data API samples, you can check their GitHub repo
const { BetaAnalyticsDataClient } = require('#google-analytics/data');
exports.scheduledTrendingFunction = functions.pubsub.schedule('0 */6 * * *').onRun((context) => {
const propertyId = process.env.GA_PROPERTY_ID;
const credentialsJsonPath = process.env.GA_CRENDENTIALS_PATH;
const analyticsDataClient = new BetaAnalyticsDataClient({
keyFilename: credentialsJsonPath,
});
async function runReport(filterType) {
// [START analyticsdata_json_credentials_run_report]
const [response] = await analyticsDataClient.runReport({
property: `properties/${propertyId}`,
dateRanges: [
{
startDate: '3daysAgo',
endDate: 'today',
},
],
dimensions: [
{
name: 'contentType',
},
{
name: 'contentId'
}
],
metrics: [
{
name: 'eventValue'
},
],
dimensionFilter: {
andGroup: {
expressions: [
{
filter: {
fieldName: "eventName",
inListFilter: {
values: ["trending_contents"]
}
}
},
{
filter: {
fieldName: "contentType",
inListFilter: {
values: [filterType]
}
}
}
]
}
},
offset: 0,
limit: 20,
orderBys: [
{
desc: true,
metric: {
metricName: "eventValue"
}
}
]
});
// [END analyticsdata_json_credentials_run_report]
const batch = admin.firestore().batch();
// BATCH: delete
const trendRef = admin.firestore().collection('trends').doc(filterType);
batch.delete(trendRef);
const subTrendRef = admin.firestore().collection('trends').doc(filterType).collection('trendContents');
// console.log(response);
response.rows.forEach((row, index) => {
// BATCH: add each contentId to trend
const contentId = row['dimensionValues']['1']['value'];
batch.set(subTrendRef.doc(contentId), {priority: index + 1});
});
// Commit the batch
await batch.commit();
}
runReport("book");
return null;
});
I am trying to make a cloud function.
Whenever i try to hit the endpoint I get 500 Internal Server Error
Postman Response Image here
I've checked logs for firebase functions and i don't see any information there too.
It just says "Function Crashed" without any further information.
I've also checked for any typos and mismatch in the Firestore database structure but it all looks fine to me.
This is the code for firebase function which i uploaded on my Firebase Project.
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const { error } = require('firebase-functions/lib/logger');
admin.initializeApp(functions.config().firebase);
exports.addEvent = functions.region('asia-east2').https.onRequest(async (req, res) => {
if (req.method === 'POST') {
var db = admin.firestore();
var write = db.collection("Colleges")
.doc(req.body.college)
.collection("events")
.doc(req.body.event.id)
.set({
id: req.body.event.id,
admin: req.body.event.admin,
event_state: req.body.event.event_state,
name: req.body.event.name,
poster_url: req.body.event.poster_url,
start_date_time: req.body.event.start,
end_date_time: req.body.event.end,
location: req.body.event.location,
short_desc: req.body.event.shortDesc,
long_desc: req.body.event.longDesc,
contacts: req.body.event.contacts,
links: req.body.event.links,
});
return res.send(write);
}
else
return res.sendStatus(403);
});
This is the body of the POST Request which i sent from Postman
{
"college": "college_name",
"event": {
"id": 1234,
"admin": "admin",
"event_state": 2,
"name": "Event Name",
"poster_url": "test",
"start": "Date Time",
"end": "Date Time",
"location": "auditorium",
"shortDesc": "lorem ipsum short",
"longDesc": "lorem ipsum long",
"contatcs": [
{
"tag": "Name Tag",
"contact": 12345678
}
],
"links": [
{
"tag": "Link Tag",
"link": 123456784
}
]
}
}
The Firestore Structure is Something like
-Colleges (Collection)
|
|
-Document
|
-events(Collection)
|
-Event Documents (Document which i want to write to ,from the firebase function)
The problem is that your event id in your payload is a number and Firestore documents ids must be strings. So you either, use .doc(req.body.event.id.toString()) or you send your event id as string in your payload id: "1234".
Also, consider refactoring your code following Firebase guidelines to handle the POST method.
I found a strange error while I developing system using Firebase with service url contains user data.
User data is below.
{
"uid": "kt9Hcp2FbYbBvvIeSHHa1RbvHcv2",
"displayName": "Anonymous 901",
"photoURL": null,
"email": null,
"emailVerified": false,
"identifierNumber": null,
"isAnonymous": true,
"providerData": [
],
"apiKey": "MyApiKeyString",
"appName": "MyAppName",
"authDomain": "my.auth.domain",
"stsTokenManager": {
"apiKey": "MyApiKeyString",
"refreshToken": "refreshTokenString",
"accessToken": "accessTokenString",
"expirationTime": 1532451863076
},
"redirectEventId": null
}
I encode the above anonymous user data and include it in the service url.
( http://myserviceurl?userdata=encodedUserData )
Inside the system receives that url, firebase creates a user object with that user data contained in the url.
The purpose of this url is to use specific user's information in any browser.
However, when I call that service url, sometimes system creates user object well, sometimes got error -
400 Bad request errors with
https://www.googleapis.com/identitytoolkit/v3/relyingparty/setAccountInfo?key=MyApiKeyString
And error data is below,
{
"error": {
"code": 400,
"message": "TOKEN_EXPIRED",
"errors": [
{
"message": "TOKEN_EXPIRED",
"domain": "global",
"reason": "invalid"
}
]
}
}
Few hours later it works well, I changed nothing though.
I could not find the exact error point, but I suspect error occurs while observing authentication state or before this step.
Here is code snipets
#bind
private makeUserLoadingPromise(): Promise<void> {
let unSubscribe: () => void;
return new Promise<void>((resolve, _reject) => {
const onInitialized = this.makeOnInitializedAuthStateChanged(resolve);
unSubscribe = this.auth.onAuthStateChanged(onInitialized);
}).then(() => {
unSubscribe();
this.auth.onAuthStateChanged(this.onAuthStateChanged);
});
}
#bind
private makeOnInitializedAuthStateChanged(resolve: () => void) {
return (user: firebase.User | null) => {
this.user = user;
resolve();
};
}
#bind
private onAuthStateChanged(user: firebase.User | null) {
this.user = user;
}
Or maybe it relates with expirationTime?
I couldn't find any hints about this situation.
Any advice would be appreciated.
It is not clear what you are doing, but it appears that you are using the API incorrectly and insecurely. The plain user object contains a refresh token that is indefinite. Passing it around via URL is a really bad idea.
First don't rely on internal implementations, it is subject to change.
To get the user's information on your backend, the right way to do it, is to get the user's ID token using officially supported API, eg user.getIdToken(), then pass it to your server.
On your server, you verify it via the Firebase Admin SDK: admin.auth().verifyIdToken(idToken). Then you know this is a real authenticated user. If you need the full user info, you can then look it up using the decoded user id in the token: admin.auth().getUser(decodedIdToken.sub).
https://github.com/kristinyim/ClassroomChat
I want to add an upvoting feature to the messages on this chatroom similar to what you have on GroupMe, but I'm new to React and built this off of a tutorial so don't know where to even begin. I'm good with webdev but am just getting started with the basics of React.js and Firebase. Thanks!
NB: There are many ways to achieve this, so the following is just a suggestion.
First you must think of how you want to store your data in the database. If you have users, messages and message-likes, you could structure it like this:
"root": {
"users": {
"$userId": {
...
"messages": {
"$messageId1": true,
"$messageId2": true,
...
}
}
},
"messages": {
"$messageId": {
"author": $userId,
"timestamp": ServerValue.TIMESTAMP
}
},
"likesToMessages": {
"$messageId": {
"$likeId": {
liker: $userId,
"message": $messageId,
"timestamp": ServerValue.TIMESTAMP
}
}
}
}
Whenever a user clicks "like" on a message, you want to write to
var messageId = ?; // The id of the message that was liked
var like = {
liker: currentUserId, // id of logged in user
message: messageId,
timestamp: firebase.database.ServerValue.TIMESTAMP
};
firebase.database.ref().child('likesToMessages').child(messageId).push(like);
Then you get a new like in the database, matching the proposed structure.
Then, when you want to read and show the count of likes for a message, you can do like this:
const Message = React.createClass({
propTypes: {
message: React.PropTypes.object,
messageId: React.PropTypes.string // you need to add this prop
}
componentWillMount() {
firebase.database.ref().child('likesToMessages').child(this.props.messageId).on('value', this.onLikesUpdated)
}
onLikesUpdated(dataSnapshot) {
var likes = snap.val();
this.setState({
likes
});
}
render() {
const {name, message} = this.props.message;
const emojifiedString = emoji.emojify(message);
return (
<p>
{name}: {emojifiedString} [{this.state.likes.length}♥]
</p>
);
}
});
Also, in your database security rules, you'd want to index by timestamp for message and like so you can quickly query the newest messages.
Also, feel free to check out a similar app I made, code in GitHub and demo on wooperate.firebaseapp.com.
I'm currently trying to add new users to Firebase via the AngularFire $set() method. I'm creating new users via the $createUser method from AngularFire. My code looks like this:
$scope.createUser = function() {
$scope.auth.$createUser('dolly#gmail.com', 'password').then(function(user, err) {
if (err) {
console.log('ERROR: ' + err);
} else {
sync.$set('users', user);
}
});
}
This is a creating new user and placing the new user object inside of users:{..}, however, it is also adding an additional user child object thats just duplicate data -- this is what the code is adding to Firebase:
{
"users": {
"email": "dolly#gmail.com",
"id": "11",
"isTemporaryPassword": false,
"md5_hash": "xxxxxxxxxxxxxxx",
"provider": "password",
"sessionKey": "xxxxxxxxxxxxx",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE0MDc5NDQ2NDYsInYiOjAsImQiOnsicHJvdmlkZXIiOiJwYXNzd29yZCIsImlkIjoiMTEiLCJ1aWQiOiJzaW1wbGVsb2dpbjoxMSIsImVtYWlsIjoiZG9sbHlAZ21haWwuY29tIiwibWQ1X2hhc2giOiIzsdrggfeedsaadrfcDc0ZDRhMTU5NTk2NzI1NzFmMDk2ZTZlNyIsImlzVGVtcG9yYXJ5UGFzc3dvcmQiOmZhbHNlLCJzZXNzaW9uS2V5IjoiM2MwMDNjODkxMDEzOWE5MjhlZTZjNWI1NjU5ZTRiZjMifSwiaWF0IjoxNDA3ODU4MjQ2fQ.p7-9GDtaNpBn1ICTLIUSwlPytaUGi-jyBgcO-LKHUys",
"uid": "simplelogin:11",
"user": {
"email": "dolly#gmail.com",
"id": "11",
"isTemporaryPassword": false,
"md5_hash": "xxxxxxxxxxxxxxx",
"provider": "dfrggrssxxxxxxx",
"sessionKey": "xxxxxxxxxxxxxxx",
"uid": "simplelogin:11"
}
}
I ideally want my users object to look like the example in firebase, except with each user key to be whatever is inside user.uid
users: {
user1: {
name: "Alice"
},
user2: {
name: "Bob"
}
}
Where each new user will be added to the users: {...} key without the duplicate user child tacked on?
If you create a reference to users/ and then call $set on that path, then whatever data you include will replace anything at that path. This is AngularFire 101. You should begin by reading at least that section if not the entire guide.
Choose the path you want to set data at when creating your sync object.
var ref = new Firebase('.../users/'+user.uid);
var sync = $firebase(ref);
sync.$set({ email: user.email, provider: user.provider });
Or, better yet, just set it on the reference since you aren't utilizing this for client-side bindings.
var ref = new Firebase('.../users/'+user.uid);
ref.set({ email: user.email, provider: user.provider });
Creating profiles is explained in the Firebase docs and covered in the AngularFire-Seed repo's createProfile service, nearly verbatim to your example.