I have this structure of my database :
Posts
-> Key
-> PostId
-> PostName
-> PostDescription
-> PostDescription2
-> Location
-> Author
-> Date
-> Key
->
->
....
Somehow the field PostDescription has been duplicated in PostDescription2, I have n such nodes and I want to delete all entries of PostDescription2 from all the nodes. How can I do this ?
I did some search on google, but most articles tells about how to delete a child node or fields inside them using some logic in query. Also, I am a bit new to firebase.
You will need to execute this function ones. (Seams so) A sample of deletion code may look like into ie: posts path:
var dB = firebase.database();
var dBRef =dB.ref('posts');
dbRef.once('value', snpsht=>{
snpsht.forEach(dp =>{
var key = dp.key;
dB.ref('posts/' + key + '/PostDescription2').set(null);
})
})
This will delete all PostDescription2 field with setting value null. Consider that posts path is not a big data to avoid blocking the code. If so, you may get data with limitToLast option like:
var dBRef = dB.ref('posts').orderByChild('PostDescription2').limitToLast(1000);
This command will order the list by PostDescription2 and null value for the specified child key come first. and we collect from last 1000 items with above code.
Related
I am working with Google Sheets, and I am trying to send data to my Firestore database. I have been able to write to Firestore from Google Sheets, but I can't seem to update a field without completely messing things up.
This is my current testing code:
function getFireStore() {
const email = "your#email.gserviceaccount.com"
const key = "-----BEGIN PRIVATE KEY-----\n your key here \n-----END PRIVATE KEY-----\n";
const id = "project_id";
var firestore = FirestoreApp.getFirestore(email, key, id);
var spreadsheet = SpreadsheetApp.getActive()
var sheet = spreadsheet.getActiveSheet()
var data = {
numIndividuals: sheet.getRange(23, individuals).getValue(),
numTeams: sheet.getRange(23, teams).getValue(),
schoolID: sheet.getRange(23, schoolID).getValue(),
uid: sheet.getRange(23, uid).getValue(),
};
firestore.createDocument("competitions/" + sheet.getRange(23, compId).getValue() + "/registration/abcdefg", data)
}
I understand after playing around with this that it will create a new subcollection titled "registration" with the document "abcdefg." The same thing happens when I use the updateDocument function, as well.
For the website that is reading and writing to this particular Firestore database, I use a similar function .update() to update the document with the correct information. However, in Google Sheets, while it would work the same way it is much more convoluted and tedious to do so.
The way that I came up with for trying to update the document was basically copying everything and adding in the new data.
However, this is seriously tedious and messy. Just copying the data that isn't changed looks like this:
var data = {
compDate: competitions.fields.compDate.stringValue,
contact: competitions.fields.contact.stringValue,
email: competitions.fields.email.stringValue,
grade: competitions.fields.grade.stringValue,
id: competitions.fields.id.integerValue,
maxTeams: competitions.fields.maxTeams.integerValue,
regDate: competitions.fields.regDate.stringValue,
schTeams: competitions.fields.schTeams.integerValue,
schedule: competitions.fields.schedule.stringValue,
site: competitions.fields.site.stringValue,
status: competitions.fields.status.stringValue,
timestamp: competitions.fields.timestamp.integerValue,
user: competitions.fields.user.stringValue,
year: competitions.fields.year.stringValue,
}
The data I want to change is a .mapValue with multiple fields where one of the fields can have multiple fields, which also have multiple fields.
Here's the hierarchy for the field I need to update:
first registration and first team
I know I could do multiple for-loops and whatnot on this, but my question is: is there a simpler way to do this, or do I have to go through and loop over everything to extract only what I want?
As a sidenote, what gets sent to Firestore if I put in the data I got from Firestore using the spread operator, without any editing, it includes every child from the above image. As in, I would have registration -> mapValue -> fields -> 0 -> mapValue -> fields -> etc. And, I don't want those mapValue and fields included, just that actual data (i.e. registration -> 0 -> {schoolID, uid, names, etc.}).
I am trying to access to a object into another object in my firebase database, i have a structure like this:
I want to get all the objects that have the email that i send by parameters, i am using .child to access to the childs into my object but i am not success with the query, this is my code
$ ref_db.child("/groups").child("members").orderByChild("email").equalTo(email).once("value", (snapshot)=>{
console.log(snapshot.val());
});
The snapshot.val() always is undefined.
could you help me with the query?
One efficient way to get "all the groups that have that email inside the members object" would be to denormalize you data and have another "main node" in your database where you store all "members" (i.e. their email) and the "groups" they belong to.
This means that each time you add a "member" node under a "group" (including its email) you will also add the group as a child of the member email, in this other "main node".
More concretely, here is how would be the database structure:
Your current structure:
- groups
- -LB9o....
...
- members
- -LB9qbd....
-email: xxxx#zzz.com
- -LBA7R....
-email: yyyyy#aaaa.com
And the extra structure:
- groupsByMembers
- xxxxxx#zzzcom
- Grupo1: true
- yyyyy#aaaacom
- Grupo1: true
- Grupo2: true
- bbbcccc#dddcom
- Grupo6: true
- Grupo8: true
Note that in the "extra structure" the dots within an email address are removed, since you cannot include a point in a node id. You will have to remove them accordingly when writing and querying.
This way you can easily query for the list of groups a member is belonging to, as shown below. Without the need to loop several times over several items. This dernomalization technique is quite classic in NoSQL databases.
const mailToSearchFor = xxxx.xx#zzz.com;
const ref = database.ref('/groupsByMembers/' + mailToSearchFor.replace(/\./g, ''));
ref.once('value', snapshot => {
const val = snapshot.val();
for (let key in val) {
if (val.hasOwnProperty(key)) {
console.log(key);
}
}
});
In order to write to the two database nodes simultaneously, use the update method as explained here https://firebase.google.com/docs/database/web/read-and-write#update_specific_fields
This is because you have a random key before members, you need to go through the path and not skip a node, to be able to access the values:
ref_db.child("groups").child("-LB9oWcnE0wXx8PbH4D").child("members").orderByChild("email").equalTo(email).once("value", (snapshot)=>{
console.log(snapshot.val());
});
Converting from Firebase to Firestore
I have a database that looks like the following
Above is a Firebase node for entries in a photo album. The top level node is the ID of the Album (reference from another node) and scoped under each albumID there is a node (auto gen id) for each album entry (or picture). The expanded album has two album entries, the first of which is expanded.
In Firebase - I could fetch the set of album entries (for a given album) by doing the following
I could offset to the subnode and read from there.
let dbKey = "byAlbum_entries"
let ref = Database.database().reference(fromURL: firebaseDB_URL)
let albumEntryReference = ref.child( dbKey).child( forAlbum.objectid)
albumEntryReference.observeSingleEvent(of : .value, with : { (snapshot) in ...
In Firestore how do I retrieve that same set of album entries?
is the correct syntax similar to below? Can I get documents from a document and Firestore will know I am asking to look for a subcollection because I said getDocuments()?
let dbKey = "byAlbum_entries"
let ref = Firestore.firestore()
let albumEntryReference = ref.collection( dbKey).document( forAlbum.objectid)
albumEntryReference.getDocuments() { (querySnapshot, err) in
if let err = err {
print("Error getting documents: \(err)")
// no album entries found
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
// loop through album entries
}
}
}
Part 2 --- Data Model Good Practices with FIRESTORE
It is better to restructure the data so that there is no subcollection and there is just a single collection where each entry includes both the albumID and the AlbumEntryID and I can just use a where clause to fetch only the album entries where albumID matches the one I am looking for? This would require an additional index whereas currently, in firebase, I can go directly to where I want to read and just read.
Even if restructuring does turn out to be better, I'd still appreciate help on the syntax to fetch all album entries for a given album as the database currently exists.
I have the following User Table structure in Firebase
As you can see in the user that I have opened, I have a Posts section, inside this post section holds the Id's all articles which have been posted by this user.
The issue I am facing is as follows:
When the user creates a new article it's saved within the Posts Table, after the save I return the newly generated ID which I then pass on to the user table, I trying to insert the newly created ID into the post section of the user, so I assumed the URL would be something like this:
Users/{UserId}/Posts
However all this does it create a new section called posts, it doesn't actually insert the record into the given area.
My code which isn't working is as follows:
let linkPost = [childautoID: true]
FIRDatabase.database().reference().child("Users/\(UserId)/Posts").child(UserId).setValue(linkPost)
FYI the two id's that are currently inside Posts I added manually.
I've also tried the following:
FIRDatabase.database().reference().child("Users/\(UserId)/Posts").setValue(linkPost)
However all this does it remove all existing Id's and then inserts the new id.
I prefer something like this. This automatically append the data without fetching first
FIRDatabase.database().reference().child("Users/\(UserId)/Posts").child(UserId).setValue(true)
To append a key-value pair in Firebase Database child node use this :-
Make a Firebase Database Reference to the Posts node of that currentUser FIRDatabase.database().reference().child("Users").child(FIRAuth.auth()!.currentUser!.uid).child("Posts")
Check if Posts node exists in your user's DB, If not then create one by :- parentRef.setValue([postId : "True"]) in else block.
But if Posts node does exist retrieve it as a NSMutableDictionary , set the new object to it, and then store the updated Dictionary to that node.
func storePostsToDB(postID :String!, userID : String! = FIRAuth.auth()!.currentUser!.uid){
let parentRef = FIRDatabase.database().reference().child("Users").child(userID).child("Posts")
parentRef.observeSingleEventOfType(.Value, withBlock: {(friendsList) in
if friendsList.exists(){
if let listDict = friendsList.value as? NSMutableDictionary{
listDict.setObject("True", forKey: postID)
parentRef.setValue(listDict)
}
}else{
parentRef.setValue([postID : "True"])
}
})
}
Calling the function:-
storePostsToDB("yourPostID")// If you want to store in the currentUser DB
storePostsToDB("yourPostID", userID : otherUserID)//If you want to store the post in some other users database with uid `otherUserID`
I'm working on a simple app where a User can follow other users. Users can star posts. And a user's feed is composed of posts that have been starred by users they follow. Pretty simple actually. However, this all gets complicated in Mongo and Meteor...
There are basically two way of modeling this that I can think of:
A user has a property, following, which is an array of userIds that the user follows. Also, a post has a property, starrers, which is an array of userIds that have starred this post. The good thing about this method is that publications are relatively simple:
Meteor.publish 'feed', (limit) ->
Posts.find({starrers: {$in: Meteor.users.findOne(#userId).following}}, {sort: {date: -1}, limit:limit})
We aren't reactively listening to who the user is following, but thats not too bad for now. The main problem with this approach is that (1) the individual documents will become large and inefficient if 1000000 people star a post. Another problem is that (2) it would be pain to keep track of information like when a user started following another user or when a user starred a post.
The other way of doing this is having two more collections, Stars and Follows. If a user stars a post, then we create a document with properties userId and postId. If a user follows another user, then we create a document with properties userId and followId. This gives us the advantage of smaller document sizes for Users and Posts, but complicated things when it comes to querying, especially because Mongo doesn't handle joins!
Now, I did some research and people seem to agree that the second choice is the right way to go. Now the problem I'm having is efficiently querying and publishing. Based on the Discover Meteor chapter about Advanced Publications, I created a publication that publishes the posts that are starred by user's followers -- sorted, and limited.
# a helper to handle stopping observeChanges
observer = (sub, func) ->
handle = null
sub.onStop ->
handle?.stop?()
() ->
handle?.stop?()
handle = func()
Meteor.publish 'feed', (limit) ->
sub = this
userId = #userId
followIds = null
eventIds = null
publishFollows = observer sub, () ->
followIds = {}
Follows.find({userId:userId}).observeChanges
added: (id, doc) ->
followIds[id] = doc.followId
sub.added('follows', id, doc)
publishStars()
removed: (id) ->
delete followIds[id]
sub.removed('follows', id)
publishStars()
publishStars = observer sub, () ->
eventIds = {}
Stars.find({userId: {$in: _.keys(followIds)}).observeChanges
added: (id, doc) ->
eventIds[id] = null
sub.added('stars', id, doc)
publishEvents()
removed: (id) ->
delete eventIds[id]
sub.removed('stars', id)
publishEvents()
publishEvents = observer sub, () ->
Events.find({_id: {$in: _.keys(eventIds)}}, {sort: {name:1, date:-1}, limit:limit}).observeChanges
added: (id, doc) ->
sub.added('events', id, doc)
changed: (id, fields) ->
sub.changed('events', id, fields)
removed: (id) ->
sub.removed('events', id)
While this works, it seems very limited at scale. Particularly, we have to compile a list of every starred post by every follower. The size of this list will grow very quickly. Then we do a huge $in query against all posts.
Another annoyance is querying for the feed on the client after we subscribe:
Meteor.subscribe("feed", 20)
posts = null
Tracker.autorun ->
followers = _.pluck(Follows.find({userId: Meteor.userId()}).fetch(), "followId")
starredPostIds = _.pluck(Stars.find({userId: {$in: followers}}).fetch(), "postId")
posts = Posts.find({_id: {$in: starredPostIds}}, {sort: {date: -1}, limit: 20}).fetch()
Its like we're doing all this work twice. First we do all the work on the server to publish the feed. Then we need to go through the exact same logic again on the client to get those posts...
My question here is a matter of design over everything. How can I efficiently design this feed based on followers staring posts? What collection / collection schemas should I use? How should I create the appropriate publication? How can I query for the feed on the client?
So it turns out that Mongo and "non-relational" databases simply aren't designed for relational data. Thus, there is no solution here with Mongo. I've ended up using Neo4j, but SQL would work fine as well.
meteor add reywood:publish-composite
Meteor.publishComposite('tweets', function(username) {
return {
find: function() {
// Find the current user's following users
return Relationships.find({ follower: username });
},
children: [{
find: function(relationship) {
// Find tweets from followed users
return Tweets.find({user: relationship.following});
}
}]
}
});
Meteor.publish('ownTweets', function(username) {
return Tweets.find({user: username});
});