Is it possible to use primary key along with secondary key in Aerospike for fetching a record? - aerospike

Is it possible to use primary key along with secondary key. I want to get all records on the basis of primary key and then apply secondary index over it? Is it the correct way to do it or some other approach should be used?
I have a bin with primary key. Once i fetch the record using primary key, i want to get only the subset of the record.
Below is the record example. Also is there a better way to organise the record? Like keeping manager, supervisor and otherStaff as the key and then querying.
{
"name" : "test",
"companyName" : "company1",
"details" : [{
"salary" : 1000,
"designation" : "manager"
},
{
"salary" : 2000,
"designation" : "supervisor"
},
{
"staff" : 500,
"designation" : "otherStaff"
}]
}

It is definitely beneficial if you can query by primary key first. It will be very efficient as the request will land on a single node (as opposed to all nodes in case of secondary index query). And on that node, the primary key index lookup is going to be fast.
When you say secondary index, looks like your requirement is to do additional filteration on the data. If my understanding is correct, you can use the Map API to fetch the keys (or values) that you are explicitly looking for. For this you may need to tweak your data model accordingly. In your example, if you are going to fetch different salaries, may be you should create a map of salaries (instead of "salary" being a key in map). Something like this:
{
"name" : "test",
"companyName" : "company1",
"salaries" : {
"manager" : 1000,
"supervisor" : 2000,
"otherStaff" : 500
}
}
You can then use the getByKeyList() map API to fetch multiple values by specifying the keys (in the map) that you are interested in (say 'manager' and 'otherStaff').

Use a Secondary Index query on the bin containing the primary search data and then use Predicate Filters to further select the records returned.
Example construct - record has country and city bins in namespace test, set testset, secondary index = equality string on country.
// Java statement
Record record = null;
Statement stmt = new Statement();
stmt.setSetName("testset");
stmt.setNamespace("test");
stmt.setIndexName("country_idx");
stmt.setFilter(Filter.equal("country","USA"));
//Additional Predicate Filtering
stmt.setPredExp(
PredExp.stringBin("city"),
PredExp.stringValue(".*Diego"),
//prefix-suffix match: prefix.*suffix, ignore case or newline
PredExp.stringRegex(RegexFlag.ICASE | RegexFlag.NEWLINE)
);

Related

What is the best way to update a known row in SQLite?

For example, if I want to frequently update a user's score during a session,
is there a more performant way than - UPDATE score FROM [databaseName] WHERE name = [username]
I feel like I should not be continuously needing to search through the entire database when the value's location has been previously found.
Thanks in advance
You wouldn't search through the entire database if you had an index on name:
create index idx_<table>_name on <table>(name);
where <table> is the name of the table you are referring to, not the database.
Incidentally, if you wanted to update the table, you would use update, not select. But update can still use the index.
You can use properties here you can search and update only the database when you need it.
private int score;
public int Score
{
get
{
score = searchDatabase();
return score;
}
set
{
UpdateDatabase(value);
score = value;
}
}
Debug.Log(Score);
The only thing that comes to mind that resembles what you mention as the value's location, is the value of the primary key column of the row with the value that you search for.
So, if the primary key in the table is id and you only have the name of the person, you can query once to get the row's id:
SELECT id FROM tablename WHERE name = ?
and then use that id in all subsequent updates:
UPDATE tablename SET score = ? WHERE id = ?
This is the fastest way to do the updates.

Many to many relationship when order metter

I have many to many relation between User and Group: group may contains few users and consists in multiply groups.
Server send me json like this, and I need to store it to DB:
group: {
name: "test", id: "UUID"
users: [
{name: "user1", id: "UUID"},
{name: "user2", id: "UUID"},
{name: "user3", id: "UUID"}
]
}
The problem is: users order in group matters - users should be displayed in the same order like in servers json.
The fist solution that I use is add to group field like
user_ids: [user1_uuid, user2_uuid, user3_uuid] and store it into DB in json fromat, but I see 2 problems in this solution:
It contradicts to first normal form (1NF)
When I SELECT values they have wrong order:
SELECT * FROM user WHERE id IN (:user_ids)
How to store it into DB and SELECT?
In SQL, table rows indeed are unordered; the only guaranteed way to get ordered rows is to use ORDER BY.
If the order of your array elements is important, add a column for the array index.
You could use the following schema:
CREATE TABLE user(id integer primary key, name text);
CREATE TABLE grp(id integer primary key, name text);
CREATE TABLE usergrp(userid integer, grpid integer, position integer);
CREATE INDEX positionindex on usergrp(grpid, position);
Then you can retrieve users belonging to a group with the query
SELECT userid FROM usergrp WHERE grpid=? ORDER BY position;
Also, maybe you would like to use the "without rowid" sqlite table storage option for usergrp.

