I have a cloud firestore with data structured this way:
baskets
basket 1
basketID
userID
description
products
productID
productID
basket 2
basketID
userID
description
products
productID
productID
...
basket N
products
product A
productID
description
productImg
price
product B
productID
description
productImg
price
...
product Z
This structure in order to avoid duplicating detailed product data inside each basket document.
I created a vue component to display at once both the basket data and the products data associated to the basket, while trying to limit and optimize firebase queries. I have sucessfully binded the basket data like this:
data() {
return {
basket: [],
basket_products: []
};
},
created() {
this.$bind('basket', db.collection("baskets").where("basketID", "==", this.basketID))
}
However I'm struggling to bind the basket_products data for the current basket. I'm looking for something like this:
this.$bind('basket_products', db.collection("products").where("productID", 'in', ONE OF THE PRODUCT_IDS WITHIN CURRENT BASKET, EQUIVALENT TO this.basket[0].products))
Glad my pointer helped!
Just for completeness sake - the best solution as mentioned is to simply store references, vuefire automatically picks up on those and saves them accordingly as part of the baskets thanks to the maxRefsDepth option.
Aside from that, you could potentially play around with the serialize option of vuefire to modify (and query additional) data when getting the baskets.
But this would be fairly complex, you might be better off just manually subscribing using the firebase SDK instead.
Related
I use firebase database filter orderBy seller_id in nested list, why is the result null? thank you 🙏
:rules Firebase
var url = Uri.parse(
'https://MeDatabase.firebasedatabase.app/orders.json?auth=$authToken&orderBy="list_produk/seller_id"&equalTo="$idUser"');
try {
final response = await http.get(url);
if (response.statusCode >= 400) {
throw EasyLoading.showError('Cek Koneksi Internetmu!');
}
final extractData =
convert.jsonDecode(response.body) as Map<String, dynamic>;
print('inii orderan');
print(extractData);
if (extractData == null) {
return _orders = [];
}
} catch (e) {
throw e;
}
You're trying to index and order each order on its list_produk/seller_id property.
But if I look at your JSON data, there is no list_produk/seller_id property under the order. There's only list_produk/1/seller_id, and presumably list_produk/0/seller_id. Those are not the same as what you use in your rules/query, so the query returns no results.
Firebase Realtime Database queries function on a single, flat list of nodes. The value you order/filter on must exist at a fixed path under each direct child node.
So in your example, you can search across all orders on for example id_user, or you can search across the seller_id of the products in a specific order, but you can't search across all sellers of products in all orders.
In other words: your current data structure makes it easy to find the products and sellers per order, it does however not make it each to find the orders per product across all users. To allow that you'll want to add an additional data structure, typically known as a reverse index, that maps from each seller_id to the orders that contain products from that seller.
For more on this, I recommend checking out:
Firebase query if child of child contains a value
Firebase Query Double Nested
Many to Many relationship in Firebase
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 new to firebase cloud firestore and need help understanding it a bit.
I am working on a project of the following structure.
Each "Restaurant" document contains its own "products" subcollection.
Here, I wanted to run a query to get all the products by different
restaurants that contain the tag "coffee" and are in a specific
Pincode "234144".
I tried Collection group queries by adding Pincode to each product owned by a particular restaurant but changing a Pincode would cost a lot, as all products would have to be edited, I guess.
Is there any efficient way of doing it or is it not possible in this
database in an efficient way?
Please let me know what do you think... Thank you.
If I understand correctly, you want to retrieve all products with a certain tag and pincode (I suppose this is similar to a postal zipcode?). There really are only two alternatives that I can think of:
Collection group query
As you mention, you can store both tag and pincode in product documents. Then perform a single collection group query along these lines (pardon the javascript, but I am not familiar with Dart, it should be very similar):
var products = await firestore.collectionGroup('products')
.where('tag', '==', 'coffee')
.where('pincode', '==', '234144').get();
As you have noted, with this solution you need to keep pincode in each product This piece of data is duplicated and it is normal to feel that it should be avoided because it is wasteful and dangerous (can go out of sync), but this is the way to go! It is called denormalization. This is well explained in this video by Todd Kerpelman.
You can then create a Cloud Function triggered by restaurant update to keep pincode in products in sync with the corresponding pincode in restaurants
Query restaurants then products
To keep the pincode in restaurants only, you have to do your query in two steps: first filter restaurants in the certain pincode, then filter products by tag:
// 1 - retrieve restaurants in specific pincode
var restaurants = await firestore.collection('restaurants').where('pincode', '==', '234144').get();
// 2 - For each retrieved restaurant, retrieve all products matching the tag
var products = [];
for(let i = 0; i < restaurants.docs.length; ++i) {
var p = await restaurants.docs[i].collection("products").where("tag", "==", "coffee");
products.push(p);
}
With this method, no need to duplicate pincode in each product, however your queries are less optimal, because you load potentially useless restaurants that do not serve coffee in your pincode.
I have a similar data structure, and Louis Coulet's answer helped me a lot. However, I ran into an issue, as my node/firebase environment complained that restaurants.docs[i].collection was not a function. Instead, I had to use restaurants.docs[i].ref.collection.
So, in my case, here's the code that wound up working (using the original poster's "restaurants/products" data model as an example):
// 1 - retrieve restaurants in specific pincode
const restaurants = await firestore.collection('restaurants').where('pincode', '==', '234144').get();
// 2 - For each retrieved restaurant, retrieve all products matching the tag 'coffee'
const products = [];
for (let i = 0; i < restaurants.docs.length; i++) {
await restaurants.docs[i].ref
.collection('products')
.where('tag', '==', 'coffee')
.get()
.then(s => Promise.all(s.docs.map(d => {
products.push(d);
})));
}
I have found that I need to return the sub-collection contents via some kind of promise, otherwise, after I try to get the data in the variable -- products, in this case -- it just shows up as undefined.
After I populate the products variable, I am then able to read its contents like this:
products.forEach(p => {
console.log(p.data().name);
console.log(p.data().tag);
}
I hope someone finds this useful, and many, MANY thanks to Louis Coulet for sending me/us down the correct path with this sub-collection headache :-)
I'm building a social app using Firebase. I store posts in Firebase like this:
posts: {
"postid": {
author: "userid"
text: "",
date: "timestamp"
category: "categoryid"
likes: 23
}
}
Each post belong to a category and it's possible to like posts.
Now, I'm trying to show posts that belong to a specific category, sorted by the number of likes. It's possible I also want to limit the filter by date, to show only the most recent, most liked posts in a category. How can I do this?
Firebase query functionality doesn't seem to support multiple queries like this, which seems strange...
You can use only one ordering function with Firebase database queries, but proper data structure will allow you to query by multiple fields.
In your case you want to order by category. Rather than have category as a property, it can act as an index under posts:
posts: {
"categoryid": {
"postid": {
author: "userid"
text: "",
date: "timestamp",
category: "categoryid",
likes: 23
}
}
}
Now you can write a query to get all the posts underneath a specific category.
let postsRef = Firebase(url: "<my-firebase-app>/posts")
let categoryId = "my-category"
let categoryRef = postsRef.childByAppendingPath(categoryId)
let query = categoryRef.queryOrderedByChild("date")
query.observeEventType(.ChildAdded) { (snap: FDataSnapshot!) {
print(snap.value)
}
The code above creates a reference for posts by a specific category, and orders by the date. The multiple querying is possible by the data structure. The callback closure fires off for each individual item underneath the specified category.
If you want to query further, you'll have to do a client-side filtering of the data.
I have a Firebase table named Orders:
{productID: xyz,
userID: abc,
quantity: 100}
There is another table Products containing product details. When showing a user's orders, I need to show some product details along with each order. How can I use $firebaseArray for this purpose?
$firebaseArray seems can only go with a table. Here, I'd need to query each ordered product's detail and add the detail to each order. Is the $extend the way to go? If so, any good example would be very appreciated.
Just saw this one. What you want to use is the NormalizedCollection. This way you can write your statement something like this:
var fb = new Firebase('https://<instance>.firebaseio.com');
var norm = new Firebase.util.NormalizedCollection(
fb.child('products'),
fb.child('productDetails')
);
var productsRef = $firebaseArray(norm.ref());