IndexedDB - boolean index - google-chrome

Is it possible to create an index on a Boolean type field?
Lets say the schema of the records I want to store is:
{
id:1,
name:"Kris",
_dirty:true
}
I created normal not unique index (onupgradeneeded):
...
store.createIndex("dirty","_dirty",{ unique: false })
...
The index is created, but it is empty! - In the index IndexedDB browser there are no records with Boolean values - only Strings, Numbers and Dates or even Arrays.
I am using Chrome 25 canary
I would like to find all records that have _dirty attribute set to true - do I have to modify _dirty to string or int then?

Yes, boolean is not a valid key.
If you must, of course you can resolve to 1 and 0.
But it is for good reason. Indexing boolean value is not informative. In your above case, you can do table scan and filter on-the-fly, rather than index query.

The answer marked as checked is not entirely correct.
You cannot create an index on a property that contains values of the Boolean JavaScript type. That part of the other answer is correct. If you have an object like var obj = {isActive: true};, trying to create an index on obj.isActive will not work and the browser will report an error message.
However, you can easily simulate the desired result. indexedDB does not insert properties that are not present in an object into an index. Therefore, you can define a property to represent true, and not define the property to represent false. When the property exists, the object will appear in the index. When the property does not exist, the object will not appear in the index.
Example
For example, suppose you have an object store of 'obj' objects. Suppose you want to create a boolean-like index on the isActive property of these objects.
Start by creating an index on the isActive property. In the onupgradeneeded callback function, use store.createIndex('isActive','isActive');
To represent 'true' for an object, simply use obj.isActive = 1;. Then add or put the object into the object store. When you want to query for all objects where isActive is set, you simply use db.transaction('store').index('isActive').openCursor();.
To represent false, simply use delete obj.isActive; and then add or or put the object into the object store.
When you query for all objects where isActive is set, these objects that are missing the isActive property (because it was deleted or never set) will not appear when iterating with the cursor.
Voila, a boolean index.
Performance notes
Opening a cursor on an index like was done in the example used here will provide good performance. The difference in performance is not noticeable with small data, but it is extremely noticeable when storing a larger amount of objects. There is no need to adopt some third party library to accomplish 'boolean indices'. This is a mundane and simple feature you can do on your own. You should try to use the native functionality as much as possible.

Boolean properties describe the exclusive state (Active/Inactive), 'On/Off', 'Enabled/Disabled', 'Yes/No'. You can use these value pairs instead of Boolean in JS data model for readability. Also this tactic allow to add other states ('NotSet', for situation if something was not configured in object, etc.)...

I've used 0 and 1 instead of boolean type.

Related

How do I retrieve all column fields as objects from Tabulator?

I want to make the custom filter dynamic. So, for writing future code I could pass in a list of references to each field object in the table.
That way I do not have to hardcode data.(field name here). Instead, it would work off the list of properties of the column object.
I know there ways to get the field normally but they are always returned as strings not object references. This obviously will not work with the dot operator.
I have some success with using JSON.parse followed by looping through the entries. But like before it returns the field as a string instead of a reference.
So is there a way to retrieve the column fields as objects and if so how?
I tried using the getColumns but I am still getting undefined when grabbing the fields. There is something wrong with my code.
function customFilter(data, filterParams) {
//data - the data for the row being filtered
//filterParams - params object passed to the filter
for (column of table.getColumns()){
field = column.getField();
console.log(data.field);
}
}
You speak about references in your question, but references to what? the field names themselves arent references to anything, they simply show Tabulator how to access the underlying row data, without a specific row data object to reference, there isn't anything to build any references from
You can only have a reference if it points to something, but there is nothing for the field definitions to point to without the row data.
If you are looking to have objects that you can manipulate the the getColumns function returns an array of Column Components with each component having a range of functions that can be called to manipulate that column. including the getField function that returns the field for that column.
Given that the Tabulator filter functions will accept the filed names with dot notation that shouldnt be an issue at all, but you can also pass the column component directly into the filter, so it shouldnt be a problem their either

Laravel: How to get counter value when inserting with UUID and Auto Increment

