Secondary Index on Custom Java Object in Aerospike - aerospike

I have two classes.
class A {
String aName;
B b;
public A(String aName, B b) {
this.aName = aName;
this.b = b;
}
public String getaName() {
return aName;
}
public B getB() {
return b;
}
}
class B {
String bName;
public B(String bName) {
this.bName = bName;
}
public String getbName() {
return bName;
}
}
I am storing A as a set in Aerospike and A.aName is primary key. I want a secondary key on A.b. I have created index on A.b attribute and able to persist also. But search from the index is not returning anything. As per my understanding, Aerospike supports only three type of indexes: String, Numeric and Geo,. Is there any option for custom object.

Actually you can also index string, numeric and geo within different source types - basic (meaning just a bin with scalar data), list (so you can index strings or numeric data that is contained in a list), map keys and map values.
See: https://www.aerospike.com/docs/guide/query.html#secondary-index-key-source-type
You could model this in a few ways:
As a map. Let's assume that you store the value of A in a bin whose type is a map, it could have two map-keys - aName and bName. You can build a secondary index for string data on map values. Now, if you search for a specific bName you'll have this record come up.
More rationally you'd do the following as separate bins.
Assume that you use several bins, among them two bins to hold aname and bname. Their values are strings. You could now build a secondary index for string values and a basic index type (string data not contained in a complex data type such as list or map). You'd query for all the records where the predicate is bname = foo.
For a more complex situation one or more bname values map to a single aname, you'd actually model this as a lookup table.
Assume a set (table) called users holding records whose keys are the aname. A single key-value operation such as a read or an upsert works on a specific instance of class A, identified by a given aname. One record in Aerospike per instance of A.
You have another set (table) as the lookup table, where for each unique bname you create a record. The specific bname is the key to this record. The value is the aname. So to find the record in the users set, you first look it up in the lookup table by the bname. You use the value of this record as the key for the users record for that aname. This is the most common way of modeling this type of use case, without a secondary index.

Here is the answer post on Aerospike forum.
Binary blobs can be anything, there’s definitely no way to index that. If there is a particular field in your class you can pull out and set as a separate bin with a string/long type then that would work
https://discuss.aerospike.com/t/secondary-index-on-custom-java-object/6485

Related

How to group objects in a list by two fields?

I would like to group a list of objects basing on two fields in those objects.
Let's say I have an object like this:
data class ItemDataDTO(
val itemId: BigInteger?,
val sequence: BigInteger?,
val serialNumber: String?,
val pickingId: String?,
val runId: String? = null,
val warehouse: String?,
)
Now I have a list of ItemDataDTO containing a lot of items. I would like to group them by runId and pickingId (because I need those items that have the same pickingId and runId grouped somehow.)
val items: List<ItemDataDTO> = someItemRepository.getItemsForWarehouse("someWarehouseId")
val groupedItems = items.groupBy({ it.runId }, { it.pickingId })
This doesn't work. I found out that I could use groupingBy() function along with a Triple, but I want just them to be grouped by two values...
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, null) }
But this doesn't work as well. I tried to add a third parameter instead of null, using it.warehouse:
val groupedItems = items.groupingBy { Triple(it.runId, it.pickingId, it.warehouse) }
It returns an instance of Grouping<ItemDataDTO, Triple<String?, String?, String?>> and I'm not sure what to do with this object.
What could I do to properly group those objects?
In a perfect world, I would like to transform this list to a list of something like:
data class PickingList(
val runId: String,
val pickingId: String,
val items: List<ItemDataDTO>,
)
So the output would be a List<PickingList>.
There's nothing special about it really! groupBy takes a keySelector function which returns some value to be used as a key for that item. So if you want to match on two properties, that key item needs to be composed of those two values.
A Triple with two items is a Pair, so you can just do this:
// just FYI, "it.runId to it.pickingId" is shorthand for a Pair - you see it more
// when defining key/value pairs for maps though. "Pair(x, y)" might read better here
// since you're really just combining values, not describing how one relates to the other
items.groupBy { Pair(it.runId, it.pickingId) }
So each item will produce a Pair with those two values. Any other items with a Pair that matches (as far as the equals function goes) will be put into the same group. It's a bit like adding to a Map, except that if a key already exists, the value is added to a list instead of overwriting the previous value.
You can do that with any key really. Pair and Triple are just quick, general convenience classes for bundling a few items together - but a lot of the time it's better to define your own data structure, e.g. using a data class. So long as two instances with the same data are equal, they count as the same key for grouping.
As for the output you want, with the PickingList... you could use something like that for your grouping operation - but in that case you'd have to pretty much reimplement groupBy yourself. You'd have to take an item, and work out its composite key from the properties you want to consider. Then you'd need to find a match for that key in some store you've created for your groups
If it's a list of PickingLists, you'd need to go through each one, comparing its IDs to the ones you want, adding to its list if you find a match and creating the object if you can't find it.
If you're storing a map of Pair(id1, id2) -> PickingList then that's close to how groupBy works anyway, in terms of generating a key for lookups. In that case, you might want to just use groupBy to group all your items, and then transform the final map:
items.groupBy { Pair(it.runId, it.pickingId) }
.map { (ids, list) ->
PairingList(runId = ids.first, pickingId = ids.second, items = list)
}
This takes every map entry (a Pair of IDs and the list of all things grouped by those IDs) and uses it to create a PairingList from that key/value data. Basically, once you've grouped all your data, you transform it into the data structures you want to work with.
This is also a good example of why your own data class might be better than just using a Pair - it.first doesn't really tell you what that value is in the Pair, just that it's the first of the two values. Whereas
data class IdCombo(val runId: String, val pickingId: String)
works the same as a Pair, but the properties have useful names and make your code much more readable and less prone to bugs:
map { (ids, list) ->
// didn't even bother with the named arguments, since the names are in
// the ids object now!
PairingList(ids.runId, ids.pickingId, items = list)
}

