Resolve reference in Block Content in Sanity.io - sanity

I have the Content Block in Sanity like so:
export default {
title: "Block Content",
name: "blockContent",
type: "array",
of: [
/// stuff here
{
title: "Book",
type: "reference",
to: [{ type: "book" }],
},
],
};
When making a query like
'*[_type == "post"]{...,body[]{..., asset->{..., "_key": _id}, markDefs[]{..., _type == "internaLink" => {"slug": #.reference->slug}}}';
I get the reference, but I want to return the full Document. I tried every method but the documentation only explain references outside Content Blocks and those methods aren't working.
This is what's returned from the Query:
_createdAt: '2020-12-07T14:43:34Z',
_id: '9d628aa6-aba7-4b53-aa9f-c6e97583baf9',
_rev: 'ZZ0GkIKCRvD0tdMQPywPfl',
_type: 'post',
_updatedAt: '2020-12-07T14:43:34Z',
body: [
{
_key: '4184df372bae',
_type: 'block',
children: [Array],
markDefs: [],
style: 'normal'
},
{
_key: '56bed8835a7d',
_ref: 'dc2eefee-2200-43e1-99c7-ea989dda16ba',
_type: 'reference'
}
],
title: 'Example'

Solved, I'm writing the solution here as I found nothing on the web.
Basically I tried anything randomly until I got the desired result.
The query is:
_type=="reference"=>^->

I had a similar situation where I had was grabbing a story object that had a body which was a blockContent, and I had just added a blog reference as a block option to the body.
I solved it by using this query:
const query = groq`*[_type == "story" && slug.current == $slug][0]{
title,
description,
body[]{
_type == 'blog' => #->,
_type != 'blog' => #,
}
}`
It loops through all the array items inside of the body and if a specific one is a blog, then it dereferences it by using ->, otherwise it outputs the already existing value.

Related

Sanity HTTP API: How to add an array of references?

What is the correct way to add an array of references using the Sanity HTTP API?
I'm able to add a single reference:
const mutations = [{
createOrReplace: {
_id: '123',
_type: 'cms.article',
title: 'An article',
author: {
_ref: '12f90f00-8cfb-4161-8bea-26180',
_type: 'reference'
},
}
}]
I expected adding an array of references to existing tags would work (it returns with Status 200)
const mutations = [{
createOrReplace: {
_id: '123',
_type: 'cms.article',
title: 'An article',
author: {
_ref: '12f90f00-8cfb-4161-8bea-26180',
_type: 'reference'
},
tags: [
{
_ref: 'foo',
_type: 'reference'
},
{
_ref: 'bar',
_type: 'reference'
},
]
}
}]
But Sanity Studio crashes with Error: Unsupported path segment {} when I attempt to view the document.
It may help to break this down:
First, a reference is an object that contains a _type and a _ref. The _type will always be 'reference' and the _ref will always be the _id of the document you're referencing.
Second, an array can be either an array of strings or of objects. Either way, any item in an array needs a unique _key.
So, putting all of that together, your mutation would look like this:
field: [
{
_key: '<some-uuid>'
_ref: '<_id-of-document>',
_type: 'reference'
},
]
When it comes to generating keys, you can use an external package (uuid is common) or if you're using the JS client auto generate keys by passing { autoGenerateArrayKeys: true } to your commit().

Indexes: Search by Boolean?

