Quick check of my data structure in Firebase database - data-structures

I'm quite new to firebase. I've been reading posts and watching some data-modelling tutorials about firebase. General advice is always:
Save duplicated data in many places. This way reading is gonna be quick as query syntax is limited. Saving will be more expensive but it occurs rarely.
This point of view makes much sense, i can certenaily understand it. That being said, i'd like to ask you for a quick check to see if is the data structure that i want to use the proper one. I have a bit of experience with mongoDB which has DB references, not existing in firebase. It just feels weird to duplicate data that much. I guess it's SQL ancestry as well, but who knows.
I'm trying to create some sort of boilerplate for a future multi-user app. Prequisites:
Each document has one author
Each document can have multiple members
Each user can be a mamber of unlimited number of documents
Open documents are publicly accessible, private documents are accessible only by members
This is schematic representation of what i came up with. Three dots mean that siblings are possible.
users: {
*userUID*: {
uid: *userUID*
name: User One
bio: Lorem ipsum
birthdate: 01-01-2000
ownedDocuments: {
*documentUID*: {
uid: *documentUID*
title: Document title
coverImage: imageUrl
status: private
membersCount: 3
}
...
}
documents: {
*documentUID*: {
uid: *documentUID*
title: Document title
coverImage: imageUrl
status: private
}
...
}
}
...
}
documents: {
*documentUID* {
uid: *documentUID*
title: Document title
coverImage: imageUrl
status: private
membersCount: 3
author: {
uid: *userUID*
name: User One
}
members: {
*userUID*
uid: *userUID*
name: User One
...
}
}
...
}
As i have said before - it feels wrong probably because of what i'm used to in SQL. So if this is (more or less) valid approach then i'll be happy to hear it. Of course if it's wrong, of there are any remarks / optimalisations / anything i'd love to read it.
Thank You

Related

Any way to split up multiple Fragment expansions for a GraphQL query into multiple calls?

