Sanity io show blog posts by author - sanity

I'm learning Sanity io CMS and GROQ. I've build a simple blog using one the Sanity predefined blog schema. I've managed to show posts, a single post and the author page, showing name, image and bio.
What I would like to do is to show a list of the blog posts belonging to the author in his page. In the posts schema there is a reference to the author:
{
name: 'author',
title: 'Author',
type: 'reference',
to: {type: 'author'},
},
I've tried to add an array reference to post in the Author schema, but the result is that in the CMS I need to manually add posts to the author. While, I'd like to query the author and return the posts belonging to the author since there's a reference to it in the posts schema , but cannot find how to do it. Is it possibly more a GROQ query than a schema problem, or both?

Found the answer myself by looking at the Sanity documentation in the Query Cheat Sheet page. It was not a scheme problem but rather a GROQ query one. In the author page, in the query that shows the author bio etc. I've added another part in which I'm querying for the posts by that author:
// GROQ Query
sanityClient
.fetch(
`*[_type == "author"]{
name,
bio,
"authorImage": image.asset->url,
"posts": *[_type == "post" && author._ref in *[_type=="author" && name == name ]._id ]{
title,
"slug": slug.current,
}
}`
)
.then((data) => setAuthor(data[0]))
.catch(console.error);
Then I can simply map through "posts", using the "slug" to build the URL to the single post.
{author.posts &&
author.posts.map((p) => {
return (
<li>
<a href={`/post/${p.slug}`}>{p.title}</a>
</li>
);
})
}

if you want to reach author by his posts, you can do this:
*[_type == "post"]{
_id,
title,
author->{ // here to reach author name and image you should type ->
name,
image
},
description,
mainImage,
slug
}

If you have an array of authors on a post the following syntax will retrieve all posts for the current author:
`*[_type == "author"]{
name,
bio,
"authorImage": image.asset->url,
"posts": *[_type == "post" && author._ref in authors[]->author._id ]{
title,
"slug": slug.current,
}
}`

Related

GROQ: Query one-to-many relationship with parameter as query input

