I'm creating a blog where each blog page will show a map using leaflet with a GPX route on it. Below the map will be some statistics and some text and images.
I have the text and images part defined in mark down so i figured the way to handle this would be to define my gpx filename in the frontmatter like so:
---
title: Awesome Blog Post Title
author: Cancrizan
date: 2021-01-04
gpxFile: activity4835702422
---
BLOG POST here
where the field gpxFile refers to a a file in my project src/gpx/activity4835702422.gpx.
I've written a transformer plugin that will read in the GPX file so that it can be queried like this:
query MyQuery {
allActivity4835702422Gpx {
edges {
node {
geometry {
coordinates
}
}
}
}
}
and outputs something like this:
{
"data": {
"allActivity4835702422Gpx": {
"edges": [
{
"node": {
"geometry": {
"coordinates": [
[
-1.2134187016636133,
52.92038678191602,
29.399999618530273
],
[
-1.2134256586432457,
52.92039977386594,
29.399999618530273
],
...,
]
}
}
}
]
}
},
"extensions": {}
}
I want to access that node based on the frontmatter of the markdown file and i'm not sure how?
Can anyone suggest a solution or am i going about this the wrong way?
The structure and the mindset you've followed is perfectly valid, the only part you're missing is to pass the gpxFile to your template in order to create another query based on that parameter.
Your gatsby-node.js should look like something like this:
const path = require("path")
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const result = await graphql(
`
{
allMarkdownRemark(limit: 1000) {
edges {
node {
frontmatter {
title
author
date
gpxFile
slug
}
}
}
}
}
`
)
if (result.errors) {
reporter.panicOnBuild(`Error while running GraphQL query.`)
return
}
const postTemplate= path.resolve(`src/templates/post.js`);
result.data.allMarkdownRemark.edges.forEach(({ node }) => {
const path = node.frontmatter.path
createPage({
path: `/posts/${node.frontmatter.slug}`,
component: postTemplate,
context: {
gpxFile: node.frontmatter.gpxFile,
path: node.frontmatter.slug
}
})
})
}
The idea is to use context API to pass data (gpxFileData and path) to your template (postTemplate) to use it as a filter for your markdown files.
In your postTemplate, your query should look like:
export const postData = graphql`
query getArticleData($path: String!, $gpxFile: String) {
post: markdownRemark (fields: {frontmatter: { slug: { eq: $path }}}) {
html
excerpt (pruneLength: 350)
frontmatter {
title
author
date
gpxFile
slug
}
}
gpxFileData: allFile(relativePath: { in: $gpxFile })
# your gpx data here
}
}
`;
It's quite self-explanatory, basically, you are passing via context the necessary data to make a query in your template (gpxFileData) from your gatsby-node.js. There, you can create a new query, allFile, filtering by relativePath (you may need to access to file directly or use absolutePath, test it in localhost:8000/___graphql) and retrieve the whole data using props.data.post and post.data.gpxFileData.
Disclaimer: I'm assuming that you've set your filesystem (gatsby-source-filesystem) properly to use allFile across your .gpx files.
Related
I am trying to get soft deleted records (deletedAt column) when using query from TypeOrmQueryService but looks like there is no way to do that. I know that I can use withDeleted if I use find or findOne but I cannot switch to these methods or use a query builder since it would require a lot of changed in the front-end.
#QueryService(Patient)
export class PatientQueryService extends TypeOrmQueryService<Patient> {
constructor(#InjectRepository(Patient) repo: Repository<Patient>) {
super(repo);
}
async getOnePatient(currentUser: User, patientId: number) {
const result = await super.query({
paging: { limit: 1 },
filter: { id: { eq: 1 } },
});
}
}
I have the following structure
User {
image: Asset
...
}
Comment {
author: User
...
}
BlogArticle {
slug: Text
author: User
comments: Comment[]
}
When I pull entries with the following method
const articles = await client.getEntries({ content_type: "BlogArticle" })
console.log(articles.entries.fields.comments)
I only get the sys property for the author
[
{
author: {
sys: {
...
}
fields ??????
}
}
]
PS: This is the case for all types that come in second level of nesting
I checked the docs and the apis but with no luck
Any help ?
I created a similar content model and was able to get the fields of the Author successfully. One thing you can do is use the include parameter. With the include parameter, your code should look as follow:
const articles = await client.getEntries({ content_type: "BlogArticle", include: 2 })
console.log(articles.entries.fields.comments)
You can learn more about it here
Reading the docs on Customizing the GraphQL Schema I'm trying to see if I have frontmatter, code:
---
title: Sample Post
date: 2019-04-01
fooId:
---
is it possible to set a default value for fooId? If I live it empty in the markdown file I get:
Cannot query field "fooId" on type "MdxFrontmatter".
If you don't expect "youTubeId" to exist on the type "MdxFrontmatter"
it is most likely a typo. However, if you expect "youTubeId" to exist
there are a couple of solutions to common problems:
If you added a new data source and/or changed something inside gatsby-node/gatsby-config, please try a restart of your development
server.
You want to optionally use your field "fooId" and right now it is not used anywhere.
It is recommended to explicitly type your GraphQL schema if you want
to use optional fields.
Attempt
exports.createSchemaCustomization = ({ actions, schema }) => {
const { createTypes } = actions
const typeDefs = [
'type MarkdownRemark implements Node { frontmatter: Frontmatter }',
schema.buildObjectType({
name: 'Frontmatter',
fields: {
tags: {
type: '[String!]',
resolve(source) {
const { fooId } = source
if (fooId === null) return 'foo'
return fooId
},
},
},
}),
]
createTypes(typeDefs)
}
When I implement the above code I still get the same error in the terminal. Is there a way in gatsby-node.js I can default fooId?
Try it like this:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type MdxFrontmatter implements Node {
fooId: String
}
`
createTypes(typeDefs)
}
Is not a "default" value per se as you mention but using type definitions you are able to customize the expected outcome of the Node when fetched. By default, all (mostly) the values are set as non-nullable (in the case above as String!). Using the previous type definition, you are setting the fooId as a nullable value, meaning that is not required, without the exclamation mark, !, what represents the nullability/non-nullability, allowing the fooId to be empty.
Just wanted to point out that if you use exports.sourceNodes in Gatsby 4.19.2:
exports.sourceNodes = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type MdxFrontmatter implements Node {
fooId: String
}
`
createTypes(typeDefs)
}
you'll get a deprecation warning which was originally posted and to prevent this issue you should use createSchemaCustomization:
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes } = actions
const typeDefs = `
type MdxFrontmatter implements Node {
fooId: String
}
`
createTypes(typeDefs)
}
I'm trying to implement cursor pagination and followed the examples in the doc but I keep getting an error saying 'Cannot query field "cursor" on type "Query"'.
I'm aware that the "cursor" field doesn't actually exist on the Accounts schema...but from what I'm reading from the docs.. you have to include it somewhere in the gql`` query. Furthermore, not sure if I'm missing anything but I'm a bit confused of how to structure my query to allow cursor pagination.
Original Query: (running this gives me no error)
const AccountsQuery = gql`
query {
accounts {
accountName
accountId
}
}
`;
New Query: (this gives "cannot find cursor field on accounts" error)
const AccountsQuery = gql`
query Accounts($cursor: String){
accounts(cursor: $cursor) {
cursor
accountName
accountId
}
}
`;
GraphQL wrapper:
export default graphql(AccountsQuery, {
props: ({ data: { loading, cursor, accounts, fetchmore } }) => ({
loading,
accounts,
loadMoreEntries() {
return fetchmore({
query: AccountsQuery,
variables: {
cursor: cursor,
},
updateQuery: (previousResult, { fetchMoreResult }) => {
const previousEntry = previousResult.entry;
const newAccounts = fetchMoreResult.accounts;
return {
cursor: fetchMoreResult.cursor,
entry: {
accounts: [...newAccounts, ...previousEntry]
},
};
},
})
}
})
})(QuickViewContainer);
Any help would be appreciated to getting cursor pagination working!
Sounds like the cursor field isn't getting implemented on the server. Your Account type needs to have that field like so:
Account {
cursor
accountName
accountId
}
For a convention on how to do cursor pagination, you should follow the standard Relay spec. You can read more about how it's implemented here in a Relay-compliant GraphQL API.
This would make your query look like this:
query {
viewer {
allAccounts {
edges {
cursor
node {
accountName
accountId
}
}
}
}
}
Each edge account has a cursor that corresponds to a node and will be auto-populated with globally-unique opaque cursor IDs from the server.
Hope this helps!
I am currently using MongoDB with node.js and Mongoose to perform geospatial searches.
I'm working with the following documents and collections:
waypoints are documents that contain location and other metadata (just is there, not otherwise relevant for this question)
targets collection contains 1...n waypoints
sources collection contains exactly 1 waypoint
Simple example of what these documents may look like:
// Target
{
waypoints: [
{
loc: [61.24, 22.24],
time: 0
},
{
loc: [61.25, 22.24],
time: 1
},
{
loc: [61.26, 22.24],
time: 2
},
]
}
// Source
{
waypoint: {
loc: [61.24, 22.24],
time: 0
}
}
So my question is this:
Given that we have a specific target document (like the one above), what is the easiest way to find all source documents near (within distance of MAX_DISTANCE) any of the given waypoints in the target?
Matching for single waypoint is trivial:
Source.find({
'from.loc': {
$within: {
$center: [target.waypoints[0].loc, MAX_DISTANCE],
$uniqueDocs: true
}
}
})
However I'm struggling to find the solution of how to match any of the given waypoints. For instance the following query doesn't work:
Source.find({
$or: [
{
'waypoint.loc': {
$within: {
$center: [target.waypoints[0].loc, MAX_DISTANCE],
$uniqueDocs: true
}
}
},
{
'waypoint.loc': {
$within: {
$center: [target.waypoints[1].loc, MAX_DISTANCE],
$uniqueDocs: true
}
}
},
{
'waypoint.loc': {
$within: {
$center: [target.waypoints[2].loc, MAX_DISTANCE],
$uniqueDocs: true
}
}
}
]
})
Any ideas why this doesn't work and what would be the alternative?
All help is much appreciated!
P.S. I'm using MongoDB v2.0.5, Mongoose 2.7.4 & node v0.8.7
$or queries are implemented as separate queries internally anyway, so aside from a lack of elegance, something like the following works w/o too much bloat (with a little help from the underscore library):
var nearSources = {}, count = target.waypoints.length;
target.waypoints.forEach(function (waypoint) {
Source.find({
'waypoint.loc': {
$within: {
$center: [waypoint.loc, MAX_DISTANCE],
$uniqueDocs: true
}
}
}, function (err, sources) {
if (sources) {
// Add the unique sources to the nearSources object by _id.
sources.forEach(function (source) {
nearSources[source._id] = source;
});
}
if (--count === 0) {
// Done! Convert nearSources to an array of source docs.
nearSources = _.values(nearSources);
}
});
});