How apply where only a column is not null in adonis query builder - node.js

I need to check if the column percentage_correct >= parCorrect if percentage_correct <> NULL
I try something like:
const unidadeSequenceAtualEstudante = await Database.select("*")
.from("student_quiz_historics")
.where("student_id", idEstudante)
.where(function(){
this
.whereNotNull("percentage_correct")
.where("percentage_correct", ">=", parQtdAcerto)
})
.where("class_id", idClasse)
.where("book_id", idLivro)
.where("execution_back_status", "<>", "Cancelado")
.orderBy("sequence", "desc")
.first();
But when i have only records with the percentage_correct null, they still are trying to apply this where.

I'm having a little trouble replicating your problem. What you have there should work, but it's possible you're not considering something about SQL's behaviour with regard to NULL: anything compared to NULL is NULL, which is why the IS NOT NULL syntax exists. So for example:
foo=# SELECT 1 >= NULL;
?column?
----------
NULL
(1 row)
foo=# SELECT NULL >= 1;
?column?
----------
NULL
(1 row)
This means that you can kind of get away with not checking for the null at all, because only rows that meet the condition will be returned:
const unidadeSequenceAtualEstudante = await Database.select("*")
.from("student_quiz_historics")
.where({
book_id: idLivro,
class_id: idClasse,
student_id: idEstudante
})
.where('percentage_correct', '>=', parQtdAcerto)
.where("execution_back_status", "<>", "Cancelado")
.orderBy("sequence", "desc")
.first();
Is this a good idea? Debatable. I think it's probably fine for this purpose, but we shouldn't assume that NULL is the same thing as FALSE, because it isn't.
If you're still having trouble with your query, you'd need to provide more details about which database you're using, what your schema is and what kind of errors your getting.

Related

Firestore in Datastore mode, query using OR [duplicate]

