Avoiding Hasura sequential scan for query on large array relation - postgresql

How do I get Hasura to generate SQL that uses the index on review.user_id?
Here is the seemingly obvious way of getting a user's reviews. I've included a simplified version of the SQL that Hasura generates, which is a sub query approach rather than a JOIN of the review table on user:
# SELECT * FROM review
# WHERE user_id = (
# SELECT id FROM "user" WHERE username = 'admin'
# )
# LIMIT 50
# This uses a sequential scan on `review` because Postgres
# can't know exactly what the subquery returns.
query ReviewsForUserSlow {
user(where: { username: { _eq: "admin" } }) {
reviews(limit: 50) {
text
}
}
}
Here is a hack to get Hasura to generate SQL that does indeed use the review.user_id index. However, the caveat is that we need to make a round trip to Hasura to get the user ID in order to build this query:
# Simplification of the SQL Hasura generates:
# SELECT * FROM review
# WHERE user_id = '8f3547e4-c8a9-480f-991f-0798c02f2ba2'
# LIMIT 50
query ReviewsForUserFast {
review(
limit: 50,
where: {
user_id: {
_eq: "8f3547e4-c8a9-480f-991f-0798c02f2ba2"
}
}
) {
text
}
}

Related

sequelize, query property on array of objects

I have looked extensively for an answer but could not find a simple solution.
I have a table that contains a column subscriptionHistory
The data can look like so:
[
{
"fromDate": "2023-01-24T10:11:57.150Z",
"userSubscribedToo": "EuuQ13"
},
{
"fromDate": "2022-01-24T10:11:57.150Z",
"tillDate": "2022-02-24T22:59:59.999Z",
"userSubscribedToo": "a4ufoAB"
}
]
I'm trying to find the records of the subscriptions.
In Mongo we do
'subscriptionHistory.$.userSubscribedToo' = 'a4ufoAB'
Nice and easy.
I'm using PostgreSQL and Sequelize,
The following doesn't work.
const totalEarnings = await SubscriptionToken.count({
where: {
'subscriptionHistory.$.userSubscribedToo': user.id,
},
});
Neither do any direct queries
SELECT *
FROM vegiano_dev."subscription-tokens"
WHERE "subscriptionHistory"->>'userSubscribedToo' = 'a4ufoAB'
--WHERE "subscriptionHistory" #> '{"userSubscribedToo": "a4ufoAB"}'
Not sure where to go now :-/
You can use a JSON path condition with the ## (exists) operator:
select *
from vegiano_dev."subscription-tokens"
where "subscriptionHistory" ## '$[*].userSubscribedToo == "a4ufoAB"'
The #> will work as welll, but because subscriptionHistory is an array, you need to use an array with that operator:
where "subscriptionHistory" #> '[{"userSubscribedToo": "a4ufoAB"}]'
This assumes that subscriptionHistory is defined as jsonb which it should be. If it's not, you need to cast it: "subscriptionHistory"::jsonb

Return aggregated data from federated query

I am working with SPARQL federated queries. I do not wish to return anything other than aggregated data from each service query. Is this possible? Currently, the query returns data from each service query, and then runs a count and group by. I tried to use subqueries, but they do not work. I am using a fuseki server.
<Edited - added sample query>
SELECT (COUNT(?var) as ?varCount) ?var2
WHERE{
{
?var a abc:Class;
}
UNION
{
SERVICE <https://sample1.org>
{
?var a abc:Class;
?var abc:var2 ?var2.
}
}
UNION
{
SERVICE <https://sample2.org>
{
?var a abc:Class;
?var abc:var2 ?var2.
}
}
} group by ?var2

DynamoDB Error: "Query key condition not supported"

I am querying data from a database in aws dynamodb and experiencing an error message on the KeyConditionExpression.
I am querying for "dominant_temporality" and "dt". These make up my composite partition key - dt is unique for each row and my sort key.
The code I'm running:
var params = {
TableName : "deardiary",
KeyConditionExpression: "#d = :dominant_temporality and dt between :minDate and :maxDate",
ExpressionAttributeNames: {
"#d" : "temporality"
},
ExpressionAttributeValues: { // the query values
":dominant_temporality": {S: "present"},
":minDate": {N: new Date("October 8, 2018").valueOf().toString()},
":maxDate": {N: new Date("October 9, 2018").valueOf().toString()}
}
};
Check if you are using BETWEEN on HASH which is not allowed - you can use only EQ for HASH or begins_with for range key.

Sequelize: where query string is in array of strings postgresql

I am trying to perform a query in sequelize where I want to get only users that have the correct role. Roles are stored as an array of strings. For example ['student'] or ['school_owner', 'admin'].
In this particular case, I'm actually trying to get a school and include the school owners for that school. The failing relevant query is
const ownerQuery: { [key: string]: any } = {};
ownerQuery.roles = {$in: ["{school_owner}"]};
School
.findById(req.params.id,{include: [{ model: User, where: ownerQuery }]})
.then((school: School | null) => {
if (school === null) {
res.status(404).json({message: "Not Found"})
} else {
res.json(school)
}
}, (error) => next(error))
sequelize is storing the array values as something like {school_owner, admin}. The documentation says that I can use the following instead for my $in query
ownerQuery.roles = {$in: ["school_owner"]};
Which removes the {} but it gives me a Array value must start with "{" or dimension information.' error.
In the first example, the query doesn't fail, but it doesn't work like an $in query either. I have to match the contents of roles exactly. For example, if a user has both admin and school_owner roles I have to say
ownerQuery.roles = {$in: ["{school_owner, admin}"]};
What's the correct way to perform an $in query so that I can match all users that have a specific roles?
The correct way to implement this functionality is to do the following
ownerQuery.roles = { $contains: ["school_owner"] };
This will return all users that have a role school_owner in their array of roles

Compare two fields in Waterline/Sails.js query

I want to compare two fields in my Waterline query in Sails.js application, e.g.: SELECT * FROM entity E WHERE E.current < E.max.
I've tried the following code, but it's expecting integer value to be passed to it instead of column name:
Entity.find({
where: {
current: {'<': 'max'}
}
});
So, how do I compare two columns?
I have ran some tests and at the same time read the Waterline documentation. There is no indication of anything that could possibly do comparison of two fields/columns via .find() or .where() methods. Reference: http://sailsjs.org/documentation/concepts/models-and-orm/query-language
Instead, I have used .query() method to compare two fields via SQL string such as :
Entity.query("SELECT * FROM `Entity` E WHERE E.current < E.max", function( err, res) {
if(err) {
//exception
} else {
console.log('response',res);
}
});
The other way would be to use one query to get the max before putting it in the criteria.
EntityOne.find({
sort: 'column DESC'
}).limit(1)
.exec(function(err,found){
var max = found[0]
EntityTwo.find({
where: {
current: {'<': found}
}
}).exec((err,found) {
// Do stuff here
});
});
The query method is ultimately going to be faster however