Best data structure to store key value pairs when the following operations are required

I need to store a list of key value pairs. The key value pair will be used by multiple classes, so ideally its something like a list of a structure I define.
Public Structure Person
Public ID As Integer
Public Weight As Decimal
End Structure
'all use Person
class census
class importpeople
class determinebestdiet
etc
I need to sort by value
I need to remove a person (by Person.ID) or add a new key value pair (and re-sort after each add)
I need to sum the total of all values
Seems like often people use dictionaries and then hack around.
You can use List<Person> to help your work
Using the List.Sort method:
theList.Sort(Function(x, y) x.age.CompareTo(y.age))
Using the OrderBy extension method:
theList = theList.OrderBy(Function(x) x.age).ToList()
Details can see this post: https://stackoverflow.com/a/11736001/1050927
Just remove from list and call Sort or OrderBy.
Depends on what you want to do, you can use OrderBy with Skip and Take to get specific nth person after sort.
Just Sum it
PersonList.Sum(Function(per) per.Weight)
And I believe Dictionary can do the same too, just you can't reuse the Person only.

Verify HTML Table using Selenium

Can someone let me know which approach is good for verifying HTML Table. I have to write test case related to adding/deleting/editing HTML table using selenium.
Table looks like this
Name and Branch could be duplicate, the only way to uniquely Identify the row is using id (First column).
I am using the page object pattern, so I need to write the function for adding/editing/deleting and verifying that row exist or not.
how should I write a function to add record and then a separate function to verify that record has been added successfully. As the name could be duplicate I can not verify if name exist in the table or not. The other way is, I return the ID of last row once I add the record. And then I can verify if name on last id is same as I recently added. However this approach has some glitch as well.
is there any better approach for this situation.
For add test case, you could create a function in your PageObject that returns the count of rows that have the same value in the Name field.
In your test case,
Store this number in a variable
Add a record
Back to the grid screen, invoke the row counting function again, comparing it with the stored value
It must be the stored value + 1, otherwise something is wrong.
As an additional check, you could also store the total number of rows in the table prior adding, and check if that value has also increased by one after edition (ensuring no record has been replaced in the process).
Note: If you're using PageFactory, you could also create an IList with the rows in the table, and use its count property instead of creating a counting function.
Edit:Your add method can be stored inside your PageObject, so that you have it separated from assertion. Even if you return the id, without knowing what was in the table prior adding your record, a separated individual verification won't be of much use.
So without seeing the html for this table, I'll have to make some assumptions. It looks like the ID column are hyperlinks, so the xpath for those will be slightly different from the other cells. Okay, so onto the solution.
We first need to think about how we're going to store the data from the table. You want some kind of 2 dimensional object that is easy to work with. I like to use HashMap to store the individual rows. HashMap stores information in un-ordered, key-value pairs. They Key would be the name of the column, and the Value would be the value of that cell for that row.
To store a table's worth of data, I would then put each of those rows that are stored in separate HashMaps and put that into an ArrayList.
Now onto how to scrape the data, the fastest solution is always the one that does the least number of lookups and calls to Selenium. In this case since you have all kinds of weirdness going on with the table, I would target all of the data cells with one lookup.
Without seeing the HTML, my xpath would look something like //table[#id='something']/tbody/tr/td[1]/a|//table[#id='something']/tbody/tr/td[not(./a[./text()='View' or ./text()='Edit' or ./text()='Delete'])]
The Pipe symbol conjoins the 2 xpaths together so it will target both types of cells in the correct order.
Here's my generic TableData class. You create an instance of this class to store the data. The data itself gets stored in the _data object.
public class TableData {
public ArrayList<HashMap<String, String>> _data;
public TableData(ArrayList<HashMap<String, String>> data) {
_data = data;
}
public static TableData get_SiteTable_ByCell(WebDriver driver, By tableCells, String[] headerValues) {
ArrayList<WebElement> tableCellElements = new ArrayList<WebElement>(driver.findElements(tableCells));
ArrayList<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
int numberRows = tableCellElements.size() / headerValues.length;
for (int i = 0; i < numberRows; ) {
HashMap<String, String> newRow = new HashMap<>();
for (int j = 0; j < headerValues.length; j++, i++) {
newRow.put(headerValues[j], tableCellElements.get(i).getText());
}
data.add(newRow);
}
return new TableData(data);
}
}
So now that we have our method for getting the data, the values that I need to pass in are...
public TableData getTable(WebDriver driver)
{
String[] headers = {"ID", "Name", "Branch"};
By cellsBy = By.xpath("//table[#id='something']/tbody/tr/td[1]/a|//table[#id='something']/tbody/tr/td[not(./a[./text()='View' or ./text()='Edit' or ./text()='Delete'])]");
return TableData.get_SiteTable_ByCell(driver, cellsBy, headers);
}
Okay, so now we want to get a specific row. I need to write a method that will return a HashMap for the given criteria. This will also go in the TableData class from earlier.
public HashMap<String, String> returnRow(String columnName, String columnValue){
for(HashMap<String, String> row : _data){
if(row.get(columnName).equals(columnValue)){
return row;
}
}
return null;
}
So now that I have all that, my code would look something like...
#Test
public void newTest1()
{
TableData customerTable = getTable(driver);
HashMap<String, String> rowWeWant = customerTable.returnRow("ID", "1");
String valueWeWant = rowWeWant.get("Name");
}

