Query object values by key, within an array using Cloud Firestore - arrays

I just started using Cloud Firestore for the CMS side of a web app hosted with the firebase platform. I know this is a very new product, but I have done my due diligence, reviewing all the docs: https://cloud.google.com/firestore/docs/, and SO forums: https://stackoverflow.com/questions/tagged/google-cloud-firestore- I can't find an answer, so I was hoping for some simple guidance.
I was wondering if there was a way to just query certain values of an object by key name, stored in an array. I would be using this for an selector element, For example- I have a very long list of stories as an array:
DATA
[ {title: "Title One", id: uid1, content: "very long string.."}, {title: "Title Two", id: uid2, content: "very long string.."} ...]
ELEMENT
<select onchange=fetchStoryByUID(story.val)>
<option value=story.uid>story.title</option>
</select>
I'd like to get just the story titles and uids to append to this element, without having to return all of the object keys and values. The response would be something like:
EXAMPLE QUERY RESPONSE (list of only titles and uids)
[{title: "Title One", id: uid2}, {title: "Title Two", id: uid2} ...]
Perhaps I need to re-think my data schema or something, but I feel that this is a common scenario that I didn't see covered in the docs. Any help would be greatly appreciated!! Thanks

Right now there is no way to retrieve partial documents using any of the Cloud Firestore mobile SDKs (Android, iOS, or JavaScript). Some of our server-side SDKs offer a select() method which allows you to do this. It does not affect pricing but it will save bandwidth.
In general you should store data in a document that you intend to fetch all at once. In your example it may make sense to store the content as another document and only fetch it when needed.

Related

When using React Query how do I make sure data is normalized across queries of a dataset?

I'm writing an sort of project management app that'll have a large number of "tasks" with a lot of properties within them that'll be rendered throughout the app. I am looking at using React Query to prefetch, cache locally, and update this data from the server.
One key architectural thing I want to get right is that when I query or mutate Tasks[123] that I affect a single underlying object in the state and not get stuck with duplicate data everywhere. On first glance React Query seems to be perfect for this job if the Query Keys are setup right. However in their examples they don't seem to do this (or I'm failing to understand).
In their Basic Example they fetch some Posts on start and query using queryClient.getQueryData(["post", post.id]). As far as I can tell this is causing the data to be duplicated if I look at the provided ReactQueryDevtools window in the example.
Am I correct in thinking the example should be rewritten to use something like queryClient.getQueryData(["posts", {id: post.id} ])?
That is indeed the way I am setting up my query keys, so that I can do: queryClient.invalidateQueries(['posts']) and it invalidates all of them. But sometimes, you need more fine granular control. If that's the case, I'd do:
["posts", "list", { filter: "all" }]
["posts", "list", { filter: "published" }]
["posts", "detail", 1]
["posts", "detail", 2]
that way, I can still tackle everything with ["posts"], all lists with ["posts", "list"], all details with ["posts", "detail"] and a specific detail with ["posts", "detail", id] etc.
It is also good practice to have a queryKeyFactory to create those keys, something like:
const postKeys = {
prefix: "posts",
lists: [postKeys.prefix, "list"],
list: (filter) => [...postKeys.lists, { filter }],
details: [postKeys.prefix, "detail"],
detail: (id) => [...postKeys.details, id]
}
Of course, I'm talking about "at scale" here. None of this is really needed for a todo app :)

How to perform Update in DynamoDB without sql-like UpdateExpression?

I have a very generic implementation of a Database class in my application.
I'd like to feed two things to the AWS DynamoDB Update: id and data {...} to be updated.
However, I only see the following SQL-like method in the docs:
{
TableName: "Music",
Key: {
"Artist":"No One You Know",
"SongTitle":"Call Me Today"
},
UpdateExpression: "SET RecordLabel = :label",
ExpressionAttributeValues: {
":label": "Global Records"
}
}
Is there really no way whatsoever to do this more MongoDB-style, without having to actually write queries? I would be looking more into something like this:
{
TableName: "Music",
Key: {
"id":"123-random-id"
},
Item: { "label": "Global Records" }
}
Unfortunately while this doesn't fail, it doesn't update anything.
I'm not familiar enough with MongoDB to compare its query syntax to DynamoDB.
However, if I understand your question correctly, you are asking if there isn't a simpler way to work with the DynamoDB API. In my experience, the answer to that question is No. Well, not using the AWS SDK directly.
There are several efforts in the community to build tooling that makes interaction with DynamoDB feel more natural. DynamoDB Toolbox is one such effort that has worked well for me, although it does not shield you from understanding the syntax of DynamoDB expressions.
For example, with DynamoDB Toolbox, your example code snippet could be written as:
let item = {
id: "123-random-id",
label: "Global Records",
}
await MyEntity.update(item)
You didn't explain what you hoped Item: { "label": "Global Records" } would do:
If you want to replace the entire item, you can do with with a PutItem.
However, if what you wanted to do is to only replace the label attribute of the item, and not touch any of the other existing attributes of the item, then you really need to use UpdateItem like you did. You can either use the UpdateExpression syntax as you did - which isn't too hard, honestly - but there is also an older syntax AttributeUpdates which you may like better because you just need to define the attributes you want to replace - without preparing an "expression". The AttributeUpdates syntax is older and considered "legacy" but still works, and many applications use it so I doubt it will go away any time soon.