I have a blog built in NextJS, backed by Sanity. I want to start tagging posts with tags/categories.
Each post may have many categories.
Category is a reference on post:
defineField({
name: 'category',
title: 'Category',
type: 'array',
of: [
{
type: 'reference',
to: [
{
type: 'category',
},
],
},
],
}),
This is my GROQ query:
*[_type == "post" && count((category[]->slug.current)[# in ['dogs']]) > 0] {
_id,
title,
date,
excerpt,
coverImage,
"slug": slug.current,
"author": author->{name, picture},
"categories": category[]-> {name, slug}
}
The above works, when it is hardcoded, but swapping out 'dogs' with $slug for example will cause the query to fail. (Where $slug is a param provided)
*[_type == "post" && count((category[]->slug.current)[# in [$slug]]) > 0]
{
$slug: 'travel'
}
How do I make the above dynamic?
Returns all documents that are storefronts // within 10 miles of the user-provided currentLocation parameter ; // For a given $currentLocation geopoint
I can't believe it. Rookie mistake. I needed to pay more attention in the Sanity IDE. (To be fair there was a UI bug that hid the actual issue)
The param should not contain the $. E.g the following works in the GROQ IDE.
{
slug: 'travel'
}

FaunaDB: how to fetch a custom column

I'm just learning FaunaDB and FQL and having some trouble (mainly because I come from MySQL). I can successfully query a table (eg: users) and fetch a specific user. This user has a property users.expiry_date which is a faunadb Time() type.
What I would like to do is know if this date has expired by using the function LT(Now(), users.expiry_date), but I don't know how to create this query. Do I have to create an Index first?
So in short, just fetching one of the users documents gets me this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z")
}
But I would like to get this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z"),
has_expired: true,
}
I have this FQL query now (ignore oauthInfo):
Query(
Let(
{
oauthInfo: Select(['data'], Get(Ref(Collection('user_oauth_info'), refId))),
user: Select(['data'], Get(Select(['user_id'], Var('oauthInfo'))))
},
Merge({ oauthInfo: Var('oauthInfo') }, { user: Var('user') })
)
)
How would I do the equivalent of the mySQL query SELECT users.*, IF(users.expiry_date < NOW(), 1, 0) as is_expired FROM users in FQL?
Your use of Let and Merge show that you are thinking about FQL in a good way. These are functions that can go a long way to making your queries more organized and readable!
I will start with some notes, but they will be relevant to the final answer, so please stick with me.
The Query function
https://docs.fauna.com/fauna/current/api/fql/functions/query
First, you should not need to wrap anything in the Query function, here. Query is necessary for defining functions in FQL that will be run later, for example, in the User-Defined Function body. You will always see it as Query(Lambda(...)).
Fauna IDs
https://docs.fauna.com/fauna/current/learn/understanding/documents
Remember that Fauna assigns unique IDs for every Document for you. When I see fields named id, that is a bit of a red flag, so I want to highlight that. There are plenty of reasons that you might store some business-ID in a Document, but be sure that you need it.
Getting an ID
A Document in Fauna is shaped like:
{
ref: Ref(Collection("users"), "101"), // <-- "id" is 101
ts: 1641508095450000,
data: { /* ... */ }
}
In the JS driver you can use this id by using documentResult.ref.id (other drivers can do this in similar ways)
You can access the ID directly in FQL as well. You use the Select function.
Let(
{
user: Get(Select(['user_id'], Var('oauthInfo')))
id: Select(["ref", "id"], Var("user"))
},
Var("id")
)
More about the Select function.
https://docs.fauna.com/fauna/current/api/fql/functions/select
You are already using Select and that's the function you are looking for. It's what you use to grab any piece of an object or array.
Here's a contrived example that gets the zip code for the 3rd user in the Collection:
Let(
{
page: Paginate(Documents(Collection("user")),
},
Select(["data", 2, "data", "address", "zip"], Var("user"))
)
Bring it together
That said, your Let function is a great start. Let's break things down into smaller steps.
Let(
{
oauthInfo_ref: Ref(Collection('user_oauth_info'), refId)
oauthInfo_doc: Get(Var("oathInfoRef")),
// make sure that user_oath_info.user_id is a full Ref, not just a number
user_ref: Select(["data", "user_id"], Var("oauthInfo_doc"))
user_doc: Get(Var("user_ref")),
user_id: Select("id", Var("user_ref")),
// calculate expired
expiry_date: Select(["data", "expiry_date"], Var("user_doc")),
has_expired: LT(Now(), Var("expiry_date"))
},
// if the data does not overlap, Merge is not required.
// you can build plain objects in FQL
{
oauthInfo: Var("oauthInfo_doc"), // entire Document
user: Var("user_doc"), // entire Document
has_expired: Var("has_expired") // an extra field
}
)
Instead of returning the auth info and user as separate points if you do want to Merge them and/or add additional fields, then feel free to do that
// ...
Merge(
Select("data", Var("user_doc")), // just the data
{
user_id: Var("user_id"), // added field
has_expired: Var("has_expired") // added field
}
)
)

Is there a way to Index a doc to Elasticsearch with a specific _id filed?

I'm looking to simulate a state where I have a specific _id field inside an index.
Let's assume I want to take the EXACT same log from index1 in my example and index it into index2.
Like so:
This is my index1
{
_index: "index-number-one",
_type: "doc",
_id: "S0meSpec!f!cID",
_score: 1,
_source: {
message: "message1",
type: "type1",
tags: [
"_bla"],
number: 3
}
}
Now I want that exact same log in my index2
{
_index: "index-number-two",
_type: "doc",
_id: "S0meSpec!f!cID",
_score: 1,
_source: {
message: "message1",
type: "type1",
tags: [
"_bla"],
number: 3
}
}
Couldn't find an API in Elasticsearch that can insert a doc to an Index with a specific _id field... (?)
If this action isn't possible so that the Elasticsearch cluster won't have duplications in the _id field, I can imagine it's because they want to keep the ability to search a doc by it's _id
field which needs to be unique, in that case, assume that I don't mind deleting the entire doc from index1 (maybe save it aside as some variable in my code), but in the end, I need the doc in index2, to have the EXACT _id as index1 once had.
And if there's a way to edit an existing _id field it would also solve my problem.
Can anyone please shed any light on how to achieve that goal?
answer to myself,
I found that it can be done in a POST request on the index like so:
POST twitter/test-index-1234/abctype/Som3Cust0mID
{
"user" : "kimchy",
"post_date" : "2009-11-15T14:12:12",
"message" : "trying out Elasticsearch"
}
And the outcome in ES:
{
_index: "test-index-1234",
_type: "abctype",
_id: "Som3Cust0mID",
_score: 1,
_source: {
user: "kimchy",
post_date: "2009-11-15T14:12:12",
message: "trying out Elasticsearch"
}
}
It is definitely possible to do this. IDs are unique per index, not per cluster.
Check the reindex API, it copies one index onto another and keeps the document IDs.
It is also possible to change the ID using a script inside the reindex call.

Excluding posts that are in a certain category

New to graphQL here to bear with me. I've read the docs and googled as best as I can but all I got were simplified answers and they didn't help.
I'm using WordPress as my data source.
I've been able to show posts that are in a specific category but I'm struggling to show all posts EXCEPT those in a certain category.
I want to get all posts EXCLUDING ones that are in the "Portfolio" category (ID: 10)
The top line "works" but it's only looking at the id of the post itself, not the categories it is in. The second line me what my brain thinks it should be but it doesn't work.
Please help if you can :)
query Home($page: Int) {
allWordPressPost(page: $page, perPage: 10, filter: { id: { nin: [12] }}) #paginate {
# allWordPressPost(page: $page, perPage: 10, filter: { categories.id: { nin: [12] }}) #paginate {
pageInfo {
totalPages
currentPage
}
edges {
node {
id
title
path
excerpt
categories {
id
title
path
}
}
}
}
}
Did you have a look to this repo?
I don't know if it helps, but this starter is probably better rather than the official one for this purpose!

MongoDB: How retrieve data that is newly constructed instead of original documents in the collection?

I have a collection in which documents are all in this format:
{"user_id": ObjectId, "book_id": ObjectId}
It represents the relationship between user and book, which is also one-to-many, that means, a user can have more than one books.
Now I got three book_id, for example:
["507f191e810c19729de860ea", "507f191e810c19729de345ez", "507f191e810c19729de860efr"]
I want to query out the users who have these three books, because the result I want is not the document in this collection, but a newly constructed array of user_id, it seems complicated and I have no idea about how to make the query, please help me.
NOTE:
The reason why I didn't use the structure like:
{"user_id": ObjectId, "book_ids": [ObjectId, ...]}
is because in my system, books increase frequently and have no limit in amount, in other words, user may read thousands of books, so I think it's better to use the traditional way to store it.
This question is not restricted by MongoDB, you can answer it in relational database thoughts.
Using a regular find you cannot get back all user_id fields who own all the book_id's because you normalized your collection (flattened it).
You can do it, if you use aggregation framework:
db.collection.aggregate([
{
$match: {
book_id: {
$in: ["507f191e810c19729de860ea",
"507f191e810c19729de345ez",
"507f191e810c19729de860efr" ]
}
}
},
{
$group: {
_id: "$user_id",
count: { $sum: 1 }
}
},
{
$match: {
count: 3
}
},
{
$group: {
_id: null,
users: { $addToSet: "$_id" }
}
}
]);
What this does is filters through the pipeline only for documents which match one of the three book_id values, then it groups by user_id and counts how many matches that user got. If they got three they pass to the next pipeline operation which groups them into an array of user_ids. This solution assumes that each 'user_id,book_id' record can only appear once in the original collection.