Firebase database modeling - firebase

I'm starting out using firebase and even though I've spent 2 days on firebase db and nosql I'm still not sure if I'm on the right track.
I'm working on a fairly simple usecase/dataset:
I have content-items (title, coordinates, ...) which can be attributed to subcategories. Subcategories are attributed to categories.
Sound simple? Ok, I created the following:
{
"contentitems": {
"item1": {
"title": "i am a content item",
"coordinates": ""
},
"item2": { ... },
"item3": { ... }
},
"subcategories": {
"sc1": {
"title": "i am a subcategory",
"contentitems": {
"item1" : true,
"item2" : true
}
},
"sc2": {
"title": "i am another subcategory",
"contentitems": {
"item1" : true,
"item134" : true
}
},
"sc3": { ... }
},
"categories": {
"c1": {
"title" : "i am a cateogry,
"subcategories": {
"sc1" : true,
"sc2" : true
},
},
"c2": { ... },
"c3": { ... }
}
}
As far as I can tell this should be fine for the following usecase (in an app):
I click on a category from the categories list and open a list with all related subcategories. When I click on a subcategory I open a list with all related content-items.
Is this correct? Are there any pitfalls I can't see at the moment? Am I completly off?
2n part of my question:
How can I achieve a geolocation search with a bounding box? Do I have to create another 'table' called geocoordinates like this:
"geocoordinates": {
"coords" : {
lat: "45",
lon: "44"
}
}
How can I get all data within the bounding box?

Related

Struggling with filtering Drupal data in Gatsby GraphQL query

I am using Drupal 8 JSON:API to expose data to my Gatsby site. I have built a GraphQL query to expose a list of "officers" which contains a field with a relationship to a set of "service periods". The data returned by the API is correct, but I would like to filter for only one specific child record (service period) and can not figure out how to do that. My query is:
officerList: allGroupContentLodgeGroupNodeOfficer(filter: {relationships: {entity_id: {relationships: {field_position: {elemMatch: {relationships: {field_service_period: {drupal_internal__tid: {eq: 203}}}}}}}}}) {
edges {
node {
r: relationships {
entity_id {
r: relationships {
field_position {
r: relationships {
field_position {
name
}
field_service_period {
name
drupal_internal__tid
}
}
}
}
title
}
}
}
}
}
}
The resulting JSON set is:
"data": {
"officerList": {
"edges": [
{
"node": {
"r": {
"entity_id": {
"r": {
"field_position": [
{
"r": {
"field_position": {
"name": "Governor"
},
"field_service_period": {
"name": "2018 - 2019",
"drupal_internal__tid": 203
}
}
},
{
"r": {
"field_position": {
"name": "Junior Past Governor"
},
"field_service_period": {
"name": "2019 - 2020",
"drupal_internal__tid": 204
}
}
}
]
},
"title": "Tom Jones"
}
}
}
}
]
}
}
}
I understand the resulting set is correct because the child is within the root. However, I can not see how to filter the full query to include only certain child records. Is this even possible? I have seen some implementations of GraphQL that seem to allow filters to be placed on children, but I don't think this is possible in Gatsby.
I have searched everywhere for possible solutions and have been banging my head against the wall for a few days. Any insight is GREATLY appreciated!
TIA!

Using Gatsby `createResolvers` to set default image if GraphQL returns null?

