Hasura complex search of products - graphql

How can i solve this problem with complex searching in products?
This is what i have now as a standard get products and search on them.
query getProducts($limit: Int, $offset: Int, $searchQuery: String) {
products_products(limit: $limit, offset: $offset, where: {_or: [{title: {_ilike: $searchQuery}}, {shortDescription: {_ilike: $searchQuery}}], active: {_eq: true}}) {
__typename
id
shortDescription
title
likes {
userId
}
images(where: {main: {_eq: true}}) {
image
}
categories {
category {
title
}
}
}
}
i have products table, categories table, join table(productId, categoryId), later on there would be reviews table, wishlist table
how to show products only in any category (record in join table)?
how to show products with reviews 3.5+ ?
You know how it is with complex filtration.
Can someone show me how it could be done?
Lastly .. im using flutter with optimistic results, so thats why i chose this structure of query. it makes sense when "replicating" the state of wishlist.

how to show products only in any category (record in join table)?
You can filter on related data as well
query {
product(where: { category: { name: { _eq: "Kitchen" } } })
}
how to show products with reviews 3.5+ ?
I assume you mean an average review of 3.5 stars or greater?
There is a brand new feature in Hasura called Aggregation Predicates that lets you do this
See last month's community call demo for how to use it:
https://youtu.be/4LUiztVe8EA?t=1505
By the way, if you are building an e-commerce type application, there is a sample application which already implements much of this functionality so you can see how it is done:
https://hasura.io/reference-app/
https://github.com/hasura/hasura-ecommerce

Related

GraphQL Shopify query and sorting

We currently have a setup where customers can sort products on price, title, created, manual, etc. Every time there's a query like
`query ($handle: String!, $numProducts: Int!, $sortKey: ProductCollectionSortKeys!, $reverse: Boolean!) {
collectionByHandle(handle: $handle) {
id
handle
description
descriptionHtml
title
image {
id
originalSrc
altText
}
products(first: $numProducts, sortKey: $sortKey, reverse: $reverse) {
pageInfo {
hasNextPage
hasPreviousPage
}
edges {...
`
There are 8 queries in total, one for each sortKey. The idea is to move the sorting logic to js, making a single query instead of 8.
The problem is, I can only do 7 types of sorting in javascript (PRICE, TITLE, CREATED (ascending and descending)), and either MANUAL or BEST_SELLING.
To get both MANUAL and BEST_SELLING, I currently will have 2 queries, which is already an improvent!
Is there a way to combine them into a single query, a bit like mySql, creating a 'virtual' field e.g. manual_sort_position: 5 to add to a product?

How to filter the products based on product_type, that are retrieved using collectionByHandle in shopify storefront graphQL api?

In our app, we are displaying the list of collections from the shopify store. When user click on any of collection, it will redirect to product list, where all the products of that clicked collection will be displayed.
Here is the sample query which I used to get the products list of particular collection :
{
collectionByHandle(handle: "") {
products(first: 10) {
edges {
node {
id
title
}
}
}
}
}
Now, I want filtering on products list. I want to display products base on product_type. But there is no any option to filter the products.
query option only available when you retrieve simple products listing using this query :
{
products(query: "product_type:casual-things", first: 10) {
edges {
node {
id
title
productType
}
}
}
}
But, this query option not available on products node of collectionByHandle.
Can, anyone help me how to implement filter option using Shopify storefront graphQL api in collectionByHandle query ?
The products connection doesn't have a query argument. You have basically access to fetching all products within that collectionByHandle and then filtering them client-side. You must have to use liquid for these types of nested features.

GraphQL - How retrieve id of previous mutation, during query of multiple mutations

