this is a question that continues another topic :
QueryBuilder/Doctrine Select join groupby
Please open it so you can see there the database schema.
My problem is:
I have a goal and a goal has many savings.
That means that if you want to save money for a goal you will add some savings, so the savings are linked to the goal by id.
In that topic someone talked about a unidirectional link between savings and goals, ( I have goal_id in the savings table, but no saving_id in the goal table).
What I want to do is show a table with a Goal information and append to it a collected value(which is not in the database) this value is the sum of all saving values linked to a goal:
ex :
Goals:
id: 1
goal name: Goal1
....
Savings:
id: 1
amount:10
goal_id: 1
id:2
amount:20
goal_id: 1
When I query the database I will get a table with the goal information and a field Amount Collected: 30 in this case.
I initially managed to make the query to get me this:
select
admin_goals.created,
admin_goals.description,
admin_goals.goal_date,
admin_goals.value,
admin_goals.budget_categ,
sum(admin_savings.value)
from admin_goals
inner join admin_savings on admin_savings.goal_id=admin_goals.id
where admin_goals.user_id=1
group by admin_goals.id
But I wanted to do the symfony way so I used query builder, made a repository and didn't have the same result.
The only link between Goals and Savings is the goal_id from the Savings table, that means that using query builder I have to start from the Savings table.. because from the Goals table I have no foreign_key for savings.
But now the problem is that because I start from the Savings table, when displaying the Goals I get a empty result, thats because initially there are no Savings for a Goal.
First you create the Goal and then the Savings. But because there is no record in the Savings table(i start from) I don't get nothing from the Goals table because I have a join on these two tables.
$qb = $this->createQueryBuilder('admin_savings');
$qb->select('SUM(admin_savings.value) AS savingValue')
->addSelect('admin_goals.id AS id')
->addSelect('admin_goals.created')
->addSelect('admin_goals.description')
->addSelect('admin_goals.goal_date')
->addSelect('admin_goals.value')
->addSelect('admin_budget.name') // daca vrei id atunci pune identity(admin_goals.categ_id) admin_budget.name, x.name
->join('admin_savings.goal_id', 'admin_goals', Join::WITH)
->join('admin_goals.budget_categ', 'admin_budget', Join::WITH) //parametru 2 e numele de referinta in selectul mare ex: admin_budget, putea fi x
->where($qb->expr()->eq('admin_goals.user_id', ':user'))
->groupBy('admin_goals.id')
->setParameter('user', $user);
I understand that this is not possible so i tried another approach, why not I query normally the Goals entity because that is what I want to see..
$repository = $this->getDoctrine()->getRepository('AppBundle:Goal');
$goals = $repository->findBy(array('user_id' => $userId));
and I made a function in my SavingsRepository :
public function getSavingSumByGoal($goalId){
$qb = $this->createQueryBuilder('saving');
$qb->select('SUM(saving.value) AS savingValue')
->where($qb->expr()->eq('saving.goal_id', ':goal'))
->setParameter('goal', $goalId);
return $qb->getQuery()->getScalarResult();
}
So now I have the Goals entity with all its information, but not the Amount Sum Collected from the Savings, I need to add that somehow to my $goals object. This is in my controller
foreach($goals as $goal_item){
$savingSum=$SavingRepository->getSavingSumByGoal($goal_item->getId());
if($savingSum){
$goal_item->savingSum=$savingSum;
}else{
$goal_item->savingSum=0.00;
}
}
$goal_item->savingSum is a fictional property, he doesnt exists in the $goal object, I made this as an example that I want to insert that savingSum somehow in the $goal object.
I don't want to create another field in the entity and set that, because then I would have a empty collumn in the database, and I don't want that.. I want to keep the structure of my tables and entities, and only add that sum of all savings amount for a goal in the goals table.
But it doens't work.
I was thinking in the foreach in my twig to execute somehow my function, but I don't think that is a good idea.
{% for goal in goals %}
<tr style="">
<td>{{ goal.created|date('Y/m/d') }}</td>
<td>{{ goal.description|e }}</td>
<td>{{ goal.value|e }}</td>
<td>{{ goal.goal_date|date('Y/m/d') }}</td>
<td>{{ goal.name|e }}</td>
<td>{{ savingRepo->getSavingSumByGoal(goal.id)|e }} </td>
</td>
</tr>
{% endfor %}
This is a bad practice and it doesn`t work in twig, but I know people used approach in Zend and other frameworks (after you made the foreach() you run a function of your on the current objects(from foreach) id and show something based on that)
If you have better ideas on how to do this the symfony way .. I can always run the rawquery and it would work, but that is not that I want to do.. there must be a way to do this in a OOP way..please help.
Thank you!
I don't want to create another field in the entity and set that, because then I would have a empty collumn in the database
You don't have to map a field to database, it can be normal class field
Related
I have a cloud firestore with data structured this way:
baskets
basket 1
basketID
userID
description
products
productID
productID
basket 2
basketID
userID
description
products
productID
productID
...
basket N
products
product A
productID
description
productImg
price
product B
productID
description
productImg
price
...
product Z
This structure in order to avoid duplicating detailed product data inside each basket document.
I created a vue component to display at once both the basket data and the products data associated to the basket, while trying to limit and optimize firebase queries. I have sucessfully binded the basket data like this:
data() {
return {
basket: [],
basket_products: []
};
},
created() {
this.$bind('basket', db.collection("baskets").where("basketID", "==", this.basketID))
}
However I'm struggling to bind the basket_products data for the current basket. I'm looking for something like this:
this.$bind('basket_products', db.collection("products").where("productID", 'in', ONE OF THE PRODUCT_IDS WITHIN CURRENT BASKET, EQUIVALENT TO this.basket[0].products))
Glad my pointer helped!
Just for completeness sake - the best solution as mentioned is to simply store references, vuefire automatically picks up on those and saves them accordingly as part of the baskets thanks to the maxRefsDepth option.
Aside from that, you could potentially play around with the serialize option of vuefire to modify (and query additional) data when getting the baskets.
But this would be fairly complex, you might be better off just manually subscribing using the firebase SDK instead.
I have a table called Products, whose Key is a Range : orgzviceid + productid. It has a map attribute called "checkout" and a quantity storing attribute called "prod_stk_qty_i_i".
Say initially, for a product with Product ID 34, total available quantity is 10. As soon as a Cart checkout happens, assuming the Checkout ID is 5, and it has checked 2 quantities out a product id 34, then the product's (for productid 34) "checkout" map entry and "prod_stk_qty_i_i" in DynamoDB would be something like this:
"checkout" : { "5" : 2 },
"prod_stk_qty_i_i" : 8
If another checkout happens for the same product (say 1 quantity), and if that checkout ID is 7, then the checkout ooks like this:
"checkout" : { "5" : 2, "7" : 1 },
"prod_stk_qty_i_i" : 7
If payment is made, the checkout entry is removed, and quantity is increased.
Now, my requirement is to periodically after some timeout (30 minutes), release the Product Quantities which have been checked out, but not released. I do this by
Increasing the Quantity by "checkout."'s value
Removing the checkout. map entry
It is important that this operation not fail even if this operation is attempted multiple times, (idempotent), so its necessary that it only update if the checkout.checkoutID field exists. If not, it should simply ignore.
I tried the following:
[
"UpdateItem",
[
{
"TableName": "Products",
"Key": {
"orgzviceid": {
"N": "3000161710"
},
"productid": {
"N": "11"
}
},
"UpdateExpression": "REMOVE #checkout.#checkoutID SET #prod_stk_qty_i_i = #prod_stk_qty_i_i + #checkout.#checkoutID",
"ExpressionAttributeNames": {
"#checkout": "checkout",
"#checkoutID": "235",
"#prod_stk_qty_i_i": "prod_stk_qty_i_i"
},
"ConditionExpression": "attribute_exists(#checkout.#checkoutID)",
"ReturnValues": "ALL_NEW"
}
]
]
However, it gives me an error in case the checkout entry is not found for checkout id 235. Note that I've written ConditionExpression to do the update only if attribute "condition.235" exists.
Error Logs:
com.amazonaws.dynamodb.v20120810#ConditionalCheckFailedException","message":"The
conditional request failed ..."
So, how do I write a query such that if the map entry exist, then do the above operation, other wise not fail?
Obviously, one bad hack is to first check in a GetItem query if the checkout entry exists for the provided CheckoutID, and then only do this, however, it just does not seem right
I believe your using conditional expressions incorrectly. The point of the conditional is to fail if certain criteria is not met. WHy do you have the conditional at all? Without the conditional it would just execute the update expression and if the item does not exist I would not expect you to get an error. Like querying for an item that does not exist. You should simply get an empty set back. Not an error.
Your approach will not work because you are mixing "Attribute" and "AttributeValue" together in your conditional expression. Let me explain:
"ConditionExpression": "attribute_exists(#checkout.#checkoutID)"
In your table, checkout is an attribute in dynamo db, whereas checkoutID is in no way related to the table schema. So for dynamo DB, checkoutID is part of the attribute's value and not the attribute itself.
Therefore, to having the condition that you do will not work.
A conditional expression for your use case would be something which says attribute checkout exists and it's value is . However, in order to do that, you'd need to pass the expected map which boils down to reading the record before updating.
I do think that reading the record, updating the value and persisting it should be the way to go ahead in this case (and is not necessarily a bad idea)
Do consider using some kind of optimistic locking in this case to prevent against dirty reads and writes.
I'm currently creating an app using ionic2 and firebase for a class and I've run into an issue.
I"m not posting code because I don't have anything relevant enough to the question.
In my database I have a 'group' that people can create. I need the 'group' to be able to store a list of users. The way I'm currently doing it is that users will click an addGroup button, and the currentUser will be added to the list of users in that group simply by typing in the name of the group. (yeah i know that's going to need a password or something similar later, people obviously shouldn't just be able to join by group name)
*Names of groups will all be different so I should be able to query the database with that and get a single reference point.
The problem is that I don't know how to properly query the database and get the key value of the group object I want.
I keep looking things up and cant find a simple way to get the key of an object. I feel like this is a dumb question but, I'm unfamiliar with typescript, firebase, and ionic2 so I'm pretty lost in everything.
Answer for the question in comment.
you can query the database like this
this.group_val:any="group1"
let group=db.list('/groups', {
query: {
orderByChild: GroupName, //here specify the name of field in groups
equalTo: this.group_val //here specify the value u want to search
}
});
group.subscribe(grp=> {
//you can use the grup object here
})
First, define your database structure. The below is probably best:
groups
<first group> (Firebase-generated ID)
name: "Cat lovers"
users:
user1ID: true
user2ID: true
<second group>
...
I assume that when the user clicks on the group he wants to join, you know the group ID. For instance, it could be the value of an option in a select.
Get the list of groups:
groups$ = this.angularFireDatabase.list('groups');
To populate a list of groups into a select:
<select>
<option *ngFor="let group of groups$ | async" [value]="group.$key">
{{group.name}}
</option>
</select>
To create a new group:
groups$.push({name: "Dog lovers"})
The select (drop-down) will update itself.
To add a user to a group:
addUserToGroup(groupId, userId) {
this.angularFireDatabase.list(`groups/${groupID}/users`).update(userID, true);
}
To find a group based on its name:
// Define the shape of a group for better type checking.
interface Group { name: string; users: {[userId: string]: boolean;}; }
// Keep local list of groups.
groups$: FirebaseListObservable<Group>;
groups: Group[];
// Keep an updated local copy of groups.
ngOnInit() {
this.angularFireDatabase.list('groups').subscribe(groups => this.groups = groups);
}
function findGroupByName(name) {
return this.groups.find(group => group.name === name);
}
I'm writing a simple web app in web2py that stores and retrieves some data from a database. To keep the entries in chronological order, my table has an attribute which stores a date time. Like so:
db.define_table('tablename',
Field( ....
....
....
Field('created_on','datetime', default=request.now, writable=False),
Field('last_modified','datetime', default=request.now, update=request.now),
)
Then when I request the data, I set the orderby attribute to last_modified:
rows = db().select(db.tablename.ALL, orderby=db.tablename.last_modified)
Then, I pass the results onto a Json dictionary object. Like so:
I'm passing them into a JSON dictionary object.Like so:
d = { r.index_id : {'index_id':r.index_id,
....
....
'comments':r.comments,
'created_on':r.created_on,
'last_modified':r.last_modified}
for r in rows}
return response.json(dict(entry_dict=d))
When I get the response from the server, I return the dict to my ractive object.
MAIN.set('data_entries', data['entry_dict']);
Then, I render the results in ractive:
{% #data_entries:index_id%}
<tr class = "datarow" onclick="window.document.location='{% url+ '/'+ index_id %}';">
<td>{% index_id %}</td>
....
<td>{% last_modified %}</td>
</tr>
{% /data_entries %}
However, when the data is returned to the webapp, I get to following:
Am I doing this right?
In Python, dictionaries do not preserve order, so when you convert the d dictionary to JSON, you will not necessarily get the keys in the order of the original Rows object. You could instead use an OrderedDict, but it doesn't appear you really need a dictionary anyway -- just create a list (using the as_list method):
return response.json(dict(entry_dict=rows.as_list()))
Then in Ractive, change:
{% #data_entries:index_id %}
to:
{% #data_entries %}
It doesn't appear you need the index value within each loop. Instead, you will be able to access the index_id field of each record as part of the data context of the section.
All,
I have an entity, that has several collections,- each collection is mapped lazily. When I run a criteria query, I get duplicate results for my root entity in the result set. How's that possible when all my collections are mapped lazily!
I verified, my collections, load lazily.
Here's my mapping:
Root entity 'Project':
[Bag(0, Lazy = CollectionLazy.True, Inverse = true, Cascade = "all-delete-orphan")]
[Key(1, Column = "job_id")]
[OneToMany(2, ClassType = typeof(ProjectPlan))]
public virtual IList<ProjectPlan> PlanList
{
get { return _planList; }
set { _planList = value; }
}
The criteria query is:
ICriteria criteria = session.Session.CreateCriteria<Entities.Project>()
.Add(Restrictions.Eq(Entities.Project.PROP_STATUS, !Entities.Project.STATUS_DELETED_FLAG));
.CreateAlias(Entities.Project.PROP_PLANLIST, "p")
.Add(Restrictions.Eq("p.County", 'MIDDLSEX'))
.setFirstResult(start).setMaxResults(pageSize)
.List<Entities.Project>();
I know, I can correct this problem w/ Distinct result transformer, I just want to know if this is normal behavior on lazy collections.
EDIT: I found the cause of this,- when looking at the raw SQL, the join, and where clause are correct but what baffles me is the generated Select clause,- it not only contains columns from the project entity (root entity) but also columns from the project plans entity which causes the issue I described above. I am not at work right now, but I'll try to do this: .SetProjection(Projections.RootEntity()), so I only get Project's columns in the select clause.
One way, how to solve this (I'd say so usual scenario) is: 1) not use fetching collections inside of the query and 2) use batch fetching, as a part of the mapping
So, we will always be querying the root entity. That will give us a flat result set, which can be correctly used for paging.
To get the collection data for each recieved row, and to avoid 1 + N issue (goign for collection of each record) we will use 19.1.5. Using batch fetching
The mapping would be like this
[Bag(0, Lazy = CollectionLazy.True
, Inverse = true
, Cascade = "all-delete-orphan"
, BatchSize = 25)] // Or something similar to batch-size="25"
[Key(1, Column = "job_id")]
[OneToMany(2, ClassType = typeof(ProjectPlan))]
public virtual IList<ProjectPlan> PlanList
{
...
Some other similar QA (with the almost same details)
How to Eager Load Associations without duplication in NHibernate?
NHibernate QueryOver with Fetch resulting multiple sql queries and db hits
Is this the right way to eager load child collections in NHibernate
And we still can filter over the collection items! but we have to use subqueries, an example Query on HasMany reference