My models have both id and counter attributes. The id is a UUID, and the counter is an integer which is auto-incremented by the database.
Both are unique however I rely on id as the primary key. The counter is just a human-friendly name that I sometimes display to the user.
Immediately before an object is created a listener gives it a UUID. This works fine.
When the record is saved, MySQL increments the counter field. This works fine except that the copy of the object which I have in memory does not have the counter value. I can reload the object to find out what its counter is, but that would require another database query.
Is there a way to find the value of the counter without a specific database query? For example, is it returned as part of the response from the database when a record is created?
Few things:
Use create(array $attributes) and you'll get exactly what you want. For this having right, you have to ensure that $fillable array consists all attributes' names passed to create method.
You should use Observer on model instead of listener (most likely creating method).
Personal preference using Eloquent is that you should use id for id (increment field) and forget custom settings between models because by default it is what relations expect and so on
public function secondModels()
{
return $this->hasMany(SecondModel::class);
}
is pretty much no brainer. But for having this working best way would be (also following recommendations of this guy) FirstModel::id, SecondModel::id, SecondModel::first_model_id; first_models, second_models as table names. Avoiding and/or skipping this kind of unification is lot of custom job afterward. I don't say it can't be done but it is lot of non-first-time-successful work done.
Also, if you want visitor to get something other than id field name, you can make computed field with accessor:
/**
* Get the user's counter.
*
* #return string
*/
public function getCounterAttribute(): string
{
return (string)$this->id;
}
Which you call then with $user->counter.
Also personal preference of mine is to have most possible descriptive variable names so uuid field of mine would be something like
$table->uuid('uuid4');
This is some good and easy to make practice of Eloquent use.
Saying all this let me just to say that create() and save() will return created object from database while insert() shall not do it.

Indexeddb sorting with multiple indexes

