newbie to Mustache here
I would like to create a table that uses one part of the template if the value of a field changes from one row to the next.
With data like the following (sorted at source by 'group'):
0: {
'group': 'stars',
'name': 'George',
'rating': '5'
}
1: {
'group': 'stars',
'name': 'Tom',
'rating': '3'
}
2: {
'group': 'singers',
'name': 'Tanya',
'rating': '5'
}
3: {
'group': 'singers',
'name': 'Fred',
'rating': '4'
}
Would produce:
+----------------------+
| NAME | Rating |
+----------------------+
| stars |
+----------------------+
| George | 5 |
+----------------------+
| Tom | 3 |
+----------------------+
| singers |
+----------------------+
| Tanya | 5 |
+----------------------+
| Fred | 4 |
+----------------------+
Is there a way/function in Mustache.js to do this? Something like an if/else with a test for a change in a variable's value from one row to the next?
Related
As a lockdown project I'm introducing myself to the concept of multi-tenancy applications. My simple application has a tenant who has a an online shop front. The shop has product categories each containing many products. My initial thought on database schema is as follows:
+====================================================================================+
| Primary Key | Sort Key (GSI PK) | Attribute 1 (GSI SK) | Attribute 2 | Attribute 3 |
|-------------|-------------------|----------------------|-------------|-------------|
| TENANT-uuid | CATEGORY-uuid | categoryName | ... | ... |
| TENANT-uuid | PRODUCT-uuid | productName | ... | ... |
| TENANT-uuid | PRODUCT-uuid | productName | ... | ... |
+====================================================================================+
So our GSI looks like so:
+=======================================================================================+
| Primary Key | Sort Key | Attribute 1 (PK) | Attribute 2 (SK) | Attribute 3 |
|---------------|-------------------|------------------|------------------|-------------|
| CATEGORY-uuid | categoryName | TENANT-uuid | CATEGORY-uuid | ... |
| PRODUCT-uuid | productName | TENANT-uuid | PRODUCT-uuid | ... |
| PRODUCT-uuid | productName | TENANT-uuid | PRODUCT-uuid | ... |
+=======================================================================================+
If I were to implement the following role policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": [
"arn:aws:dynamodb:XXX:XXX:table/XXX"
],
"Condition": {
"ForAllValues:StringEquals": {
"dynamodb:LeadingKeys": [
"TENANT-uuid"
]
}
}
}
]
}
How does the LeadingKeys condition work if we're running a query on an index?
Update 1
So upon further inspection it seems one way to do this (for this situation) is to have a GSI with the partition key as the TENANT-uuid and the sort key as the item's parent. I've realised I should probably add slightly more information as follows.
Our desired outcomes are:
Get list of tenant's categories -> Query with PK = TENANT-uuid and SK BeginsWith "CATEGORY"
Get list of tenant's products -> Query with PK = TENANT-uuid and SK BegingsWith "PRODUCT"
Get list of products in a specific tenant's category -> ???
Get single tenant's category -> Query with PK = TENANT-uuid and SK = CATEGORY-uuid
Get single tenant's product -> Query with PK = TENANT-uuid and SK = PRODUCT-uuid
As it stands the only one that was an issue was number 3. A little reorganisation of the schema as follows seems to work. However it does limit our ability to sort our data slightly.
Table
+----------------------+---------------+-----------------+-------------+
| TenantID (PK/GSI PK) | ItemType (SK) | Data - (GSI SK) | Attribute 2 |
+----------------------+---------------+-----------------+-------------+
| TENANT-uuid | CATEGORY-1 | Category Name | ... |
+----------------------+---------------+-----------------+-------------+
| TENANT-uuid | PRODUCT-1 | CATEGORY-1 | ... |
+----------------------+---------------+-----------------+-------------+
| TENANT-uuid | PRODUCT-2 | CATEGORY-1 | ... |
+----------------------+---------------+-----------------+-------------+
Index
+---------------+---------------+------------+-------------+
| TenantID (PK) | Data (SK) | ItemType | Attribute 2 |
+---------------+---------------+------------+-------------+
| TENANT-uuid | Category Name | CATEGORY-1 | ... |
+---------------+---------------+------------+-------------+
| TENANT-uuid | CATEGORY-1 | PRODUCT-1 | ... |
+---------------+---------------+------------+-------------+
| TENANT-uuid | CATEGORY-1 | PRODUCT-2 | ... |
+---------------+---------------+------------+-------------+
So now, for number 3, to get a list of products in a specific tenant's category we query the index with PK = TENANT-uuid and SK=CATEGORY-uuid
This allows us to meet the leadingKeys condition.
However, I'm not sure if this it the best solution. For the time being, in my little project, it works.
After almost giving up, I have found a solution. See this SO post describing how you can use wildcards in the IAM policy. Then in your GSI's, you could prefix each of your Id's with a tenant ID. Using your second table as an example, replace CATEGORY-uuid with TENANT-uuid-CATEGORY-uuid
And then your policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"dynamodb:GetItem",
"dynamodb:PutItem"
],
"Resource": [
"arn:aws:dynamodb:XXX:XXX:table/XXX"
],
"Condition": {
"ForAllValues:StringLike": {
"dynamodb:LeadingKeys": [
"TENANT-uuid*"
]
}
}
}
]
}
I tested this quick, it works just fine, and this is the approach I plan to use in my multi-tenant app.
I have a SQLite database that looks similar to this:
---------- ------------ ------------
| Car | | Computer | | Category |
---------- ------------ ------------
| id | | id | | id |
| make | | make | | record |
| model | | price | ------------
| year | | cpu |
---------- | weight |
------------
The record column in my Category table contains a comma separated list of the table name and id of the items that belong to that Category, so an entry would look like this:
Car_1,Car_2.
I am trying to split the items in the record on the comma to get each value:
Car_1
Car_2
Then I need to take it one step further and split on the _ and return the Car records.
So if I know the Category id, I'm trying to wind up with this in the end:
---------------- ------------------
| Car | | Car |
---------------| -----------------|
| id: 1 | | id: 2 |
| make: Honda | | make: Toyota |
| model: Civic | | model: Corolla |
| year: 2016 | | year: 2013 |
---------------- ------------------
I have had some success on splitting on the comma and getting 2 records back, but I'm stuck on splitting on the _ and making the join to the table in the record.
This is my query so far:
WITH RECURSIVE record(recordhash, data) AS (
SELECT '', record || ',' FROM Category WHERE id = 1
UNION ALL
SELECT
substr(data, 0, instr(data, ',')),
substr(data, instr(data, ',') + 1)
FROM record
WHERE data != '')
SELECT recordhash
FROM record
WHERE recordhash != ''
This is returning
--------------
| recordhash |
--------------
| Car_1 |
| Car_2 |
--------------
Any help would be greatly appreciated!
If your recursive CTE works as expected then you can split each of the values of recordhash with _ as a delimiter and use the part after _ as the id of the rows from Car to return:
select * from Car
where id in (
select substr(recordhash, 5)
from record
where recordhash like 'Car%'
)
I already have GSI set to author as partition key and status as a sort key and use them to query the data I need. However, I can't figure out to sort my return data by chronological order.
DynamoDb table
-----------------------------------------------
| id | post | author | status | createdAt |
-----------------------------------------------
| 1 | post1 | author1 | publish | 2019-12-10 |
-----------------------------------------------
| 2 | post2 | author2 | draft | 2019-12-11 |
-----------------------------------------------
| 3 | post3 | author1 | publish | 2019-12-12 |
-----------------------------------------------
and the query
var params = {
TableName : "TABLENAME",
IndexName: "gsi-authorStatus",
KeyConditionExpression: "author = :author AND status = :status",
ExpressionAttributeValues: {
":author": JSON.stringify(event.bodyJSON.author),
":status": JSON.stringify(event.bodyJSON.status)
}
};
dynamo.query(params, function (err, data) {
if (err) {
console.error('Error with ', err);
context.fail(err);
} else {
context.succeed(data);
}
});
My query give me the data where author and status are matching but it give me a data in a random order. It is possible to use createdAt to make the return data order by latest?
I am trying to do the following.
Connected to DevCluster
[cqlsh 5.0.1 | Cassandra 3.10.0.1695 | DSE 5.1.1 | CQL spec 3.4.4 | Native protocol v4]
Use HELP for help.
user#cqlsh:test> desc table del28;
CREATE TABLE test.del28 (
sno int PRIMARY KEY,
dob date,
name range_dates,
ssss_details map<text, date>,
ssss_range map<text, frozen<map<date, date>>> );
CREATE INDEX idx_ssss_range ON test.del28 (keys(ssss_range));
CREATE INDEX ssss_details_idx ON test.del28 (values(ssss_details));
CREATE INDEX ssss_range_idx ON test.del28 (values(ssss_range));
user#cqlsh:test> select * from del28;
sno | dob | name | ssss_details | ssss_range
-----+------+--------------------------------------+----------------------------------------------+---------------------------------
5 | null | {start: 2014-03-05, end: 2018-04-05} | {'hello': 2014-05-05} | {'1': {2018-04-05: 2012-02-05}}
8 | null | {start: 2018-03-04, end: 2018-08-02} | {'hello8': 2018-08-08} | {'8': {2018-08-08: 2012-02-08}}
2 | null | {start: 2018-03-04, end: 2018-05-05} | {'hello': 2018-05-05} | {'1': {2018-07-08: 2018-09-01}}
4 | null | {start: 2014-03-04, end: 2018-04-02} | {'hello1': 2014-05-02} | {'1': {2018-04-08: 2012-02-04}}
7 | null | {start: 2014-03-04, end: 2018-04-02} | {'hello4': 2014-05-03, 'hello5': 2014-05-02} | {'2': {2018-04-08: 2012-02-04}}
6 | null | {start: 2014-03-04, end: 2018-04-02} | {'hello2': 2014-05-02, 'hello3': 2014-05-03} | {'2': {2018-04-08: 2012-02-04}}
9 | null | {start: 2014-03-04, end: 2018-04-02} | {'hello7': 2014-05-02, 'hello8': 2014-05-03} | {'2': {2018-04-08: 2012-02-04}}
3 | null | {start: 2014-03-04, end: 2018-04-02} | {'hello': 2014-05-02} | {'1': {2018-04-08: 2012-02-04}}
(8 rows)
My question is, can I use Filters on ssss_range, if so how? If not what is the best way to save this data. Idea is, there is number or text followed by dates. Example house1: {2012-04-05: 2013-02-05}, house2:{2013-04-08: 2014-02-04}...... for one particular user and where dates are a set and , explaining that person stayed on these times. I tried to split the dates in 'name' column. Still it did not work for me. Now there is lot of other info regarding this record.
I should be able to query based on house1, house2 i.e. where aaa = 'house1', some thing like that. Also should be able query based on dates i.e. where from_date > '' and to_date < ''. Something like that.
I am okay to change the way data is changed if it can query a better way. Any type of collections or data types are fine.
Please suggest the right approach.
Thanks
I'm parsing a big XML file, with many items. Each item has many categories, which can repeat. Here's a sample XML.
<item>
<category>Category1</category>
<category>Category2</category>
<category>Category3</category>
<category>Category4</category>
<category>Category5</category>
</item>
<item>
<category>Category1</category>
<category>Category2</category>
<category>Category3</category>
<category>Category7</category>
<category>Category9</category>
</item>
Using doctrine to handle the many-to-many relationship described above, I have a sample code like this:
$em = $this->getDoctrine()->getEntityManager();
foreach ($items as $item) {
[...]
$categories = ... //Array with category names, parsed from the XML.
foreach ($categories as $category) {
//This will check if the 'item' entity
//already has a category with that name.
$exists = $entity->getCategories()->exists(function($key, $element) use ($category) {
return $category == $element->getName();
});
if (!$exists) {
//If there's already one on the database, we'll load it.
//Otherwise, we'll save a new Category..
$query = $this->_entityManager->createQueryBuilder();
$query->select('c')
->from("MyBundle:Category, 'c');
->where("c.name = :name")
->setParameter("name", $category);
}
$result = $query->getQuery()->getOneOrNullResult();
if ($result != null) {
$item->addCategory($result);
} else {
$categoryEntity = new Category($category);
$em->persist($categoryEntity);
$item->addCategory($categoryEntity);
}
}
}
}
The thing is: I only flush() the entitymanager when I complete looping through all items. Therefore, $query->getQuery()->getOneOrNullResult() always returns null, leading me to create duplicated categories.
In the XML example above, I have the following:
| item |
| 1 |
| 2 |
| category.id, category.name |
| 1, Category1 |
| 2, Category2 |
| 3, Category3 |
| 4, Category4 |
| 5, Category5 |
| 6, Category1 |
| 7, Category2 |
| 8, Category3 |
| 9, Category7 |
| 10, Category9 |
| item | category |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 2 | 6 |
| 2 | 7 |
| 2 | 8 |
| 2 | 9 |
| 2 | 10 |
I wanted the following:
| item |
| 1 |
| 2 |
| category.id, category.name |
| 1, Category1 |
| 2, Category2 |
| 3, Category3 |
| 4, Category4 |
| 5, Category5 |
| 6, Category7 |
| 7, Category9 |
| item | category |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 1 | 4 |
| 1 | 5 |
| 2 | 1 |
| 2 | 2 |
| 2 | 3 |
| 2 | 9 |
| 2 | 10 |
Simply adding $em->flush() after $em->persist($categoryEntity) solves it, but I don't want to flush things just yet (or for that matter, flush only a category). There are a lot of unfinished stuff to do and I don't want to interrupt my transaction. I want to still be able to rollback to the very beginning and exclude all unused categories, if I need to (and, obviously, without running additional queries).
My question is: is there a way to access both the database and doctrine's internal entity mapping to retrieve an entity that might or might not have an ID? Or will I have to create this mapping myself, run a DQL and check on my mapping?
Doctrine2 can't do this for you,
but it's pretty easy to store the newly created categories in your loop and check them when you get a MISS from the database.
$_created_categories = array();
if (!$exists) {
// If there's already one on the database, we'll load it.
// Otherwise, we'll save a new Category..
$query = $this->_entityManager->createQueryBuilder();
$query->select('c')
->from("MyBundle:Category, 'c');
->where("c.name = :name")
->setParameter("name", $category);
$result = $query->getQuery()->getOneOrNullResult();
if ($result) {
$item->addCategory($result);
elseif ( isset($_created_categories[$category]) ) {
$item->addCategory($_created_categories[$category]);
} else {
$categoryEntity = new Category($category);
$em->persist($categoryEntity);
$item->addCategory($categoryEntity);
$_created_categories[$category] = $categoryEntity;
}
}
There is no memory overhead to store the new categories entities in the $_created_categories array as all objects are manipuled by reference in PHP.