I want to use Algolia to fetch products by SKU

I've got a product that has related product data, which gives me the object below:
[{
link_type: "related"
linked_product_sku: "gcn-202-1"
linked_product_type: "configurable"
position: 3
sku: "EMBN-101-CL-BK-CY"
}]
The linked_product_sku is the related product. There might be multiple products in the array and I'm wondering how I would fetch that product data via the sku using Algolia.
I want to use Algolia instead of changing the data we are getting from magento because Algolia is so fast, I just can't see on their documentation how you would go about doing this.
Hoping someone has ran into the same issue.
https://community.algolia.com/react-instantsearch/
Important to know I'm doing this in React.
From your description it sounds like linked_product_sku will be an array of values. You intend to use this array of values to retrieve objects from Algolia, and not for display to the user. Is that correct?
To make sure you get the exact object you want from Algolia, I would suggest referencing the objectID of these linked products (you would therefore have to update your existing records).
Your old object looked like this:
[{
link_type: "related"
linked_product_sku: "gcn-202-1"
linked_product_type: "configurable"
position: 3
sku: "EMBN-101-CL-BK-CY"
}]
Your updated object might look like this:
[{
link_type: "related"
linked_product_objectIDs: ["1234", "5678"]
linked_product_type: "configurable"
position: 3
sku: "EMBN-101-CL-BK-CY"
}]
There is no magic bullet to resolve your custom use-case, but in essence you could then use these objectIDs in a filters parameter of a <Configure /> widget.
<Configure
filters={'objectID:1234 OR objectID:5678'}
/>

How to create the following data structure in a NoSQL environment