From the docs:
You can also chain multiple where() methods to create more specific queries (logical AND).
How can I perform an OR query?
Example:
Give me all documents where the field status is open OR upcoming
Give me all documents where the field status == open OR createdAt <= <somedatetime>
OR isn't supported as it's hard for the server to scale it (requires keeping state to dedup). The work around is to issue 2 queries, one for each condition, and dedup on the client.
Edit (Nov 2019):
Cloud Firestore now supports IN queries which are a limited type of OR query.
For the example above you could do:
// Get all documents in 'foo' where status is open or upcmoming
db.collection('foo').where('status','in',['open','upcoming']).get()
However it's still not possible to do a general OR condition involving multiple fields.
With the recent addition of IN queries, Firestore supports "up to 10 equality clauses on the same field with a logical OR"
A possible solution to (1) would be:
documents.where('status', 'in', ['open', 'upcoming']);
See Firebase Guides: Query Operators | in and array-contains-any
suggest to give value for status as well.
ex.
{ name: "a", statusValue = 10, status = 'open' }
{ name: "b", statusValue = 20, status = 'upcoming'}
{ name: "c", statusValue = 30, status = 'close'}
you can query by ref.where('statusValue', '<=', 20) then both 'a' and 'b' will found.
this can save your query cost and performance.
btw, it is not fix all case.
I would have no "status" field, but status related fields, updating them to true or false based on request, like
{ name: "a", status_open: true, status_upcoming: false, status_closed: false}
However, check Firebase Cloud Functions. You could have a function listening status changes, updating status related properties like
{ name: "a", status: "open", status_open: true, status_upcoming: false, status_closed: false}
one or the other, your query could be just
...where('status_open','==',true)...
Hope it helps.
This doesn't solve all cases, but for "enum" fields, you can emulate an "OR" query by making a separate boolean field for each enum-value, then adding a where("enum_<value>", "==", false) for every value that isn't part of the "OR" clause you want.
For example, consider your first desired query:
Give me all documents where the field status is open OR upcoming
You can accomplish this by splitting the status: string field into multiple boolean fields, one for each enum-value:
status_open: bool
status_upcoming: bool
status_suspended: bool
status_closed: bool
To perform your "where status is open or upcoming" query, you then do this:
where("status_suspended", "==", false).where("status_closed", "==", false)
How does this work? Well, because it's an enum, you know one of the values must have true assigned. So if you can determine that all of the other values don't match for a given entry, then by deduction it must match one of the values you originally were looking for.
See also
in/not-in/array-contains-in: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any
!=: https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html
I don't like everyone saying it's not possible.
it is if you create another "hacky" field in the model to build a composite...
for instance, create an array for each document that has all logical or elements
then query for .where("field", arrayContains: [...]
you can bind two Observables using the rxjs merge operator.
Here you have an example.
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
...
getCombinatedStatus(): Observable<any> {
return Observable.merge(this.db.collection('foo', ref => ref.where('status','==','open')).valueChanges(),
this.db.collection('foo', ref => ref.where('status','==','upcoming')).valueChanges());
}
Then you can subscribe to the new Observable updates using the above method:
getCombinatedStatus.subscribe(results => console.log(results);
I hope this can help you, greetings from Chile!!
We have the same problem just now, luckily the only possible values for ours are A,B,C,D (4) so we have to query for things like A||B, A||C, A||B||C, D, etc
As of like a few months ago firebase supports a new query array-contains so what we do is make an array and we pre-process the OR values to the array
if (a) {
array addObject:#"a"
}
if (b) {
array addObject:#"b"
}
if (a||b) {
array addObject:#"a||b"
}
etc
And we do this for all 4! values or however many combos there are.
THEN we can simply check the query [document arrayContains:#"a||c"] or whatever type of condition we need.
So if something only qualified for conditional A of our 4 conditionals (A,B,C,D) then its array would contain the following literal strings: #["A", "A||B", "A||C", "A||D", "A||B||C", "A||B||D", "A||C||D", "A||B||C||D"]
Then for any of those OR combinations we can just search array-contains on whatever we may want (e.g. "A||C")
Note: This is only a reasonable approach if you have a few number of possible values to compare OR with.
More info on Array-contains here, since it's newish to firebase docs
If you have a limited number of fields, definitely create new fields with true and false like in the example above. However, if you don't know what the fields are until runtime, you have to just combine queries.
Here is a tags OR example...
// the ids of students in class
const students = [studentID1, studentID2,...];
// get all docs where student.studentID1 = true
const results = this.afs.collection('classes',
ref => ref.where(`students.${students[0]}`, '==', true)
).valueChanges({ idField: 'id' }).pipe(
switchMap((r: any) => {
// get all docs where student.studentID2...studentIDX = true
const docs = students.slice(1).map(
(student: any) => this.afs.collection('classes',
ref => ref.where(`students.${student}`, '==', true)
).valueChanges({ idField: 'id' })
);
return combineLatest(docs).pipe(
// combine results by reducing array
map((a: any[]) => {
const g: [] = a.reduce(
(acc: any[], cur: any) => acc.concat(cur)
).concat(r);
// filter out duplicates by 'id' field
return g.filter(
(b: any, n: number, a: any[]) => a.findIndex(
(v: any) => v.id === b.id) === n
);
}),
);
})
);
Unfortunately there is no other way to combine more than 10 items (use array-contains-any if < 10 items).
There is also no other way to avoid duplicate reads, as you don't know the ID fields that will be matched by the search. Luckily, Firebase has good caching.
For those of you that like promises...
const p = await results.pipe(take(1)).toPromise();
For more info on this, see this article I wrote.
J
OR isn't supported
But if you need that you can do It in your code
Ex : if i want query products where (Size Equal Xl OR XXL : AND Gender is Male)
productsCollectionRef
//1* first get query where can firestore handle it
.whereEqualTo("gender", "Male")
.addSnapshotListener((queryDocumentSnapshots, e) -> {
if (queryDocumentSnapshots == null)
return;
List<Product> productList = new ArrayList<>();
for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
Product product = snapshot.toObject(Product.class);
//2* then check your query OR Condition because firestore just support AND Condition
if (product.getSize().equals("XL") || product.getSize().equals("XXL"))
productList.add(product);
}
liveData.setValue(productList);
});
For Flutter dart language use this:
db.collection("projects").where("status", whereIn: ["public", "unlisted", "secret"]);
actually I found #Dan McGrath answer working here is a rewriting of his answer:
private void query() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("STATUS")
.whereIn("status", Arrays.asList("open", "upcoming")) // you can add up to 10 different values like : Arrays.asList("open", "upcoming", "Pending", "In Progress", ...)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
// I assume you have a model class called MyStatus
MyStatus status= documentSnapshot.toObject(MyStatus.class);
if (status!= null) {
//do somthing...!
}
}
}
});
}

