I have a versionable table in my schema. I want it not to be versioned when i change status for example, but versioned when i change shippingPrice.
MyOrder:
actAs:
Timestampable:
Versionable:
versionColumn: version
className: %CLASS%Version
auditLog: true
columns:
userId: { type: integer, notnull: true }
status: {type: enum, values: ["status1", "status2"]}
shippingPrice: { type: float, notnull: true }
#more columns
How can i do that ?
This article might be of some interest:
http://blog.solutionset.com/wpmu/2009/08/26/doctrine-with-nested-i18n-versionable/
The author wants to nest I18N functionality with Versionable. The problem is slightly different; you can't even build the database when you nest I18N and Versionable, but the solution he came up with might be useful.
In essence, he rolled his own Versionable. Not the most pleasant prospect, but looking at how he implemented it might save you some time on your own spike.
This is a bit hacky but should do what is required I think.
I haven't tested it but my understanding is that if you do a dql update() Versionable is not triggered.
Like this
$result = $this->createQuery()
->update('MyOrder m')
->set('m.status', $var)
->execute();
But if you retrieve the object, change it and save thus:
$m = MyOrderTable::getInstance()->findOneById($id);
$m->setShippingPrice($price);
$m->save();
Then Versionable will be triggered so you should get a new version.
So you could use this to solve the problem.
Related
New to ElasticSearch and I was wondering if there is a way to construct conditional queries/filters. I am working with Rails, so I suppose it has to be on that particular level, as I couldn't find anything that points to conditional queries at ES-Level and I am pretty sure it was silly just to assume!
So here is the (working) query I have:
search_definition = {
query: {
bool: {
must: [
{
more_like_this: {
fields: tag_types,
docs: [
{
_index: self.class.index_name,
_type: self.class.document_type,
_id: id
}
],
min_term_freq: 1
}
}
],
should: [
range: {
age: {
gte: min_age,
lte: max_age,
boost: 4.0
}
}
],
filter: {
bool: {
must: [
term: {
active: true
}
],
must: [
geo_distance: {
distance: xdistance,
unit: "km",
location: {
lat: xlat,
lon: xlng
},
boost: 5.0
}
]
}
}
}
},
size: how_many
}
And it works perfectly fine. Now let's assume I'd like to apply additional filters, in this particular example I need to verify when the user who is searching, that the users on the other end are, in fact, looking for a person of gender for whoever is searching. This is held in 2 separate boolean attributes in the database (male/female). I thought it would be simple enough to prepare two similar filters - however, there are a few more conditional filters that run into the queries, and I would eventually end up with more than ten pre-prepared filters. There must be a more elegant way! Thank you!
Are you familiar with elasticsearch search templates?
Using search templates you can have conditional and dynamic queries. for example you can have a list of fields and values to do terms filter and pass it to search template as a parameter.
As suggested by Mohammad - in the end, I pursued a solution using ES search templates which made my life a lot easier. The problem with JBuilder, ElasticSearch-DSL and other solutions is that they appear not to be working with the latest ES, and subsequently, I am not sure where I end up should there me ever any changes to gems or version of ES. So cutting the middle man out and taking full control with templates that are in fact super easy to create made a lot of sense to me. The versions I set up with JBuilder and ES-DSL never worked correctly as their output was random at best.
Search Templates -> More Information
JBuilder -> More Information
ElasticSearch-DSL -> More Information
There are other solutions that I haven't tried, but with search templates, I didn't see any need for that.
It has taken me quite a long (calendar) time to get my head around CouchDB and map/reduce and how I can utilize it for various use cases. One challenge I've put myself to understanding is how to use it for normalized data effectively. Sources all over the internet simply stop with "don't use it for normalized data.". I do not like the lack of analysis on how to use it effectively with normalized data!
Some of the better resources I've found are below:
CouchDB: Single document vs "joining" documents together
http://www.cmlenz.net/archives/2007/10/couchdb-joins
In both cases, the authors do a great job at explaining how to do a "join" when it is necessary to join documents when there is denormalized commonality across them. If, however, I need to join more than two normalized "tables" the view collation tricks leveraged to query just one row of data together do not work. That is, it seems you need some sort of data about all elements in the join to exist in all documents that would participate in the join, and thus, your data is not normalized!
Consider the following simple Q&A example (question/answer/answer comment):
{ id: "Q1", type: "question", question: "How do I...?" }
{ id: "A1", type: "answer", answer: "Simple... You just..." }
{ id: "C1", type: "answer-comment", comment: "Great... But what about...?" }
{ id: "C2", type: "answer-comment", comment: "Great... But what about...?" }
{ id: "QA1", type: "question-answer-relationship", q_id:"Q1", a_id:"A1" }
{ id: "AC1", type: "answer-comment-relationship", a_id:"A1", c_id:"C1" }
{ id: "AC2", type: "answer-comment-relationship", a_id:"A1", c_id:"C2" }
{ id: "Q2", type: "question", question: "What is the fastest...?" }
{ id: "A2", type: "answer", answer: "Do it this way..." }
{ id: "C3", type: "answer-comment", comment: "Works great! Thanks!" }
{ id: "QA2", type: "question-answer-relationship", q_id:"Q2", a_id:"A2" }
{ id: "AC3", type: "answer-comment-relationship", a_id:"A2", c_id:"C3" }
I want to get one question, its answer, and all of its answer's comments, and no other records from the databse with only one query.
With the data set above, at a high level, you'd need to have views for each record type, ask for a particular question with an id in mind, then in another view, use the question id to look up relationships specified by the question-answer-relationship type, then in another view look up the answer by the id obtained by the question-answer-relationship type, and so on and so forth, aggregating the "row" over a series of requests.
Another option might be to create some sort of application that does process above to cache denormalized documents in the desired format that automatically react to the normalized data being updated. This feels awkward and like a reimplementation of something that already exists/should exist.
After all of this background, the ultimate question is: Is there a better way to do this so the database, rather than the application, does the work?
Thanks in advance for anyone sharing their experience!
The document model you have is what I would do if I'm using traditional relational database, since you can perform joins more naturally with those ids.
For a document database however, this will introduce complexity since 'joining' document with MapReduce isn't the same thing.
In the Q&A scenario you presented, I would model it as follow:
{
id: "Q1",
type: "question",
question: "How do I...?"
answers: [
{
answer: "Simple... You just...",
comments: [
{ comment: "Great... But what about...?" },
{ comment: "Great... But what about...?" }
]
},
{
answer: "Do it this way...",
comments: [
{ comment "Works great! Thanks!" },
{ comment "Nope, it doen't work" }
]
}
]
}
This can solve a-lot of issues with read from the db, but it does make your write more complex, for example when adding a new comment to an answer, you will need to
Get the document out from CouchDB.
Loop through the answer and find the correct position, and push comment into the array.
Save document back to CouchDB.
I'd only consider to spit the answer as a separate document if there's a-lot of them (e.g. 1 question yield 1000 answers'), otherwise it's easier to just package them in a single document. But even in that case, try putting the relationship info inside the document, e.g.
{
id: "Q1",
type: "question",
question: "How do I...?"
}
{
id: "A1",
type: "answer",
answer: "Simple... You just..."
question_id: "Q1"
}
{
id: "C1",
type: "comment",
comment: "Works great! Thanks!"
answer_id: "A1"
}
This can make you'r write operation easier but you will need to create view to join the documents so it returns all documents with one request.
And always keep in mind that the return result from a view is not necessary a flat structure like rows like in sql query.
I've got two little questions:
actAs: { Timestampable: ~ }
What the "~" what mean in the code above?
Then, I've seen that tables with actAs: { Timestampable: ~ } have two fields (created_at and updated_at).
Is it possible to bind the updated_at field to a particular field (I update this field, then updated_at get a new value) ?
The "~" means that you will use default values or default configuration. In your case, the behavior Timestampable, will use the default value and configuration. So you don't have to redefine them.
From the doc, here are some configuration:
Timestampable:
created:
name: created_at
type: timestamp
format: Y-m-d H:i:s
updated:
disabled: true
You will also find this "~" (a lot) in the default generator.yml. This way, the generator, even empty, will generate a nice admin:
config:
actions: ~
fields: ~
list: ~
filter: ~
form: ~
edit: ~
new: ~
For your second question, the goal of the Timestampable is for each modification on a row, the field updated_at will be set with the current date. So you don't need to take care of it.
Edit:
And if you want to manually update the updated_at field:
first: you will have to disable the timestampable behavior for this field (see the example above
second: you will have to do the behavior on your own.
The easiest way is to extends the preSave function of your model and do the job here. Like:
class Article extends BaseArticle
{
public function preSave($event)
{
if(array_key_exists("your_field", $this->getModified())
{
$this->setUpdatedAt(time());
}
}
I have a many to many relationship between products and colours.
What I am trying to do is find products by their colours.
eg)
$colours = $em->getRepository('Xxxxx\XxxxxBundle\Entity\Colour')->findBy(array('name'=>'red');
$products = $em->getRepository('Xxxxx\XxxxxBundle\Entity\Product')->findBy(array('colours'=>$colours));
This is my Yaml config:
Xxxxx\XxxxxBundle\Entity\Product:
type: entity
manyToMany:
colours:
targetEntity: Colour
joinTable:
name: Product_Colour
joinColumns:
product_id:
referencedColumnName: id
inverseJoinColumns:
colour_id:
referencedColumnName: id
.
Xxxxx\XxxxxBundle\Entity\Colour:
type: entity
id:
id:
type: integer
generator:
strategy: AUTO
fields:
hex:
type: string
length: 320
name:
type: string
length: 320
The error message I am getting is:
Notice: Undefined index: joinColumns in /home/xxx/public_html/products/vendor/doctrine/lib/Doctrine/ORM/Persisters/BasicEntityPersister.php line 1217
Would someone be able to shine some light on why this is not working.
I know this is an old question, but if anyone else arrives here via Google (like I did), I had to eschew the findBy and use DQL in the repository:
$products = $em->getRepository('Vendor\Bundle\Entity\Product')->findByColours($colours);
And in the repository:
public function findByColours($colours)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb ->select(array('p'))
->from('VendorBundle:Product', 'p')
->join('p.colours', 'c', 'WITH', $qb->expr()->in('c.id', $colours));
$result = $qb->getQuery()->execute();
return $result;
}
You may need to change the join based on what $colours is. This is assuming it's an array of colour IDs. If it's a string you can forgo the in() or if it's an array of strings you'll need to bind the strings as parameters (see the following link). Clarification on expr() and such is in the Doctrine docs
I don't know why Undefined index: joinColumns occurs, but this is a method to side-step it altogether. Hopefully someone can clarify as to the error, as my solution adds extra work to the Many to Many relationship.
I'm repeatedly getting confused between Language & Culture in Symfony.
- I'm setting culture as (en_US) which I guess is correct
- but translation files are created for a language (en)
- also, when using translation in the database, the lang=en
I have a model whose definition is as follows:
Option:
package: Options
tableName: Com_Options
actAs:
I18n:
fields: [name, tooltip]
columns:
id:
type: integer(2)
primary: true
notnull: true
unsigned: true
# autoincrement: true
name:
type: string(50)
notnull: true
notblank: true
tooltip:
type: string(100)
sequence:
type: integer(1)
unsigned: true
range: [0,255]
This class is referred by several other classes.
When I try to print the name as $refObj->Option->Name I get nothing. When I check the queries, I see that 3 queries are fired in all.
1) to get refObj
2) to get Option
3) to get Translation
But the real problem is that for 3, there is no language parameter in the query.
I wonder how it will get the translated name? Current value of sf_culture: en_US
Is there another way to access the 'name' according to user's language?
Or do I need to write custom queries on every model?
Is the problem because autoincrement is OFF, or because Im using a package?
Thanks a lot!
Found this: a very tedious & costly method, but works:
$class->relation->Translation[$lang]->property
If you wish to read directly from database
$q = Table::getInstance()->createQuery('a')
->select('a.id, t.name')
->leftJoin('a.Translation t')
->where('t.lang = ?', $lang);
return $q->execute(array(), Doctrine::HYDRATE_NONE);
If you use XLIFF files, you need not pass culture/language
__('text which is translated in the XLIFF for user\'s culture');