i would like run multiple mutations in the same query.
In the example below, i create an order and after i create a product record, concerning previously created.
I must have 2 mutations.
First, i insert an order. In output, i retrieve among others, idorder.
Then, i insert an product. This product
mutation {
createOrder(input: {
order: {
ordername: "My order"
}
}) {
order {
idorder
ordername
}
},
createProduct(input: {
product: {
quantity: 3
idrefproduct: 25 # link to refProduct
idorder: XXXX # how can i retrieve idorder from output of createOrder above ? 🤔
}
}) {
product {
idproduct
}
}
}
Real example with SQL structure :
user(iduser, othersFields);
scenarios(idscenario, iduser, name, otherFields);
cultA(idcultA, idscenario, ...); // this table need of idscenario field
cultB(idcultB, idscenario, ...); // this table need of idscenario field
cultC(idcultC, idscenario, ...); // this table need of idscenario field
how can i retrieve idorder from output of createOrder above ? 🤔
It is possible ?
If i forgot some informations, don't hesitate.
Thanks in advance.
EDIT :
With PostGraphile, plugin "postgraphile-plugin-nested-mutations" or "custom mutations" (with PL PGSQL function)
Without PostGraphile, a resolver as the example of #xadm permits this particular nested mutation.
IMHO you can search for "nested mutations" - not described here, you'll easily find examples/tutorials.
Proposed DB structure (n-to-n relation):
order{orderID,lines[{orderLineID}] } >
order_line{orderLineID, productID, anount, price} >
product {productID}
... created in nested mutations (in reverse order product>order_line>order)
Product don't need orderID, but when you ask for it [in product resolver]
query product(id) {
id
orderedRecently {
orderID
date
price
}
}
... you can simply get it (or rather many - array) from orderLines and orders tables [using simple SQL query - where price will be read from orderLines]
orderedRecently resolver can get product id from parent object (usually 1st param)
Of course you can (and should) return data as order and orderLine types (to be cached separately, normalized):
query product($id: ID!) {
product(id: $id) {
id
orderedRecently {
id
date
orderLine {
id
amount
price
}
}
}
}
where type orderedRecently: [Order!] - array can be empty, not eordered yet
update
I slightly misunderstood your requirements (naming convention) ... you already have proper db structure. Mutation can be 'feeded' with complex data/input:
mutation {
createOrder(input: {
order: {
ordername: "My order"
products: [
{
quantity: 3
idrefproduct: 25
},
{
quantity: 5
idrefproduct: 28
}
]
}
}) {
order {
id
ordername
products {
id
idrefproduct
quantity
}
}
}
}
Your product is my orderLine, idrefproduct is product.
createOrder creates/inserts order and then use its id for creation of product records (order.id, idrefproduct and quantity). Resolver can return only order id or structured data (as above).

GraphQL with multiple objects: should I keep all objects in a single query?

I´m building a SaaS B2B application composed of several different objects. Examples:
Users
Customers
StockItens
StockLevels
PriceList
Sales
Returns
Etc...
I´ll have around 40 different objects, that can be listed and created, edited, and deleted individually.
Facing the GraphQL concepts for the first time, should I build a large schema for all objects, like the example below, or should I keep each object on its own query.
query {
viewer {
Users {
id
firstName
lastName
address
city
...
}
Customers {
id
firstName
lastName
address
city
rating
...
}
StockItens {
id
item_id
sales {
id
dateTime
qty
unitValue
totalValue
...
}
...
}
StockLevels {
...
}
PriceList {
...
}
Sales {
id
dateTime
qty
unitValue
totalValue
...
}
Returns {
...
}
}
}
Looking for the first option (keeping everything into one single query) seens logical as I will be using fragments to access the desired piece of information, but then I will have a huge schema with lots of inter relations.
PLease advice what would be the best practice on that use case.
I suggest you do not write a query where you add all needed data but use the concept of fragments as you already pointed out.
And you fetch only the data which are needed for the current page. So the throughput is kept minimal.
e.g.
If you have a page where you update a user you just fetch the needed data for this user in a specialized query. The query consists of fragments.
The fragments are related to the subcomponents which are used in the page, for example a form where you show the data of the user.
The fragment of the form defines the data it needs from the user and the update page combines the fragments to the query.
// in user form component
const userFormFragments = {
name: "UserForm",
document: `fragment UserForm on User {
id
name
}`
};
// in update user page
const userQuery = `query getUserQuery($userId: ID!) {
getUser(userId: $userId) {
...${userFormFragment.name}
}
${userFormFragment.document}
}`