I'm having some trouble with FaunaDB Indexes. FQL is quite powerful but the docs seem to be limited (for now) to only a few examples/use cases. (Searching by String)
I have a collection of Orders, with a few fields: status, id, client, material and date.
My goal is to search/filter for orders depending on their Status, OPEN OR CLOSED (Boolean true/false).
Here is the Index I created:
CreateIndex({
name: "orders_all_by_open_asc",
unique: false,
serialized: true,
source: Collection("orders"),
terms: [{ field: ["data", "status"] }],
values: [
{ field: ["data", "unique_id"] },
{ field: ["data", "client"] },
{ field: ["data", "material"] },
{ field: ["data", "date"] }
]
}
So with this Index, I want to specify either TRUE or FALSE and get all corresponding orders, including their data (fields).
I'm having two problems:
When I pass TRUE OR FALSE using the Javascript Driver, nothing is returned :( Is it possible to search by Booleans at all, or only by String/Number?
Here is my Query (in FQL, using the Shell):
Match(Index("orders_all_by_open_asc"), true)
And unfortunately, nothing is returned. I'm probably doing this wrong.
Second (slightly unrelated) question. When I create an Index and specify a bunch of Values, it seems the data returned is in Array format, with only the values, not the Fields. An example:
[
1001,
"client1",
"concrete",
"2021-04-13T00:00:00.000Z",
],
[
1002,
"client2",
"wood",
"2021-04-13T00:00:00.000Z",
]
This format is bad for me, because my front-end expects receiving an Object with the Fields as a key and the Values as properties. Example:
data:
{
unique_id : 1001,
client : "client1",
material : "concrete",
date: "2021-04-13T00:00:00.000Z"
},
{
unique_id : 1002,
client : "client2",
material : "wood",
date: "2021-04-13T00:00:00.000Z"
},
etc..
Is there any way to get the Field as well as the Value when using Index values, or will it always return an Array (and not an object)?
Could I use a Lambda or something for this?
I do have another Query that uses Map and Lambda to good effect, and returns the entire document, including the Ref and Data fields:
Map(
Paginate(
Match(Index("orders_by_date"), date),
),
Lambda('item', Get(Var('item')))
)
This works very nicely but unfortunately, it also performs one Get request per Document returned and that seems very inefficient.
This new Index I'm wanting to build, to filter by Order Status, will be used to return hundreds of Orders, hundreds of times a day. So I'm trying to keep it as efficient as possible, but if it can only return an Array it won't be useful.
Thanks in advance!! Indexes are great but hard to grasp, so any insight will be appreciated.
You didn't show us exactly what you have done, so here's an example that shows that filtering on boolean values does work using the index you created as-is:
> CreateCollection({ name: "orders" })
{
ref: Collection("orders"),
ts: 1618350087320000,
history_days: 30,
name: 'orders'
}
> Create(Collection("orders"), { data: {
unique_id: 1,
client: "me",
material: "stone",
date: Now(),
status: true
}})
{
ref: Ref(Collection("orders"), "295794155241603584"),
ts: 1618350138800000,
data: {
unique_id: 1,
client: 'me',
material: 'stone',
date: Time("2021-04-13T21:42:18.784Z"),
status: true
}
}
> Create(Collection("orders"), { data: {
unique_id: 2,
client: "you",
material: "muslin",
date: Now(),
status: false
}})
{
ref: Ref(Collection("orders"), "295794180038328832"),
ts: 1618350162440000,
data: {
unique_id: 2,
client: 'you',
material: 'muslin',
date: Time("2021-04-13T21:42:42.437Z"),
status: false
}
}
> CreateIndex({
name: "orders_all_by_open_asc",
unique: false,
serialized: true,
source: Collection("orders"),
terms: [{ field: ["data", "status"] }],
values: [
{ field: ["data", "unique_id"] },
{ field: ["data", "client"] },
{ field: ["data", "material"] },
{ field: ["data", "date"] }
]
})
{
ref: Index("orders_all_by_open_asc"),
ts: 1618350185940000,
active: true,
serialized: true,
name: 'orders_all_by_open_asc',
unique: false,
source: Collection("orders"),
terms: [ { field: [ 'data', 'status' ] } ],
values: [
{ field: [ 'data', 'unique_id' ] },
{ field: [ 'data', 'client' ] },
{ field: [ 'data', 'material' ] },
{ field: [ 'data', 'date' ] }
],
partitions: 1
}
> Paginate(Match(Index("orders_all_by_open_asc"), true))
{ data: [ [ 1, 'me', 'stone', Time("2021-04-13T21:42:18.784Z") ] ] }
> Paginate(Match(Index("orders_all_by_open_asc"), false))
{ data: [ [ 2, 'you', 'muslin', Time("2021-04-13T21:42:42.437Z") ] ] }
It's a little more work, but you can compose whatever return format that you like:
> Map(
Paginate(Match(Index("orders_all_by_open_asc"), false)),
Lambda(
["unique_id", "client", "material", "date"],
{
unique_id: Var("unique_id"),
client: Var("client"),
material: Var("material"),
date: Var("date"),
}
)
)
{
data: [
{
unique_id: 2,
client: 'you',
material: 'muslin',
date: Time("2021-04-13T21:42:42.437Z")
}
]
}
It's still an array of results, but each result is now an object with the appropriate field names.
Not too familiar with FQL, but I am somewhat familiar with SQL languages. Essentially, database languages usually treat all of your values as strings until they don't need to anymore. Instead, your query should use the string definition that FQL is expecting. I believe it should be OPEN or CLOSED in your case. You can simply have an if statement in java to determine whether to search for "OPEN" or "CLOSED".
To answer your second question, I don't know for FQL, but if that is what is returned, then your approach with a lamda seems to be fine. Not much else you can do about it from your end other than hope that you get a different way to get entries in API form somewhere in the future. At the end of the day, an O(n) operation in this context is not too bad, and only having to return a hundred or so orders shouldn't be the most painful thing in the world.
If you are truly worried about this, you can break up the request into portions, so you return only the first 100, then when frontend wants the next set, you send the next 100. You can cache the results too to make it very fast from the front-end perspective.
Another suggestion, maybe I am wrong and failed at searching the docs, but I will post anyway just in case it's helpful.
My index was failing to return objects, example data here is the client field:
"data": {
"status": "LIVRAISON",
"open": true,
"unique_id": 1001,
"client": {
"name": "TEST1",
"contact_name": "Bob",
"email": "bob#client.com",
"phone": "555-555-5555"
Here, the client field returned as null even though it was specified in the Index.
From reading the docs, here: https://docs.fauna.com/fauna/current/api/fql/indexes?lang=javascript#value
In the Value Objects section, I was able to understand that for Objects, the Index Field must be defined as an Array, one for each Object key. Example for my data:
{ field: ['data', 'client', 'name'] },
{ field: ['data', 'client', 'contact_name'] },
{ field: ['data', 'client', 'email'] },
{ field: ['data', 'client', 'phone'] },
This was slightly confusing, because my beginner brain expected that defining the 'client' field would simply return the entire object, like so:
{ field: ['data', 'client'] },
The only part about this in the docs was this sentence: The field ["data", "address", "street"] refers to the street field contained in an address object within the document’s data object.
This is enough information, but maybe it would deserve its own section, with a longer example? Of course the simple sentence works, but with a sub-section called 'Adding Objects to Fields' or something, this would make it extra-clear.
Hoping my moments of confusion will help out. Loving FaunaDB so far, keep up the great work :)

Using rally app lookback API - unable to fetch defects that are tagged

I am using rally lookback API and creating a defect trend chart. I need to filter defects that do not have a tag "xyz".
Using the following:
this.myTrendChart = Ext.create('Rally.ui.chart.Chart', {
storeType: 'Rally.data.lookback.SnapshotStore',
storeConfig: {
find: {
_TypeHierarchy: "Defect",
State: { $lt: "Closed"},
Tags.Name: { $nin: ["xyz"] },
_ProjectHierarchy: projectOid,
_ValidFrom: {$gte: startDateUTC}
}
},
calculatorType: 'Calci',
calculatorConfig: {},
chartConfig: {
chart: {
zoomType: 'x',
type: 'line'
},
title: {
text: 'Defect trend'
},
xAxis: {
type: 'datetime',
minTickInterval: 7
},
yAxis: {
title: {
text: 'Number of Defects'
}
}
}
});
This does not return any data. Need help with the filter for tags.
Tags is a collection of tag-oids so you'll need to find and use the oid of the tag vs the name, at which point it'll just be Tags: { $nin: [oid] }. Caveat: technically, due to how expensive it is, $nin is unsupported (https://rally1.rallydev.com/analytics/doc/#/manual/48e0589f681160fc316a8a4802dc389f)...but it doesn't throw an error so maybe it works anyway.

Set Keystone List "noedit" based on field value?

I would like to make my Keystone List object editable only if the object is not published. Here's my reduced List definition:
var Campaign = new keystone.List('Campaign', {
nodelete: true,
track: {
createdAt: true,
},
});
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publishedOn: '',
},
},
publishedOn: {
type: Types.Datetime,
label: 'Published On',
hidden: true,
},
});
Is it possible to set noedit only if publishedOn is not null? I'm trying to prevent the object from being modified after it's "published", and am coming up short on examples.
The noedit property for a list or field is part of the model definition, and not part of the definition of an individual item. There is no way to mark a single item as uneditable in the admin UI unless you want to apply it to the field or model as a whole.
If you are less worried about clarity, and more worried about ensuring it cannot be edited, you could try the following:
Campaign.schema.post('init', function () {
if (this.published) this.invalidate('Published items cannot be edited');
});
This will cause an error to be thrown if you attempt to save the item once it is published.
While you could use dependsOn: { published: { $exists: true } } to filter fields, this would hide the information.
Interestingly, I was able to make the publish field simply check itself:
Campaign.add({
...,
publish: {
type: Types.Boolean,
required: false,
initial: false,
dependsOn: {
publish: false,
},
},
...,
});
and now it only shows if publish is false, which works exactly as I hoped it would.