Intro
I have a FireStore database similar to a social media db, with 3 collections Users, Events, and EventUpdates. My goal is to create a feed with eventUpdates created by me and my friends. So i have to expand my database with friendship connections. But i struggle with 3 problems, and hopefully somebody here can push me in the right direction to solve these.
Problem/Question 1:
I added username and user image to the EventUpdate model so it's easier to query. I've heard denormalise is the way to go in a NoSQL database. But if a user updates his user image, i've to update all eventUpdates created by that user. Sounds like something you don't wanne do. But is there a better way to do this?
Problem/Question 2:
How can i create a data structure that is optimised for performing the following query: get eventUpdates from me and my friends ordered by date.
Problem/Question 3:
How to store likes? I can keep a counter in a eventUpdate. But this becomes a problem when i denormalise eventUpdates (see current solution underneath EDIT)..
Data structure example .
{
"users": {
"1": { "name": "Jack", "imageUrl": "http://lorempixel.nl" }
},
"events": {
"A": {
"name": "BeerFestival",
"date": "2018/09/05",
"creatorId": "1"
}
},
"eventUpdates": {
"1": {
"timestamp": "13243543",
"creatorId: "1",
"creatorName": "Jack",
"creatorImageUrl": "http://lorempixel.nl",
"eventId": "A",
"message": "Lorem ipsum"
}
}
}
EDIT
OK, after some trial and error i ended up with the following structure. This structure seems work, but my problem with this solution is that i need to make a lot of write calls to update a single eventUpdate because of all the copies in each feed (1000 followers means 1000 copies). And it looks like i need to do that a lot.
I would like for example to add a like button to each event update. This trigger an update on all EventUpdate copies. For me it looks like firebase is not suited for my project and i'm thinking of replacing it with a SQL DB, or can anyone here change my mind with a better solution?
{
"users": {
"user1": { "name": "Jack",
"imageUrl": "http://lorempixel.nl",
"followers": ["user1"]
}
},
"feeds": {
"user1": {
"eventUpdates": {
"1": {
"timestamp": "13243543",
"creatorId: "1",
"eventId": "A",
"message": "Lorem ipsum"
}
},
"following": {
"user1": {
"name": "Jack",
"imageUrl": "http://lorempixel.nl",
"followers": ["user1"]
}
}
},
"events": {
"A": {
"name": "BeerFestival",
"date": "2018/09/05",
"creatorId": "1"
}
}
}
I added username and user image to the EventUpdate model so it's easier to query. I've heard denormalise is the way to go in a NoSQL database.
That's right, denormalization and is a common practice when it comes to Firebase. If you are new to NoQSL databases, I recommend you see this video, Denormalization is normal with the Firebase Database for a better understanding. It is for Firebase realtime database but same rules apply to Cloud Firestore.
But if a user updates his user image, i've to update all eventUpdates created by that user. Sounds like something you don't wanne do. But is there a better way to do this?
Yes, that's also correct. You need to update all the places where that image exists. Because you have chosen google-cloud-firestore as a tag, I recommend you see my answer from this post because in case of many write operations, Firestore might be a little costly. Please also see Firestore pricing plans.
Regarding Firestore, instead of holding an entire object you can only hold a reference to a picture. In this case, there is nothing that you need to update. It's always a trade between these two techniques and unfortunately there is no way between. You either hold objects or only references to objects. For that, please see my answer from this post.
How can i create a data structure that is optimised for performing the following query: get eventUpdates from me and my friends ordered by date.
As I see, your schema is more a Firebase realtime database schema more than a Cloud Firestore. And to answer to your question, yes you can create. So talking about Firestore, you can create a collection named eventUpdates that can hold eventUpdate objects and to query it according to a timestamp, a query like this is needed:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
CollectionReference eventUpdatesRef = rootRef.collection("eventUpdates");
Query query = eventUpdatesRef.orderBy("timestamp", Query.Direction.ASCENDING);
But please note that the timestamp field should be of type Date and not long. Please also take a look at my answer from this post, to see how you can add a date property in a Cloud Firestore database.
How to store likes? I can keep a counter in a eventUpdate. But this becomes a problem when i denormalise eventUpdates (see current solution underneath EDIT)
You can simply add likes but I recommend you see the last part of my answer from this post. So you might consider adding that count in a Firebase realtime database rather than in Cloud Firestore. Both databases work very well together.
This structure seems work, but my problem with this solution is that i need to make a lot of write calls to update a single eventUpdate because of all the copies in each feed (1000 followers means 1000 copies). And it looks like i need to do that a lot.
You might also take a look at my answer from this post.
For me it looks like firebase is not suited for my project and i'm thinking of replacing it with a SQL DB, or can anyone here change my mind with a better solution?
I don't think this way. There are many apps out there that have the exact mechanism as yours and are working very well.
If you want your feed items to be in sync with the real users data (new profile image when the user changes it for example) you can simply store the user ID in the eventUpdate document. This way you don't have to keep them in sync manually, and every time you have to display the item in the feed you could simply fetch user data, and easily query many eventUpdates on userId and created_at fields ( assuming you have them ).
To implement likes in your feed the solution depends on a bunch of things like traffic volume.
The simplest way is to update a likes field with a transaction, but Firestore has a maximum updates frequency on a single document of 1 second. Plus, a transaction can easily fail if more than 5 transactions are trying to update the same document.
To implement a more solid likes system take a look at this page from the official Firebase docs.
Firestore has a different approach to the NoSQL world. Once you know the data you will use (as You already do) there are some very important points about what architecture the data will have. And It depends a lot about how the data grows, what kind of queries you will need and how often you will use them. Some cases You can create a root collection that aggregates data and queries might be easier.
There is a great video from Firebase Channel that might help. Check it out!
How to Structure Your Data | Get to Know Cloud Firestore #5
https://www.youtube.com/watch?v=haMOUb3KVSo
[UPDATED] December 26th
Others videos that might help to model and query your data is these videos:
How to Connect Firebase Users to their Data - 3 Methods
https://www.youtube.com/watch?v=jm66TSlVtcc
How to NOT get a 30K Firebase Bill
https://www.youtube.com/watch?v=Lb-Pnytoi-8
Model Relational Data in Firestore NoSQL
https://www.youtube.com/watch?v=jm66TSlVtcc

Creating dynamic input form in Ionic v1

I have an older project in Ionic v1 (Angular 1.x) where users can create groups and add members. I need to add a capability to the App for the group Admin to ask questions to group members - sort of a dynamic form. The questions can be of three types - a Yes/No (to be shown as a toggle), String (input Text) and a question with choices (to be shown as a select drop down). The number of questions can vary.
The questions are stored along the lines of the hash below.
{
id1: {q: "question1", type: "String"},
id2: {q: "question2", type: "Yes/No"}
id3: {q: "question3", type: "Choice", choices: "Choice1, Choice2, Choice3"},
id4: {q: "question4", type: "String"},
id5: {q: "question5", type: "Yes/No"}
id6: {q: "question6", type: "Choice", choices: "Choice4, Choice5, Choice6"}
}
I am trying to think of the best way to be able to show these questions dynamically to the user and collect the answers but not quite sure what's the best way to do this. I started looking at trying to create a Directive but wanted to get advice before spending too much time down a path.
Look forward to your thoughts and suggestions.
-S
I would suggest a simpler approach than using a directive, unless you have the need to reuse this as a component across multiple views. Start by having ng-repeat="(key, value) in questions" where questions is the name of your object. Then simply show/hide different forms for each question based on value.type: ng-if="value.type=='String'"...
The form you show in each case would be a checkbox for yes/no, textarea or input(type=text) for string and radio buttons for choice.
I'm not providing full code as I assume you know enough Angular to be able to create those - if not each case is an easy google away. One further thing you'll need is a place on each question for the model (i.e., the user's answer), which might be value.answer. For example:
<input type="checkbox" ng-model="value.answer">
Does this make sense?

Resources