Context
This problem is likely predicated on certain choices, some of which are changeable and some of which are not. We are using the following technologies and frameworks:
Relay / React / TypeScript
ContentStack (CMS)
Problem
I'm attempting to create a highly customizable page that can be built from multiple kinds of UI components based on the data presented to it (to allow pages to be built using a CMS using prefab UI in an unpredictable order).
My first attempt at this was to create a set of fragments for the potential UI components that may be referenced in an array:
query CustomPageQuery {
title
description
customContentConnection {
edges {
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
"""
Further fragments are added here as we add more kinds of UI
"""
}
}
}
}
In the CMS we're using (ContentStack), the complexity of this query has grown to the point that it is rejected because it requires too many calls to the database in a single query. For that reason, I'm hoping there's a way I can split up the calls for the fragments so that they are not part of the initial query, or some similar solution that results in splitting up this query into multiple pieces.
I was hoping the #defer directive would solve this for me, but it's not supported by relay-compiler.
Any ideas?
Sadly #defer is still not a standard so it is not supported by most implementation (since you would also need the server to support it).
I am not sure if I understand the problem correctly, but you might want to look more toward using #skip or #include to only fetch the fragment you need depending on the type of the thing. But it would require the frontend to know what it wants to query beforehand.
query CustomPageQuery($hero: Boolean, $tweet: Boolean, $video: Boolean) {
title
description
customContentConnection {
edges {
node {
... HeroFragment #include(if: $hero)
... TweetBlockFragment #include(if: $tweet)
... EmbeddedVideoFragment #include(if: $video)
}
}
}
}
Generally you want to be able to discriminate the type without having to do a database query. So say:
type Hero {
id: ID
name: String
}
type Tweet {
id: ID
content: String
}
union Content = Hero | Tweet
{
Content: {
__resolveType: (parent, ctx) => {
// That should be able to resolve the type without a DB query
},
}
}
Once that is passed, each fragment is then resolved, making more database queries. If those are not properly batched with dataloaders then you have a N+1 problem. I am not sure how much control (if at all) you have on the backend but there is no silver bullet for your problem.
If you can't make optimizations on the backend then I would suggest trying to limit the connection. They seem to be using cursor based pagination, so you start with say first: 10 and once the first batch is returned, you can query the next elements by setting the after to the last cursor of the previous batch:
query CustomPageQuery($after: String) {
customContentConnection(first: 10, after: $after) {
edges {
cursor
node {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}
pageInfo {
hasNextPage
}
}
}
As a last resort, you could try to first fetch all the IDs and then do subsequent queries to the CMS for each id (using aliases I guess) or type (if you can filter on the connection field). But I feel dirty just writing it so avoid it if you can.
{
one: node(id: "UUID1") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
two: node(id: "UUID2") {
... HeroFragment
... TweetBlockFragment
... EmbeddedVideoFragment
}
}

GitHub v4 API: Calculate content specific reaction count on a comment

I am trying to get the reaction count for each content using the Github v4 API (GraphQL). Can anyone suggest how can I achieve this?
Github supports the following reactions:
THUMBS_UP
THUMBS_DOWN
LAUGH
HOORAY
CONFUSED
HEART
ROCKET
EYES
For each reaction, I want a count which denotes the number of people who reacted. For eg. I am referring to this comment -> #2190.
Github API provides a feature called reaction group. Refer to the following query...
{
repository(owner: "sindresorhus", name: "refined-github") {
issue(number: 2190) {
reactionGroups {
content
users {
totalCount
}
}
}
}
}
Hope this solves your problem!

How can I define and use a UUID field?

I need a UUID field in a content type, with the help of the introduction below I have modified the file "MyType.settings.json".
https://strapi.io/documentation/3.x.x/guides/models.html#define-the-attributes
"uid": {
"default": "",
"type": "uuid"
},
I thought a UUID is automatically saved, but nothing happens.
How can I define and use a UUID field? Can someone give me a hint?
Should I also modify the file \api\MyType\controllers\MyType.js?
Thanks in advance!
Benjamin
You will have to use uuid node module.
So keep your attribute and in the lifeCyle functions, set your uuid with the lib.
'use strict';
const uuid = require('uuid');
module.exports = {
beforeCreate: async (model) => {
model.set('uid', uuid());
}
};
You will probably also want a v4 UUID, which is the fastest to generate by a very large margin and will give you the least chance for collision with all other things being equal (can be 10x or even faster), which is what 99% of all people want to generate UUIDs for:
Work in the global UUID community, remain as unique as possible, not collide with anything else.
In addition, v4 is more secure than v1 as v1 includes the time it was made and which hardware it was created on, and has an insanely higher collision rate as today nobody respects the node field and just fills it with entropy which defeated the whole purpose of v1.
The reasons to use v4 over v1 is so stark in fact that this information should be made much more public and widespread. It is night and day. All major frameworks that only seek entropy now use v4.
import { v4 as uuid } from "uuid";
// Then just...
const myUUID = uuid()
UUID v1 was created because people, even the genius computer scientists that came up with UUID, didn't fully grasp that the address space of 128 bits alone was much better than fancy timing and node tracking. UUID v1 is a great case of over-engineering.
In order to auto-generate uuid both after an entity has been saved via API and admin UI, use the lifecycle-method of your model, not the controller. The file /api/myType/models/myType.js thus may look like this:
'use strict';
const { v4: uuid } = require('uuid');
module.exports = {
lifecycles: {
beforeCreate: async (data) => {
if (!data.uuid) {
data.uuid = uuid();
}
},
}
};
In Strapi V4 create file ./src/api/[api-name]/content-types/[api-name]/lifecycles.js
with following content:
"use strict";
const { v4: uuid } = require("uuid");
module.exports = {
beforeCreate: async (data) => {
if (!data.params.data.uuid) {
data.params.data.uuid = uuid();
}
},
};

RestTemplate: how to deal with metadata

I have to consume a REST webservice which has the following syntax for all requests:
{
message: "OK",
success: true,
results: 1,
data: {
name: "Berlin",
lat: 52.2,
lon: 13.25,
id: 1701
},
(...)
}
When I try to deserialize using:
City source = getRestTemplate().getForObject("http://myws.com/cities/{cityId}", City.class, "1701");
The default HttpMessageConverter tries to look for attributes named message, success, results in the City bean, and since it cannot find them, it's throwing an Exception.
I wonder if there's any way to take advantage of the default HttpMessageConverter but somehow tell it to interpret message, success and results differently, or do I have to create my own HttpMessageConverter altogether?
I had the same issue and the way around it is to create a wrapper object that contains the meta data fields. This method turned out to be quite useful and made it incredibly easy to get at the data.
In your case, the core model is obviously the city but then the wrapper object would be something like this
class CityWrapper{
String message;
String success;
Integer results;
#JsonProperty("data")
City city;
}
When I tackled it I had a list of data coming back and that worked fine with
#JsonProperty("data")
List<City> cities;

Google Places Autocomplete Remove State and Country from Result

https://google-developers.appspot.com/maps/documentation/javascript/examples/places-autocomplete
I have a page similar to the above google places autocomplete demo url whereby if i type Buckingham Palace. It will return results of
Buckingham Palace Road, London, United Kingdom
Buckingham Palace Shop, Buckingham Palace Road, Victoria, London,
United Kingdom
and etc. How do i remove London, United Kingdom from the results?
It seems Google have no interest in sorting it out anytime soon. I have had to resort to the following, might be sufficient for others' needs as well:
document.addEventListener('DOMNodeInserted', function(event) {
var target = $(event.target);
if (target.hasClass('pac-item')) {
target.html(target.html().replace(/, Australia<\/span>$/, "</span>"));
}
});
note that pac-item is the class used on each suggestion. See Google Reference for other classes used. The container with pac-container class seems to drop the items when it is not shown and add new ones when it displays so if these pac-items are getting added to the DOM, it means suggestions are on their way to be displayed and pac-container is about to become visible.
just worked this out so open to improvements.
This also is not a complete solution. When selecting a suggestion with the country removed, autocomplete still adds the country to the geocoding! place_changed is too late a stage to change that so please see the solution above as only part of the answer. I'll update this again once I figure out the rest.
--- update
personally, i ended up not using the google autocomplete at all as i couldn't find a way around the problem of the autocomplete still showing the country once a modified suggestion is selected. a more usable approach was to use twitter typeahead and use the google APIs for getting the suggestions only. this gave me more control over the suggestions but obviously requires more manual work to make up for lost functionality
Here is How I made it work with jQuery auto-complete. Hope it helps someone.
$("#Search").autocomplete({
source: function (request, response) {
var options = {
input: request.term,
types: ['(cities)'],
region: 'US',
componentRestrictions: { country: "us" }
};
function callback(predictions, status) {
for (var i = 0, prediction; prediction = predictions[i]; i++) {
results.push(prediction.description.replace(/, United States$/, ""));
}
response(results);
}
var service = new google.maps.places.AutocompleteService();
service.getPlacePredictions(options, callback);
var results = [];
}
});
This is not possible without manually processing the results. If you think that it would be a useful feature, please file a Places API - Feature Request.

Resources