I have a file object store by indexing name and library_id like below,
let objectStore = db.createObjectStore('file', { keyPath: 'id' });
tempStore.createIndex('nameLibId', ['attributes.name', 'attributes.library_id'], { unique: false });
The object store contains multiple library id's files. I'd like apply the name sort to the particular library id's files. I tried indexing in the below format but it returns empty data.
let self = this,
db = get(self, 'db'),
transaction = db.transaction(["file"], "readonly"),
objectStore = transaction.objectStore("file"),
index = objectStore.index('nameLibId'),
keyRange = IDBKeyRange.only('library_id')),
req = index.getAll(keyRange);
req.onsuccess = ((e)=>{
console.log(e.target.result); // returns empty array
});
Attached the screenshot of db model for reference.
24536475, abc, created, jhgf and lastmodified file names are belongs to a library id called 123.
Screen Shot..* file names are belongs to an another library id called 234.
I need the files which are sorted by name only the given library id. Any help would be highly appreciated.
If your index is based on a properties array and you want to match something using IDBKeyRange.only, then your parameter to IDBKeyRange.only should also be an array. Right now you are comparing a basic string value against a properties array value, where of course nothing matches. In other words, you cannot query against a two-part array using only one part of it.
Furthermore, the parameter to IDBKeyRange.only isn't a property name, it is a value. You want to specify a value to match in the index's set of keypath values. For example, if your index was based exclusively on attributes.name, then you would want to specify a particular value within that index, such as "abc".
And so, taking into account the above two points, and given that your index is not a single value but is instead an array of two properties, you need to revise your parameter to IDBKeyRange.only to look for an array. Something like IDBKeyRange.only(['abc', 'yoktc....']);.
Now, this is further complicated by the fact that what you are doing in your code does not actually accomplish what you want. Ignoring the sort concern for a moment, you only want to use the id condition, and not the name, when matching rows of this index. So you might be tempted to try IDBKeyRange.only([undefined, 'asdf']). Unfortunately this will not work at all because you cannot specify undefined (you will get a javascript error).
So, you must always query by both values, even though you only want to apply criteria to one of the values. The trick here is that you switch to using a different method than only. You use IDBKeyRange.bound(), and furthermore, you do a trick where you specify a criteria such as "smallest possible number is less than my number and my number is less than largest possible number", e.g. a condition that always is true. You use "smallest possible value" as your lower boundary, and "largest possible value" as your upper boundary.
Here is an example in your case. The smallest possible value of name I think is empty string. The largest possible value of name is probably any non-alphanumeric character, so let's use tilde "~". So, now we would rewrite the range parameter. Instead of using IDBKeyRange.only, we use IDBKeyRange.bound. It looks like the following (roughly):
var libId = ???;
var smallestNameValue = '';
var largestNameValue = '~';
var lowerBound = [smallestNameValue, libId];
var upperBOund = [largestNameValue, libId];
var range = IDBKeyRange.bound(lowerBound, upperBound);
Now, the second part, regarding sorting, and a major caveat of using indices that have multiple parts (not to be confused with the multiPart index property, ugh). And I myself get this backwards all the time, so I might even be wrong here and the above will work. The problem with the above is that one the first criterion is met the second is ignored, because of how the short-circuited array sorting algorithm works in indexedDB's comparison function. Your query is going to match everything, because every index row meets the criteria. So the trick to this is to always query first by the important condition, to basically pay attention to the order in which you specify your conditions. So what that means is that you need to switch the order of the properties you specified when creating the index, so that you can query first by libId and then by name.
Instead of createIndex('nameLibId',['attributes.name','attributes.library_id']); you want to do createIndex('nameLibId',['attributes.library_id', 'attributes.name']);. And this also means you need to swap your lower and upper bound queries, e.g. var lowerBound = [libId, smallestNameValue]; (and don't forget to switch the upper).
As I mentioned in my answer on using compound indices, you can always using indexedDB.cmp to experiment. Right now, open up the console on this web page. In the console, type something like this:
indexedDB.cmp(['', '5'], ['~', '5']);
Take a look at the results.
Some final notes:
Tilde might be the wrong thing to use, sorry but I am not bothering to remember, you could also just try any valid sentinel value, where by sentinel I mean any value you know will always come after all your other valid values
As I point out in my other answer, if either prop is missing in the data the actual object won't match
for cmp, -1 means left is less than right, 0 means left equals right, and 1 means left greater than right

Define custom POST method for MyDAC

I have three tables objects, (primary key object_ID) flags (primary key flag_ID) and object_flags (cross-tabel between objects and flags with some extra info).
I have a query returning all flags, and a one or zero if a given object has a certain flag:
SELECT
f.*,
of.*,
of.objectID IS NOT NULL AS object_has_flag,
FROM
flags f
LEFT JOIN object_flags of
ON (f.flag_ID = of.flag_ID) AND (of.object_ID = :objectID);
In the application (which is written in Delphi), all rows are loaded in a component. The user can assign flags by clicking check boxes in a table, modifying the data.
Suppose one line is edited. Depending on the value of object_has_flag, the following things have to be done:
If object_has_flag was true and still is true, an UPDATE should be done on the relevant row in objects_flags.
If object_has_flag was false but is now true, and INSERT should be done
If object_has_flag was true, but is now false, the row should be deleted
It seems that this cannot be done in one query https://stackoverflow.com/questions/7927114/conditional-replace-or-delete-in-one-query.
I'm using MyDAC's TMyQuery as a dataset. I have written separate code that executes the necessary queries to save changes to a row, but how do I couple this to the dataset? What event handler should I use, and how do I tell the TMyQuery that it should refresh instead of post?
EDIT: apparently, it is not completely clear what the problem is. The standard UpdateSQL, DeleteSQL and InsertSQL cannot be used because sometimes after editing a line (not deleting it or inserting a line), an INSERT or DELETE has to be done.
The short answer is, to paraphrase your answer here:
Look up the documentation for "Updating Data with MyDAC Dataset Components" (as of MyDAC 5.80).
Every TCustomDADataSet (such as TMyQuery) descendant has the capability to set update SQL statements using SQLInsert, SQLUpdate and SQLDelete properties.
TMyUpdateSQL is also a promising component for custom update operations.
It seems that the easiest way is to use the BeforePost event, and determine what has to be done using the OldValue and NewValue properties of several fields.

LINQ to SQL Table Extensibility Methods

If I have a LINQ to SQL table that has a field called say Alias.
There is then a method stub called OnAliasChanging(string value);
What I want to do is to grab the value, check the database whether the value already exists and then set the value to the already entered value.
So I may be changing my alias from "griegs" to "slappy" and if slappy exists then I want to revert to the already existing value of "griegs".
So I have;
partial void OnaliasChanging(string value)
{
string prevValue = this.alias;
this.Changed = true;
}
When I check the value of prevValue it's always null.
How can I get the current value of a field?
Update
If I implement something like;
partial void OnaliasChanging(string value)
{
if (this.alias != null)
this.alias = "TEST VALUE";
}
it goes into an infinte loop which is unhealthy.
If I include a check to see whether alias already == "TEST VALUE" the infinate loop still remains as the value is always the original value.
Is there a way to do this?
The code snippets you've posted don't lend themselves to any plausible explanation of why you'd end up with an infinite loop. I'm thinking that this.alias might be a property, as opposed to a field as the character casing would imply, but would need to see more. If it is a property, then you are invoking the OnAliasChanging method before the property is ever set; therefore, trying to set it again in the same method will always cause an infinite loop. Normally the way to design this scenario is to either implement a Cancel property in your OnXyzChanging EventArgs derivative, or save the old value in the OnXyzChanging method and subsequently perform the check/rollback in the OnXyzChanged method if you can't use the first (better) option.
Fundamentally, though, what you're trying to do is not very good design in general and goes against the principles of Linq to SQL specifically. A Linq to SQL entity is supposed to be a POCO with no awareness of sibling entities or the underlying database at all. To perform a dupe-check on every property change not only requires access to the DataContext or SqlConnection, but also causes what could technically be called a side-effect (opening up a new database connection and/or silently discarding the property change). This kind of design just screams for mysterious crashes down the road.
In fact, your particular scenario is one of the main reasons why the DataContext class was made extensible in the first place. This type of operation belongs in there. Let's say that the entity here is called User with table Users.
partial class MyDataContext
{
public bool ChangeAlias(Guid userID, string newAlias)
{
User userToChange = Users.FirstOrDefault(u => u.ID == userID);
if ((userToChange == null) || Users.Any(u => u.Alias == newAlias))
{
return false;
}
userToChange.Alias = newAlias;
// Optional - remove if consumer will make additional changes
SubmitChanges();
return true;
}
}
This encapsulates the operation you want to perform, but doesn't prevent consumers from changing the Alias property directly. If you can live with this, I would stop right there - you should still have a UNIQUE constraint in your database itself, so this method can simply be documented and used as a safe way to attempt a name-change without risking a constraint violation later on (although there is always some risk - you can still have a race condition unless you put this all into a transaction or stored procedure).
If you absolutely must limit access to the underlying property, one way to do this is to hide the original property and make a read-only wrapper. In the Linq designer, click on the Alias property, and on the property sheet, change the Access to Internal and the Name to AliasInternal (but don't touch the Source!). Finally, create a partial class for the entity (I would do this in the same file as the MyDataContext partial class) and write a read-only wrapper for the property:
partial class User
{
public string Alias
{
get { return AliasInternal; }
}
}
You'll also have to update the Alias references in our ChangeAlias method to AliasInternal.
Be aware that this may break queries that try to filter/group on the new Alias wrapper (I believe Linq will complain that it can't find a SQL mapping). The property itself will work fine as an accessor, but if you need to perform lookups on the Alias then you will likely need another GetUserByAlias helper method in MyDataContext, one which can perform the "real" query on AliasInternal.
Things start to get a little dicey when you decide you want to mess with the data-access logic of Linq in addition to the domain logic, which is why I recommend above that you just leave the Alias property alone and document its usage appropriately. Linq is designed around optimistic concurrency; typically when you need to enforce a UNIQUE constraint in your application, you wait until the changes are actually saved and then handle the constraint violation if it happens. If you want to do it immediately your task becomes harder, which is the reason for this verbosity and general kludginess.
One more time - I'm recommending against the additional step of creating the read-only wrapper; I've put up some code anyway in case your spec requires it for some reason.
Is it getting hung up because OnaliasChanging is firing during initialization, so your backing field (alias) never gets initialized so it is always null?
Without more context, that's what it sounds like to me.