I'm working on a gatsby site using gatsby-source-wordpress to source posts for the blog. However, if any of the WordPress posts do not include a featured image this causes the build to fail. I understand that this is expected behavior.
Here is the build error I am seeing:
29 | {posts.map(({ node: post }, index) => (
30 | <li key={post.id} {...post}>
> 31 | <Img fixed={post.featured_media.localFile.childImageSharp.fixed} />
| ^
32 | <p>
33 | <Link to={`/insights/${post.slug}`}>
34 | {post.title}
WebpackError: TypeError: Cannot read property 'localFile' of null
This is caused by the resulting query, which is returning a null result in the second node because there is no featured image on the post:
{
"data": {
"allWordpressPost": {
"edges": [
{
"node": {
"id": "28ec9054-5b05-5f94-adcb-dcbfc14659b1",
"featured_media": {
"id": "f12d613b-e544-560b-a86f-cd0a7f87801e",
"localFile": {
"id": "7fca2893-ff80-5270-9765-d17d3dc21ac2",
"url": "https://www.mycustomdomain.com/wp-content/uploads/2020/01/some-featured-image.jpg"
}
}
}
},
{
"node": {
"id": "91a236ed-39d5-5efc-8bed-290d8344b660",
"featured_media": null
}
}
]
}
}
}
How I would like to fix:
As an ideal solution, I would like to use schema customization to set a default image if there is no featured image in WordPress. But I am at a total loss how to correctly do so. I am working from this documentation to guide me, but I'm just not getting my head wrapped around it properly.
A similar working example:
Tag data is similar to featured images in that the query returns null if the post has no tags. However I am able to set a default undefined tag using createResolvers like so:
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
wordpress__POST: {
tags: {
resolve(source, args, context, info) {
const { tags } = source
if (tags === null || (Array.isArray(tags) && !tags.length)) {
return [
{
id: 'undefined',
name: 'undefined',
slug: 'undefined',
}
]
} else {
return info.originalResolver(source, args, context, info)
}
},
},
},
}
createResolvers(resolvers)
}
And this works as shown in the following query results:
{
"data": {
"allWordpressPost": {
"edges": [
{
"node": {
"id": "28ec9054-5b05-5f94-adcb-dcbfc14659b1",
"tags": [
{
"id": "undefined"
}
]
}
},
{
"node": {
"id": "91a236ed-39d5-5efc-8bed-290d8344b660",
"tags": [
{
"id": "50449e18-bef7-566a-a3eb-9f7990084afb"
},
{
"id": "8635ff58-2997-510a-9eea-fe2b88f30781"
},
{
"id": "97029bee-4dec-5198-95af-8464393f71e3"
}
]
}
}
]
}
}
}
What I tried for images (isn't working...)
When it comes to nested nodes and image files I'm at a total loss. I am heading in the following direction based on this article and this code example, but so far it isn't working:
exports.createResolvers = ({
actions,
cache,
createNodeId,
createResolvers,
store,
reporter,
}) => {
const { createNode } = actions
const resolvers = {
wordpress__POST: {
featured_media: {
type: `File`,
resolve(source, args, context, info) {
return createRemoteFileNode({
url: 'https://www.mycustomdomain.com/wp-content/uploads/2017/05/placeholder.png',
store,
cache,
createNode,
createNodeId,
reporter,
})
},
},
},
}
createResolvers(resolvers)
}
I realize the above code does not have an if else statement, so the expectation is that all featured images would be replaced by the placeholder image. However the resulting GraphQL query is unaffected (as shown at top).
Can anyone point me in the right direction here? I can't seem to wrap my head around what information I can find out there.
WebpackError: TypeError: Cannot read property 'localFile' of null
'localFile' of null means that nulled is a parent of localfile - featured_media ... you can see that in results:
"featured_media": null
... so you're trying to fix localfile while you should work on featured_media level
why?
You can easily render conditionally [in react] what you need (placeholde, component) on nulled nodes ... why at all you're trying to fix graphql response?

alexa - audioPlayer.Play issue displaying content on Echo Show Now Playing screen

I am having issues understanding how to display images on the Echo Show inside the audioPlayer 'Now Playing' screen.
I am currently playing an audio file and want to display an image on the 'Now Playing' screen. The closest I have been able to get is the following code which displays the image and title just before the audio starts, but then disappears immediately and the Echo Show goes to the 'Now Playing' screen with no background image and no metadata. I feel I'm close, but just cannot understand how to update the 'Now Playing' screen, rather than the screen that comes immediately before it.
This is part of the code (which works as per above):
var handlers = {
'LaunchRequest': function() {
this.emit('PlayStream');
},
'PlayStream': function() {
let builder = new Alexa.templateBuilders.BodyTemplate1Builder();
let template = builder.setTitle('Test Title')
.setBackgroundImage(makeImage('https://link_to_my_image.png'))
.setTextContent(makePlainText('Test Text'))
.build();
this.response.speak('OK.').
audioPlayerPlay(
'REPLACE_ALL',
stream.url,
stream.url,
null,
0)
.renderTemplate(template);
this.emit(':responseReady');
}
I have been looking at this page https://developer.amazon.com/docs/custom-skills/audioplayer-interface-reference.html but cannot understand how to convert the structure of what is on that page into my code. I assume that, from the code on the page :
{
"type": "AudioPlayer.Play",
"playBehavior": "valid playBehavior value such as ENQUEUE",
"audioItem": {
"stream": {
"url": "https://url-of-the-stream-to-play",
"token": "opaque token representing this stream",
"expectedPreviousToken": "opaque token representing the previous stream",
"offsetInMilliseconds": 0
},
"metadata": {
"title": "title of the track to display",
"subtitle": "subtitle of the track to display",
"art": {
"sources": [
{
"url": "https://url-of-the-album-art-image.png"
}
]
},
"backgroundImage": {
"sources": [
{
"url": "https://url-of-the-background-image.png"
}
]
}
}
}
}
I somehow need to get this part :
"metadata": {
"title": "title of the track to display",
"subtitle": "subtitle of the track to display",
"art": {
"sources": [
{
"url": "https://url-of-the-album-art-image.png"
}
]
},
Into this block of my code :
audioPlayerPlay(
'REPLACE_ALL',
streamInfo.url,
streamInfo.url,
null,
0)
.renderTemplate(template);
(and could probably lose the .renderTemplate(template); part as it only flashes up briefly before the 'Now Playing' screen loads anyway.
Any ideas on how to achieve this?
Thanks!
Update :
I have added the following to index.js:
var metadata = {
title: "title of the track to display",
subtitle: "subtitle of the track to display",
art: {
sources: {
url: "https://url-of-the-album-art-image.png"
}
}
};
And modified the audioPlayer as follows :
audioPlayerPlay(
'REPLACE_ALL',
stream.url,
stream.url,
null,
0,
metadata)
.renderTemplate(template);
And modified the responseBuilder.js as indicated:
audioPlayerPlay(behavior, url, token, expectedPreviousToken, offsetInMilliseconds, metadata) {
const audioPlayerDirective = {
type : DIRECTIVE_TYPES.AUDIOPLAYER.PLAY,
playBehavior: behavior,
audioItem: {
stream: {
url: url,
token: token,
expectedPreviousToken: expectedPreviousToken,
offsetInMilliseconds: offsetInMilliseconds,
metadata : metadata
}
}
};
this._addDirective(audioPlayerDirective);
return this;
}
But I'm still not getting anything displayed on the 'Now Playing' screen.
For some reason the Echo Show is not updating in realtime and needs to be rebooted before it will show whatever is passed in the metadata variable, which is why I wasn't seeing any results.
Simply passing a variable as such works fine. I just need to find out why the content gets stuck on the 'Now Playing' screen and requires a reboot to work.
var "metadata": {
"title": "title of the track to display",
"subtitle": "subtitle of the track to display",
"art": {
"sources": [
{
"url": "https://url-of-the-album-art-image.png"
}
]
},
Just define your metadata as below. And pass it as a 6th argument to audioPlayerPlay;
"metadata": {
"title": "title of the track to display",
"subtitle": "subtitle of the track to display",
"art": {
"sources": [
{
"url": "https://url-of-the-album-art-image.png"
}
]
},
audioPlayerPlay(
'REPLACE_ALL',
streamInfo.url,
streamInfo.url,
null,
0,metadata)
P.S. For this to work properly, You have to modify your node modules which you ll be zipping and uploading to lambda.
steps -
Go to your node_modules\alexa-sdk\lib and open responseBuilder file in it. And modify the code as follows-
audioPlayerPlay(behavior, url, token, expectedPreviousToken, offsetInMilliseconds, **metadata**) {
const audioPlayerDirective = {
type : DIRECTIVE_TYPES.AUDIOPLAYER.PLAY,
playBehavior: behavior,
audioItem: {
stream: {
url: url,
token: token,
expectedPreviousToken: expectedPreviousToken,
offsetInMilliseconds: offsetInMilliseconds
},
**metadata : metadata**
}
};
this._addDirective(audioPlayerDirective);
return this;
}
P.S. - The node module modifications required only if you are using alexa-sdk version 1.
I know it's been years since this question was originally posted, but for those like me who stumble upon this now, make sure you use a unique token in the play directive because metadata is cached using that token.
See the yellow Important note in the following section https://developer.amazon.com/en-US/docs/alexa/custom-skills/audioplayer-interface-reference.html#images
Important: The metadata for a given audio stream is identified by the
audioItem.stream.token included in the Play directive. Note that the
metadata associated with a particular audioItem.stream.token may be
cached in the Alexa service for up to five days, so changes to the
metadata (such as a different image, or a change to the title text)
may not be reflected immediately on the device. For instance, you may
notice this when testing if you experiment with different images or
title text for the same audio stream. You can send a new Play
directive with a different audioItem.stream.token to clear the cache.
And an example payload with a token:
{
"type": "AudioPlayer.Play",
"playBehavior": "valid playBehavior value such as ENQUEUE",
"audioItem": {
"stream": {
"url": "https://cdn.example.com/url-of-the-stream-to-play",
"token": "opaque token representing this stream",
"expectedPreviousToken": "opaque token representing the previous stream",
"offsetInMilliseconds": 0,
"captionData":{
"content": "WEBVTT\n\n00:00.000 --> 00:02.107\n<00:00.006>My <00:00.0192>Audio <00:01.232>Captions.\n",
"type": "WEBVTT"
}
},
"metadata": {
"title": "title of the track to display",
"subtitle": "subtitle of the track to display",
"art": {
"sources": [
{
"url": "https://cdn.example.com/url-of-the-album-art-image.png"
}
]
},
"backgroundImage": {
"sources": [
{
"url": "https://cdn.example.com/url-of-the-background-image.png"
}
]
}
}
}
}

Firebase NoSQL Data modeling - Like counter

I'm currently learning Firebase/NoSQL database modeling. I just ended watching the Firebase for SQL developers, but I still have few doubts.
Let's say I'm creating Instagram-styled app where users could share their photos and each user could like each photo.
So I would like to achieve two things:
1. Know which user has liked which photo. (So only one like per user for photo)
2. How many likes each photo has.
My current database looks like this:
{
"images": {
"100": {
"imageUrl": "../../image.png",
},
"101": {
"imageUrl": "../../image.png",
}
},
"users": {
"200": {
"name": "user1"
},
"201": {
"name": "user2"
}
},
"likes": {
"100": 1,
"101": 2
},
"likesPerUser": {
"200": {
"100": "true"
},
"201": {
"100": "true",
"101": "true"
}
}
"imagesPerUser": {
"200": {
"101": "true"
},
"201": {
"100": "true"
}
}
My question is related to the counter, that counts how many likes each photo has. Would the best practice be that I have them as their own "root"-object (current model) OR to create key-value pair for "likes" under the photo (and maybe do the same for authorID)?
This Firebase Sample recommends having the counter(likes_count) under each post. And also having a node(likes) with a list/lookup . Like this:
"images": {
"100": {
"imageUrl": "../../image.png",
"likes_count":2,
"likes":{
"200":true,
"201":true
}
},
"101": {
"imageUrl": "../../image.png",
"likes_count":1,
"likes":{
"201":true
}
}
}
This way you'll ensure only one like per user, because keys must be unique under a Firebase Realtime Database node, and the user ids are used as keys under the likes_count node. You can also know which users liked the photo because their uids are there. And obviously, you can see how many likes a photo has by accessing the counter.

highmaps get country name on click event

$('#container').highcharts('Map', {
title : {
text : 'Highmaps basic demo'
},
subtitle : {
text : 'Source map: Africa'
},
mapNavigation: {
enabled: true,
buttonOptions: {
verticalAlign: 'bottom'
}
},
colorAxis: {
min: 0
},
series : [{
data : data,
mapData: Highcharts.maps['custom/africa'],
joinBy: 'hc-key',
name: 'Random data',
states: {
hover: {
color: '#BADA55'
}
},
dataLabels: {
enabled: true,
format: '{point.name}'
}
}]
});
});
http://jsfiddle.net/gh/get/jquery/1.11.0/highslide-software/highcharts.com/tree/master/samples/mapdata/custom/africa
I am using this fiddle and I want to get the country name on click event on the country. Anybody can help me with the example or link to the API of this? I read the API but could not find, I guess I am missing some point.
thanks in advance
Pretty simple, just add this:
plotOptions:{
series:{
point:{
events:{
click: function(){
alert(this.name);
}
}
}
}
}
The this in the point scope represents the clicked point, therfore you have access to it's properties.
http://jsfiddle.net/farz5vq2/

Resources