Increment a column with the default value as null

I need to increment a column with 1 on some occasions, but the default value of that column is null and not zero. How do I handle this case using sequelize? What method could be utilized?
I could do by checking the column for null in one query and updating it accordingly in the second query using sequelize but I am looking for something better. Could I handle this one call?
I'll confess that I'm not terribly experienced with sequelize, but in general you'll want to utilize IFNULL. Here's what the raw query might look like:
UPDATE SomeTable
SET some_column = IFNULL(some_column, 0) + 1
WHERE <some predicate>
Going back to sequelize, I imagine you're trying to use .increment(), but judging from the related source, it doesn't look like it accepts anything that will do the trick for you.
Browsing the docs, it looks like you might be able to get away with something like this:
SomeModel.update({
some_column: sequelize.literal('IFNULL(some_column, 0) + 1')
}, {
where: {...}
});
If that doesn't work, you're probably stuck with a raw query.
First you need to find the model instance and update via itself, or update directly via Sequelize Static Model API.
Then you'll check whether the updated field got nullable value or not ? If fails then do the manual update as JMar propose above
await model.transaction({isolationLevel: ISOLATION_LEVELS.SERIALIZABLE}, async (tx) => {
const user = await model.User.findOne({
where: {
username: 'username',
},
rejectOnEmpty: true,
transaction: tx,
});
const updatedRecord = await user.increment(['field_tag'], {
transaction: tx,
});
if (!updatedRecord.field_tag) {
/** Manual update & Convert nullable value into Integer !*/
await model.User.update({
field_tag: Sequelize.literal('IFNULL(field_tag, 0) + 1')
}, {
where: {
username: 'username',
},
transaction: tx,
});
}
});

Distinct count with sequelize

I'm trying to get a distinct count with sequelize such as
'SELECT COUNT(DISTINCT(age)) AS `count` FROM `Persons` AS `Person`'
As long as I use a raw query, I get the desired result. However, as soon as I change to the sequelize count function, the query is broke in Postgres:
Person.count({distinct:'age'}).then(...);
results to
'SELECT COUNT(DISTINCT(*)) AS `count` FROM `Persons` AS `Person`'
which leads to a syntax error. Solutions described in different posts such as How to get a distinct count with sequelize? do not work, unless you add an include statement or a where clause which I do not have in this special case.
Does anybody know a proper solution for this?
You have to use Sequelize aggregation to make it worked correctly.
Model.aggregate(field, aggregateFunction, [options])
Returns: Returns the aggregate result cast to options.dataType, unless
options.plain is false, in which case the complete data result is
returned.
Example:
Person.aggregate('age', 'count', { distinct: true })
.then(function(count) {
//.. distinct count is here
});
Executing (default):
SELECT count(DISTINCT("age")) AS "count" FROM "persons" AS "person";
You can do it something like this :
models.User.findAll({
attributes: ['user_status',[sequelize.fn('COUNT', sequelize.col('user_status')), 'total']] ,
group : ['user_status']
});
That will return something like :
[
{ user_status : 1 , total : 2 },
{ user_status : 2 , total : 6 },
{ user_status : 3 , total : 9 },
...
]
Then you can loop through returned data and check of status
In latest version, you should be doing
Person.count({distinct:true, col: 'age'}).then(...);
See: http://docs.sequelizejs.com/class/lib/model.js~Model.html#static-method-count

What is returned from Mongoose query that finds no matches?