GraphQL: Filter data in an array

I'm sure it's a simple thing to do, but I couldn't find anything in either GraphQL's doc or Graphcool's.
Say I have an entity with this schema (new GraphQL user, sorry if I make mistake in the schema representation):
Book {
name: String!
author: String!
categories: [String!]
}
How would I do a query for all books that are part of the "mystery" category? I know I can filter with allBooks(filter: {}), but categories_in: ["mystery"] and categories_contains: "mystery" didn't do the trick.
Category model
Thinking a bit more about this situation, creating a Category model is definitely the way to go.
For example, imagine you want to allow readers to subscribe to their favorite categories later. Or, what if you want a list of all existing categories? Using string lists, you would need to query all books and somehow postprocess all obtained categories. Handling this on a model level rather than using string lists feels much more natural.
Instead, you can create a new Category model and add a many-to-many relation between Category and Book. In situations like this, I like to add a unique enum field tag and a string field text. (A unique string field tag alone would also be suitable, probably a matter of taste.)
With this setup, you can easily fulfill data requirements like
Which books are assigned to a given category?
query {
# query books by unique category tag
Category(tag: MYSTERY) {
books {
id
}
}
# query books by specific category text
Category(filter: {
text: "mystery"
}) {
books {
id
}
}
}
Which books are assigned to at least one category of a given list?
query {
allCategories(filter: {
OR: [{
tag: MYSTERY
}, {
tag: MAGIC
}]
}) {
books {
id
}
}
}
Which books are assigned to all categories of a given list?
query {
allCategories(filter: {
AND: [{
tag: MYSTERY
}, {
tag: MAGIC
}]
}) {
books {
id
}
}
}
Related filters
Even though the above queries fulfill the specified data requirements, books are grouped by Category in the response, meaning that we would have to flatten the groups on the client.
With so called related filters, we can turn that around to only obtain books based on conditions defined its related categories.
For example, to query books assigned to at least one category of a given list:
query {
allBooks(filter: {
OR: [{
categories_some: {
tag: MYSTERY
},
categories_some: {
tag: MAGIC
}
}]
}) {
id
}
}
If you are interested in using a hosted GraphQL service, scaphold.io has had this feature for a while now. All connection fields in your API come with a WhereArgs argument that exposes filters that let you really dig into your data. When you have a list of scalars like this, the WhereArgs include a contains & notContains field that allow you to filter results based off the values in your list. This allows you to make a query like this.
query MysteriousBooks($where:BookWhereArgs) {
viewer {
allBooks(where:$where) {
edges { node { title, ... } }
}
}
}
# Variables
{
"where": {
"categories": {
"contains": "mystery"
}
}
}
Just to be complete, you could also do a slight schema readjustment to make this work without having to filter on a scalar list. For example, you could make Category a node implementing type and then create a connection between Category and Book. Although a Book will likely not have many categories, this would allow you to issue a query like this:
query MysteriousBooks($where: CategoryWhereArgs) {
viewer {
allCategories(where: $where) {
books {
edges { node { title, ... } }
}
}
}
}
# Variables
{
"where": {
"name": {
"eq": "mystery"
}
}
}
If you structure your schema this way then you would also be able to do more filtering on the books in the category without having to loop through every book in your archive. E.G. you could efficiently ask for "all the mystery books written in the last year."
Full disclosure: I work at Scaphold and although I'd love you to try it out no hard feelings if you don't switch over. I'm excited to see people trying and loving GraphQL. If you're curious about how to implement this type of behavior on your own server let me know and I'd be happy to help there as well!
I hope this helps!

Resources