Generic Grouping of objects properties

I'm trying to group this object by name, so in fine I would be able to distinguish all names 'duval' from 'lulu':
const groupName = R.groupBy(R.prop('name'), [data]);
But this won't work on:
let data = {
benj: {
content : undefined,
name : 'duval',
complete: false,
height : 181
},
joy: {
content : undefined,
name : 'duval',
complete: true
},
kaori: {
content : undefined,
name : 'lulu',
complete: true
},
satomi: {
content : undefined,
name : 'lulu',
complete: true
}
}
Should I change the format of my object, or is there a way to do it in this kind of object ?
Passing [data] to groupBy is not going to help much. You want to group a list containing a single item. The fact that that item has properties like 'bennj' and 'joy' rather than 'name' is only part of the issue.
You can get something that will work, I think, if this output would be useful:
{
duval: {
benj: {
content: undefined,
name: "duval",
complete: false,
height: 181
},
joy: {
content: undefined,
name: "duval",
complete: true
}
},
lulu: {
kaori: {
content: undefined,
name: "lulu",
complete: true
},
satomi: {
content: undefined,
name: "lulu",
complete: true
}
}
}
But it's a bit more involved:
compose(map(fromPairs), groupBy(path([1, 'name'])), toPairs)(data)
toPairs converts the data to a list of elements something like this:
[
"benj",
{
complete: false,
content: undefined,
height: 181,
name: "duval"
}
]
then groupBy(path[1, 'name']) does the grouping you're looking to do, by finding the name on the object at index 1 in this list.
And finally map(fromPairs) will turn these lists of lists back into objects.
It's not a particularly elegant solution, but it's fairly typical when you want to do list processing on something that's not really a list.