SQLite UNIQUE constraint dependent on another column

I have a Java file that creates a subjects table in SQLite like so:
public static final String CREATE_SUBJECTS_TABLE = "create table " + SUBJECTS_TABLE_NAME +" (ID INTEGER PRIMARY KEY AUTOINCREMENT,SUBJECT TEXT,LEVEL INTEGER)";
db.execSQL(CREATE_SUBJECTS_TABLE);
If I wanted to make SUBJECT TEXT unique I could edit it to say SUBJECT TEXT UNIQUE, however this would mean I couldn't have the same subject at multiple levels. I want a way to be able to have the same subject at multiple levels, but not have a repeat of a subject with the same level. A UNIQUE constraint, but only within subjects with the same value in the level column.
I was wondering if there is a built in way to do this, or if I'll have to code it myself. If so, is there a common syntax that I should be using to achieve this?
Thanks
Sounds like you actually want the (subject, level) pair to be unique. That would let you have duplicate subjects as long as the levels were different but duplicate subjects with the same level would not be allowed. A standard unique constraint supports this so you could add unique(subject, level) at the end of your table's definition:
public static final String CREATE_SUBJECTS_TABLE = "create table " + SUBJECTS_TABLE_NAME + " (..., unique(subject, level))";

Remove duplicate lines from SQL Server table

Someone deployed a SQL table with the schema
ConfigOptions
name VARCHAR(50)
value VARCHAR(50)
and the following logic for saving options:
int i = ExecuteNonQuery("UPDATE ConfigOptions SET value=#value WHERE name=#name");
if(i==0) i = ExecuteNonQuery("INSERT INTO ConfigOptions (name,value) (#name,#value)");
We now saw that this table is littered with duplicates, and we want to change this.
As far as I can tell, the logic is: whenever the UPDATE affected zero rows, another row is inserted. If I am not mistaken, this can be caused by:
a row by the name of #name does not exist or
the row exists, but already contains value #value
So, all rows with same name should be full duplicates. If now, something is completely wrong (and behaviour may be undefined).
Now I have to fix this problem of duplicates, so I want to add a PK on name. Before I can do this, I have to remove all rows with duplicate names, only keeping one of each.
In the installer (only the installer is allowed to change schema), I only have SQL queries at hand, so I can't do it with C# logic:
Dictionary<string, int> dic = new Dictionary<string, int>();
SqlDataReader sdr = ExecuteReader("SELECT name,COUNT(value) FROM ConfigOptions GROUP BY name HAVING COUNT(value)>1");
while (sdr.Read()) dic.Add(sdr.GetString(0), sdr.GetInt32(1));
sdr.Close();
foreach (var kv in dic) {
AddParameter("#name", System.Data.SqlDbType.VarChar, 50, kv.Key);
ExecuteNonQuery("DELETE TOP " + (kv.Value - 1) + " FROM ConfigOptions WHERE name=#name");
}
ExecuteNonQuery("ALTER TABLE program_options ADD PRIMARY KEY (name)");
Is there a way to put this into SQL logic?
Using %%physloc%%, the phys(ical) loc(ation) of the row, should do the trick:
DELETE FROM ConfigOptions
WHERE %%physloc%% NOT IN (
SELECT MIN(%%physloc%%)
FROM ConfigOptions
GROUP BY name);
After this cleanup, you can add the primary key to the table.
NOTE: this will leave you with only one row for every name. If the value column is different in two records with the same name, you will lose the newest record. If you want to change this, use GROUP BY name, value.

How to set primary key on a field in fusion table

I have a table named "Testing". fields in this table are Id,Name and Description.
I want to make Id field as a primary key i.e. only unique values should be entered in this field.
Thanks,
Himanshu
When you do CREATE TABLE
In addition to the column names you specify in the CREATE TABLE statement, Google Fusion Tables will create a column named ROWID that contains an automatically-assigned, unique ID number for each row.
I don't think you can do this. It appears to be a much requested feature: https://code.google.com/p/fusion-tables/issues/detail?id=112
It's not available. I use a workaround that performs a COUNT(ROWID) query for the unqiue ID I'm about to insert and then take steps depending on whether or not that unique ID is already in the table.
The basic algo is something like this (using Google Apps Script):
var check = FusionTables.Query.sql("SELECT COUNT(ROWID) FROM <table ID> WHERE 'Trip ID' = '" + rowID + "'");
if (check.rows[0][0] > 0) {
// ID already exists
} else {
// ID is new, insert the record.
}