I'm a little confused reading the Mongoose documentation.
If I run a query in mongoose which matches no documents in the collection, what are the values of err and results in the callback function callback(err, results)? I just don't know what Mongoose considers an "error". As a mathematician, returning the empty set (i.e. results array empty) seems perfectly valid and shouldn't be an "error" - the query executed fine, there was just no matching documents. On the other hand, some may consider it an "error". From mongoose docs, either:
err = null, results = []
err = null, results = null
err = error document, results = null
It depends on the query. If it is a find, then results == []. If it is a findOne, then results == null. No errors if everything else is ok.
If conditions were valid but no matches were found:
find: err is null, result is []
findOne and findById: err is null, result is null
However, if some condition was invalid (e.g. field is string but you pass an object, or you pass an invalid _id)
For all three: err is {..}, result is undefined
If using .find(),
The handy way would be
models.<your collection name>.find({ _id: `your input` }).then(data => {
if (data.length == 0) return // throw your error to the console;
});

Sequelize select distinct rows

Is there a way to select distinct rows from a table using sequelize.js?
I looked through the documentation but the "finder methods" do not specify a way to accomplish this task.
Assuming you want to apply DISTINCT to the following query:
Tuple.findAll({attributes: ['key', 'value']});
then this is a (hackish) way to achieve what you want without having to write the whole query yourself:
Tuple.findAll({attributes: [[Sequelize.literal('DISTINCT `key`'), 'key'], 'value']});
(Tested with Sequelize v2.1.0)
Edit 2015-06-08: Still works with Sequelize v3.1.1
You can do the following:
myModel.findAll({
attributes: [[sequelize.fn('DISTINCT', sequelize.col('col_name')), 'alias_name']],
where:{}
}).then(data => {}).....
taken from issues and it works.
edit your "node_modules/sequelize/lib/dialects/mysql/query-generator.js"
at around line 118
change
var query = "SELECT <%= attributes %> FROM <%= table %>"
into
var query = "SELECT " + ((options.distinct)? 'DISTINCT ':'') +"<%= attributes %> FROM <%= table %>",
now you can add an option distinct: true in your sequelize request
hope it helps -_^
Model.findAll({Attributes: ['col_name1', 'col_name2'], group: ['col_name1', 'col_name2']});
it's perfectly fine with Sequelize 5.21
It's not possible automatically but if you don't mind creating the sql on your own, you could do this:
sequelize.query('sql goes here', null, { raw: plain }).success(function(data){
console.log(data)
})
Have fun :)
UPDATE
Sequelize now uses then instead of success as promise function.
Sequelize.query has been refactored to only use paramters sql and options
raw accepts true/false but no plain as value.
So, according to the new version, the code should look like this:
sequelize.query('sql goes here', { raw: true }).then(function(data){
console.log(data);
});
This is somewhat similar to the solution proposed by Pascal Ludwig, but for those landing here looking to get a list of distinct values for a given column, you can do the following:
MyModel.aggregate('teh_field', 'DISTINCT', { plain: false }).then(...)
// Resolves to: [ { DISTINCT: value1 }, { DISTINCT: value2 }, ... ]
With that, it's easy to transform it into a standard list:
MyModel.aggregate('teh_field', 'DISTINCT', { plain: false })
.map(function (row) { return row.DISTINCT })
.then(function (tehValueList) {
// tehValueList = [ value1, value2, ... ]
})
;
As of Sequelize version 1.7, the select query has been moved into lib/dialects/abstract/query-generator.js.
Around line 1167, change
mainQueryItems.push("SELECT "+mainAttributes.join ....)
to
mainQueryItems.push('SELECT ');
if (options.distinct) {
mainQueryItems.push('DISTINCT ');
}
mainQueryItems.push(mainAttributes.join(', ') + ' FROM ' + options.table);
By the way, I use Sqlite and Postgres, both of which support "DISTINCT". If you're using a dialect that doesn't support distinct, then obviously this line will cause problems for you, since it will be generated for all the SQL flavors that you're using. I suspect this is why this simple change hasn't made it into the main Sequelize source tree.

Resources