I'm struggling with a somewhat more complex SQL Query which HAS TO BE in Objection.js.
Below is the code so far
const tagEntry = await Tag_overview.query()
.where('playable') //the playable column is a boolean
.whereExists(
InnerTableName.query().findById([<normal variable>, tag.id]) //<= tag.id is the id of the row in the outer query
)
)
.orderBy(raw('random()'))// this randomly orders the selection
.limit(1)
"tag.id" should be the value of the row in the top/outer query thats currently being checked. In SQL I'd solve it with a simple line like (< normal variable> is a Javascript variable passed into the query and can be trated as a hardcoded value, it and tagid are a compound key)
and EXISTS (SELECT tagid, othercolumn FROM kunstmakler_preselection WHERE tag.id = tagid AND <normal variable> = othercolumn)
But I have absolutely no clue how to do this in Objection.js. Yes, it needs an inner Query, but HOW do I pass this tag.id in there? I'm completely lost and neither the API Reference nor the Recipe Book is any help (found here: https://vincit.github.io/objection.js/recipes/ )
Is a join necessary here? Would it be worth it? [The tagoverview Table is rather small while "InnerTableName" is quite large]. I feel like that can't really be the solution since in ülain SQL it'd be such a smooth one liner
First make sure that you have declared composite key correctly in model InnerTableName https://vincit.github.io/objection.js/recipes/composite-keys.html
Then you should be able to do:
.whereExists(
InnerTableName.query().findById([theJsVariable, ref("Tag_overview.id")])
)
Related
Bit of a lengthy one so those of you who like a challenge (or I'm simply not knowledgeable enough - hopefully it's an easy solution!) read on!
(skip to the actual question part to skip the explanation and what I've tried)
Problem
I have a site that has a dataset that contains an object with multiple objects inside. Each of those objects contains an array, and within that array there are multiple objects. (yes this is painful but its from an API and I need to use this dataset without changing or modifying it.) I am trying to filter the dataset based of the key-value pairs in the final object. However, I have multiple filters being executed at once.
Example of Path before looping which retrieves the key-value pair needed for one hall.
["Hamilton Hall"]["Hire Options"][2].Commercial
After Looping Path of required key-value pair for all halls, not just one (the hall identifier is stored):
[0]["Hire Options"][2].Commercial
Looping allows me to check each hall for a specific key-value pair (kind of like map or forEach, but for an object).
After getting that out of the way back to the question.
How would I go about filtering which of the looped objects are displayed?
What I have Tried
(userInput is defined elsewhere - this happens on a btn click btw)
let results = Object.keys(halls);
for (key of results) {
let weekend = [halls[ `${key}` ][ 'Hire Options' ][4][ 'Weekend function' ]];
if(userInput == weekend) {
outputAll([halls[ `${key}` ]]);
}
}
That filters it fine. However, I run into an issue here. I want to filter by multiple queries, and naturally adding an AND into the if statement doesn't work. I also dont want to have 10 if statements (I have 10+ filters of various data types I need to sort by).
I have recently heard of ternary operators, but do not know enough about them to know if that is the correct thing to do? If so, how? Also had a brief loook at switches, but doesnt seem to look like what I want (correct me if I am wrong.)
Actual Question minus the babble/explanation
Is there a way for me to dynamically modify an if statements conditions? Such as adding or removing conditions of an if statement? Such as if the filter for 'a' is set to off, remove the AND condition for 'a' in the if statement? This would mean that the results would only filter with the active filters.
Any help, comments or 'why haven't you tried this' remark are greatly appreciated!
Thanks!
Just for extra reference, here is the code for retrieving each of the objects from the first object as it loops through them:
(Looping Code)
halls = data[ 'Halls' ];
let results = Object.keys(halls);
for (key of results) {
let arr = [halls[ `${key}` ]];
outputAll(arr);
}
You can use Array.filter on the keys array - you can structure the logic for a match how you like - just make it return true if a match is found and the element needs to be displayed.
let results = Object.keys(halls);
results.filter(key => {
if (userInput == halls[key]['Hire Options'][4]['Weekend function']) {
return true;
}
if (some other condition you want to match) {
return true;
}
return false;
}).forEach(key => outputAll([halls[key]]));
I currently have a manually composed query in knex.js that uses knex.raw:
db.raw(`SELECT * FROM
(VALUES ${Array(ids.length).fill('(?)').join(',')}) v (__whereId)
LEFT JOIN LATERAL
(SELECT * FROM ?? WHERE ?? = v.__whereId ORDER BY ?? ASC LIMIT 2) t
ON true`,
[...ids, table, `${table}.${field}`, primaryKey])
I'd like to convert it to use as much knex.js built-in stuff as possible, but every time I try to come up with cleaner options using knex.wrap, knex.as etc. I run in to something that is unconvertible, or I just don't know how to write it.
I know knex.js does not support VALUES in anything except insert, so that has to be something raw, but the other things probably should be possible
I managed to find a solution to my own question:
const values = db.raw(`(VALUES ${Array(ids.length).fill('(?)').join(',')}) AS v (__whereId)`, ids);
const sub = db.table(table).whereRaw('??.?? = v.__whereId', [table, field]).orderBy(primaryKey, 'asc').limit(2).as('t');
const query = db.from(values).joinRaw('left join lateral ? on true', sub).select();
The last part is the interesting part - apparently using ? inside a raw input, and giving a sub query as the parameter for the placeholder triggers some internal knex logic to automatically wrap the query and the nested placeholders correctly.
First I inserted a new record into a database with a field file_size. It all works fine. Then I want to delete the record with a given name and size from the database. It works with name, but when I add AND statement it doesnt work.
$conn->query("DELETE FROM mytable WHERE name LIKE '%{$t}' AND file_size = '$file_size'");
file_size is passed through $file_size= $_POST['size']; and it works correctly. The number in the database and the one passed is the same. I have no idea why the above doesnt work...at first I thought that maybe these are different data types and hence I am comparing string with integer, but in Javascript it shouldnt matter...Any advice would be greately appreciated.
Maybe prepare request
$query=$conn->prepare("DELETE FROM mytable WHERE name LIKE :like_param AND file_size = :filesize");
$query->excute(array(':like_param' => '%'.$t, ':filesize' => $file_size));
db = new Array("myserver", "myfolder\\mydb.nsf")
dir = getComponent("Dir").value;
div = getComponent("Div").value;
lu = #DbLookup(db, "ManagerAccess", dir + "PP" + div, "DTManagers");
var a = [];
a.push(lu);
var item:NotesItem = docBackEnd.replaceItemValue('FormReaders', #Unique(a));
item.setReaders(true);
That code is on the querySaveDocument ssjs. The result I get from the #DbLookup (when I put in a computed field) look like this:
Pedro Martinez,Manny Ramirez,David Ortiz,Terry Francona
I tried doing an #Explode(#Implode) thing on it, but it doesn't seem to work.
The error I get in the browser just tells me that the replaceItemValue line is broken.
To test it, I pushed several strings one at a time, and it worked correctly populating my FormReaders field with the multiple entries.
What am I doing wrong?
I see several problems here:
A. In cases as described by you #Dblookup in fact would return an array. If you push an array into a plain computedField control it will exactly look as that you wrote:
value1, value2, ..., valueN
A computedField doesn't know anything about multiple values etc, it just can display strings, or data that can be converted to strings.
If you want to test the return value you could try to return something like lu[0]; you then should receive the array's 1st element, or a runtime error, if lu is NOT an array. Or you could ask for the array's size using lu.length. That returns the number of array elements, or the number of characters if it's just a plain string.
B. your code contains these two lines:
var a = [];
a.push(lu);
By that you create an empty array, then push lu[] to the first element of a[]. The result is something like this:
a[0] = [value1, value2, ..., valueN],
i.e. a is an array where the first element contains another array. Since you don't want that, just use #Unique(lu) in your replaceItemValue-method.
C. I don't see why replaceItemValue would throw an error here, apart from what I wrote in topic B. Give it a try by writing lu directly to the item (first without #Unique). That should work.
D. for completeness: in the first line you used "new Array". A much better way to define your db parameters is
var db = ["myserver", "myfolder/mydb.nsf"];
(see Tim Tripcony's comment in your recent question, or see his blog entry at http://www.timtripcony.com/blog.nsf/d6plinks/TTRY-9AN5ZK)
OK, I'm missing something here and I just can't seem to find it because the logic seems correct to me, but I'm certain I'm not seeing the error.
var VisibleMarkers = function() {
var filtered = _.reject(Gmaps.map.markers, function(marker) {
return marker.grade != $('.mapDataGrade').val() && !_.contains(marker.subjects,$('.mapDataSubjects').val())
});
return filtered
}
I'm using underscore.js and jQuery to simplify my javascript work.
So right now, I'm checking by means of selects which data gets to be rejected and then I display the filtered markers on the (google) map (if it helps at all, this is using gmaps4rails which is working perfectly fine, its this bit of javascript that's making me lose the last of the hairs on my head).
Currently, the code functions 100% correctly for the ".mapDataGrade" select, but the ".mapDataSubjects" isn't. Now the markers object has a json array of the subjects (this is for students) and each item in the array has its ID. Its this ID that I am supposed to be checking.
Can someone see what I'm doing wrong?
If there's more info that needs to be included, please let me know.
This is on plain javascript on a RoR application using gmaps4rails
Now the markers object has a json array of the subjects (this is for students) and each item in the array has its ID. Its this ID that I am supposed to be checking.
_.contains compares a values, but it sounds like you want your iterator to compare a value to an object's "id" property. For that, _.some would work; it's like contains, except that, instead of comparing values, you can write the comparison as a function:
Returns true if any of the values in the list pass the iterator truth test.
Here's how you'd use it:
!_.some(marker.subjects, function(subject) {
return subject.id == $('.mapDataSubjects').val();
})
If I'm right, the whole line should be like this:
return marker.grade != $('.mapDataGrade').val() &&
// check that none of the subjects is a match
!_.some(marker.subjects, function(subject) {
// the current subject is a match if its ID matches the selected value
return subject.id == $('.mapDataSubjects').val();
});