Map implementaion using arraylist as value

I am using Maps in my code for the first time, hence require some inputs from you experts.
My requirement is I have to check two different tables from database. Value from First table will be used as Key and Value for second table will be used as Value for the key.
Each key will have multiple values, so I will be storing all values against each key in a arraylist i.e. my Map will be like MAP.
Now, my issue is following:
I don't know the total no. of keys, so I can't create arraylist objects in advance. How to manage this?
How can I check if key exists in map such that if it exists then I have to updated the arraylist corresponding to it only. And if it doesn't exist then create new key, create arraylist corresponding to it, populate arraylist with the value.
Finally I have to iterate whole map and use the key and values.
How can it be implemented? Am I following the right approach? if not what is a better approach?
Thanks
With lists in Java you do not need to know the size up front. That is a requirement for Arrays. Therefore just create your Map
Map> myMap = new HashMap<>();
This should work
if (myMap.containsKey(someKey)) {
myMap.get(someKey).add(someValue); // adds a value to the list that already is in the map
} else {
myMap.put(someKey, Arrays.asList(someValue)); // which inserts a new key/value
}
If you need to iterate over all values in the list then you need a nested for loop
for (Map.Entry> entry : myMap.entrySet()) {
// your key = entry.getKey()
for (ValueType value : entry.getValue()) {
// use your value
}
}
http://docs.oracle.com/javase/7/docs/api/java/util/Map.html
Hope that helps.

NHibernate autoincrement string column

I want to have a column of type string that will be composed of static prefix and dynamic auto incremented integer. Number should autoincrement on every insert. This column is not PK but it should act like id. Also i would like to filter on that column.
Something like
PREFIX_0000
PREFIX_0001
PREFIX_0002
PREFIX_0003
I don't want to store a counter in some table.
Is there a possibility to do that on NHibernate mapping level? Or any other ideas?
Thanks.
in you DTO you can make a getter which returns a prefix_######ID:
public string MyAutoIncrement
{
get { return string.Format("{0}_{1:D8}",this.Prefix,this.Id); }
}