How we can insert unique record to our types with ElasticSearch, for example 3 user updated the same record and we should store 3 record in our index with 3 updated_by and updated_at field. I'm using Elasticquent and now for example when i want to update a record from course table after that i do the bellow:
for example user with id 1 update the record.
$course = new Course();
......
.....
$course->save();
$course->updated_at = '2016-01-06'
$course->updated_by = '1';
$course->addToIndex();
and then user with id 2 update the same record
but inside index the document that created in first user update action is rewrite by the second user action.
I dont understand why you need elasticsearch for this purpose. You can easily maintain a course_history table : id, course_id, updated_by, updated_at. Then to find any history of course editions, you can simply make a join. If you need document store for some performance benefits, I suggest choose some different one as elasticsearch is better for search and aggregations. It is not made for a document store. However, if this is really your requirement, then I would suggest maintaining a history table and then use associations with elasticsearch to make it searchable.
In such case it is better to store multiple documents. What you are trying to do cannot be done in a single document easily. An easier, and IMO better, solution is to store multiple documents. So with every update you sore new document. Recorc ID will be one of the fields you can search for afterwards.
[
{
"_index": "logs",
"_type": "log_record",
"record_id": 1,
"updated_by": 100,
"updated_at": "2016-01-01T00:00:00.000Z"
},
{
"_index": "logs",
"_type": "log_record",
"record_id": 1,
"updated_by": 200,
"updated_at": "2016-01-01T01:00:00.000Z"
}
]
Now you can easily search for what you need and deleted specific records if necessary.
You don't really care about the decument ID here. You can let ES generate it for you. All you need is to store the correct record ID inside document.
Edit:
Here is how it may look in Elasticquent (please notice I don't know details of Elasticquent).
Lets say you have a class like CourseChageLog with the following mapping:
class CourseChageLog extends Eloquent
{
use ElasticquentTrait;
public $record_id;
public $updated_by;
public $updated_at;
protected $mappingProperties = array(
'record_id' => array(
'type' => 'integer',
'index' => 'not_analyzed'
),
'updated_by' => array(
'type' => 'integer',
'index' => 'not_analyzed'
),
'updated_at' => array(
'type' => 'date',
'index' => 'not_analyzed'
),
);
}
CourseChageLog::putMapping();
The point is you need to ask the correct question. You don't ask how to store something. You ask what question you want to ask Elasticsearch and then you create an appropriate document form it. Don't create documents and then wonder if they can answer your question.
Related
I have a User model which has a hasMany relationship to a Brands model and I am having issues updating the Brands for a user properly.
I have a form which allows a user to enter / delete / update their own Brands in text fields, the current method i am using to update the users Brands after they have entered them all or edited them is to delete all existing Brands associated with the User then loop over the values, create the Brand model and then 'saveMany' ... But i seem to be getting a constraint violation when adding ... I am wondering if there is a better way to do this;
My User model has the following;
public function brands()
{
return $this->hasMany('Brands::class');
}
Then in my controller I have the following code to update the Brands;
$user->brands()->delete();
foreach ($request['brands'] as $brand) {
$brandArray[] = new Brand([
'name' => $brand['name'],
'rating' => $brand['rating'],
]);
}
!empty($brandArray) && $user->brands()->saveMany($brandArray);
Is there a better way of doing this?
Let's separate things into three parts:
# constraint key violation:
If you've added foreign key constraint on another table, and you need to delete the brands, you should also delete all those related data constrained by your foreign key.
# design
If deleting brand related data is not possible, then maybe we can think about if there is a better design. Maybe we could add a hook on the frontend that call a DELETE API whenever certain data is removed by the user.
# query
If the brand has some unique key, you could use upsert instead of saveMany. That will be more efficient.
# conclusion
I would suggest deleting brands by hooks on the frontend whenever a brand is removed by users, and use upsert to deal with create and update stuff
It looks fine to me. But from my point of view,
Instead of deleting all fields, then creating them. You can use updateOrCreate eloquent method inside your foreach.
And in place of foreach, you can use the map method.
Since you only want to delete all the previous brands of the user, and create brand new brands without editing anything, you can simply follow the concept below:
In your controller:
// Step 1) load all the user brands
$user->load('brands');
// Step 2) delete all the brands with this user
$user->brands()->delete();
// Step 3) create all the new brands.
$user->brands()->createMany($request['brands']);
/* Concept of how createMany works:
The brand array should look similar to this, you can do dd($request['brands']) to verify
$user->brands()->createMany([
['name' => 'A new name.', 'rating' => '1'],
['name' => 'Another new name.', 'rating' => '2'],
]);
*/
You can find more examples in laravel documentation on createMany method: https://laravel.com/docs/9.x/eloquent-relationships#the-create-method
You can also go a step further and validate the array from the request:
$data = request()->validate([
'brands.*' => 'required',
]);
$user->brands()->createMany($data['brands']);
Hope this helps, good luck!
When you save everything you have for the user, do this under:
foreach ($request['brands'] as $brand) {
Brand::updateOrCreate(
[
'name' => $brand['name'],
'rating' => $brand['rating'],
],
[
'name' => $brand['name'],
'rating' => $brand['rating'],
]
);
}
I have been looking for a solution that would replicate MySQL's INSERT ... ON DUPLICATE KEY UPDATE in my Codeigniter model, which led me to the save() function.
I get the logic of it, and my script works fine... but for UPDATING only.
What I've realised now is that my data array is formed from info pulled from an external datafeed, and includes both new AND existing products. There will ALWAYS be a sku (primary key) included in for every product, therefore the save function will ALWAYS look to update the corresponding row in my database - but never insert it - hence, no new objects are ever added to my database - only updated.
// Defined as a model property
$primaryKey = 'sku';
// Does an insert()
$data = [
'brand' => 'Heinz',
'name' => 'Baked Beans'
];
$userModel->save($data);
// Performs an update, since the primary key, 'sku', is found.
$data = [
'sku' => xrt567ycr,
'brand' => 'Heinz',
'title' => 'Baked Beans'
];
$userModel->save($data);
What is the easiest way to get around this and have the script either insert or update based on whether the sku is actually present in the database already or not? All help greatly appreciated!
after I did some research, I didn't find any proper answer to this question.
I'm starting writing tests for my CakePHP (3.x) app and I was wondering how can we add fixtures and their associations?. In other words, how can we link a fixture to another without writing the Primary and foreign keys directly inside the fixture.
i.e
I have a Users table and a UserProfiles table.
User hasOne UserProfile through user_id column. Here's my user fixture.
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UsersFixture extends TestFixture
{
public $import = ['table' => 'users'];
public $records = [
[
'username' => 'mark#mail.com',
'primary_role' => 1
'is_active' => 1,
]
];
}
And here's my UserProfile fixture
namespace App\Test\Fixture;
use Cake\TestSuite\Fixture\TestFixture;
class UserProfilesFixture extends TestFixture
{
public $import = ['table' => 'user_profiles'];
public $records = [
[
'first_name' => 'Mark',
'last_name' => 'Yellowknife',
]
];
}
How do I link both record together? Aka, how can I tell the user profile record to add its user_id to be linked to the user record?
Must be a way to do this with Fixtures without writing the keys manually.
Thanks guys!
You have to define this in the fixtures. The only other way would be to issue update queries at runtime to set the foreign keys, but that's anything but a good idea.
Other than that there isn't really a way to do this, I mean, how would any code know which records needs to be associated with each other without you explicitly defining it?
So, you'll have to at least define the foreign key, and depending on how primary keys are being generated (auto incrementing integers, UUIDs, etc), you might have to define them too.
I am using a mongodb-codeigniter library by Alex Bilbie.
I have a collection called "user_visits". I insert into collection like below.
$query = $this->mongo_db->insert('user_restaurant_visits',[
'_id' => 1,
'user_id' => $user_id,
'pages_visited' => [
'page_id' => $restaurant_id
'visited' => [
'deal' => $purchase_deal,
'ordered' => $purchased_item
]
]
]);
Now all I would want to know is,
I would like to add the documents by "upsert=true" boolean flag as specified in here which will insert if the field is not present and update if the field is present. And I could not find a way to do so in the library I use! Am I misguided?
Is this a good way? is there anything wrong in the way I have organized the fields (I mean Schema as we say in RDBMS). I specifically ask this because, some feel nested arrays are better than embedded documents. like philnate says here in his answer and comments
If I'd want to upsert, increment a field, and addToSet, in the same query, is this possible with the library I currently use?
Let me know if I miss something, I can clarify in comments. I am totally new to NoSQL DBs.
I am sorry if that looked amaeturish.
Answer of one of your question :
How to Use "Upsert" in CodeIgniter while updating :
// Where Condition, if any
$this->mongo_db->where(array('condition_key' => 'condition_value'));
// Update Data Array
$this->mongo_db->set($mongoArray);
// Set Options
$option = array('upsert' => true);
// Call Update Function
$this->mongo_db->update('Collection_Name', $option);
I hope this will help you :)
OK, I am a little bit lost...
I am pretty new to PHP, and I am trying to use CakePHP for my web-site.
My DB is composed of two tables:
users with user_id, name columns
copies with copy_id, copy_name, user_id (as foreign key to users) columns.
and I have the matching CakePHP elements:
User and Copy as a model
UserController as controller
I don't use a view since I just send the json from the controller.
I have added hasMany relation between the user model and the copy model see below.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Without the association every find() query on the users table works well, but after adding the hasMany to the model, the same find() queries on the users stop working (print_r doesn't show anything), and every find() query I am applying on the Copy model
$copy = $this->User->Copy->find('all', array(
'condition' => array('Copy.user_id' => '2')
));
ignores the condition part and just return the whole data base.
How can I debug the code execution? When I add debug($var) nothing happens.
I'm not an expert, but you can start with the following tips:
Try to follow the CakePHP database naming conventions. You don't have to, but it's so much easier to let the automagic happen... Change the primary keys in your tabel to 'id', e.g. users.user_is --> users.id, copies.copy_id -->copies.id.
Define a view, just for the sake of debugging. Pass whatever info from model to view with $this->set('users', $users); and display that in a <pre></pre> block
If this is your first php and/or CakePHP attempt, make sure you do at least the blog tutorial
Make CakePHP generate (bake) a working set of model/view/controllers for users and copies and examine the resulting code
There's good documentation about find: the multifunctional workhorseof all model data-retrieval functions
I think the main problem is this:
'condition' => array('Copy.user_id' => '2')
It should be "conditions".
Also, stick to the naming conventions. Thankfully Cake lets you override pretty much all its assumed names, but it's easier to just do what they expect by default.
The primary keys should be all named id
The controller should be pluralised: UsersController
First off, try as much as possible to follow CakePHP convention.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Your association name is 'Copy' which is a different table and model then on your classname, you have 'Friendship'.
Why not
var $hasMany = array(
'Copy' => array('className'=>'Copy')
);
or
var $hasMany = array(
'Friendship' => array('className'=>'Friendship')
);
or
var $hasMany = array(
'Copy' => array('className'=>'Copy'),
'Friendship' => array('className'=>'Friendship')
);
Also, check typo errors like conditions instead of condition
Your table name might be the problem too. I had a table named "Class" and that gave cake fits. I changed it to something like Myclass and it worked. Class was a reserved word and Copy might be one too.