Assuming I have a music app (like the example) using GetStream.io feeds, where I have user feeds and band feeds (user being able to follow other users, or bands).
I am using the stram-ruby gem.
Now, say I am posting as a user to a band feed, doing something like:
user_feed = #client.feed('user', user_id)
activity_data = {
:actor => "User:#{user_id}",
:verb => 'post',
:object => "Post:#{post.id}",
:target => "Band:#{band_id}",
:foreign_id => "Post:#{post.id}",
:time => post.created_at.as_json,
:comment => 'comment 1',
:to => ["band:#{band_id}"]
}
user_feed.add_activity(activity_data)
This works fine, and retrieving the band feed, I can see that post:
#client.feed('band', band_id).get()
Now, I am attempting to update this entry (using the comment field just to see something changes):
activity_data = {
:actor => "User:#{user_id}",
:verb => 'post',
:object => "Post:#{post.id}",
:target => "Band:#{band_id}",
:foreign_id => "Post:#{post.id}",
:time => post.created_at.as_json,
:comment => 'comment 2',
:to => ["band:#{band_id}"]
}
#client.update_activity(activity_data)
getting the band feed will show correctly the new comment ('comment 2'), but my problem is this:
I am using the created_at field of my post (to ensure my ability to update it as GetStream docs say this is part of the unique key)
I use the same created_at time on the update
When fetching the band feed, I would expect this activity to be the top one - as the feed should be sorted chronologically. But it remains where it was before based on the created_at time.
What to do?
I can try using the updated_at field of the post, but then, if for any reason the post in my DB changes without me updating the GetStream feed, I will no longer be able to update it in GetStream.
Am I missing anything?
There are 2 ways in which you can achieve this.
1.) You could use aggregated feeds. They are sorted based on the last update to the aggregated item. (assuming this fits your use case of course)
2.) You could send a field to Stream called updated_at. Next you enable ranked feeds and simply tell Stream to sort by updated_at instead of the regular chronological sort.
In general, creating a ranking method gives you full control over the ranking of your feed. (paid plans only though)
https://getstream.io/docs/#custom_ranking
Related
I have a Ruby script using the Sequel gem to access my Postgres database.
At the moment, here are my calls:
#db[:items].insert_conflict.insert(:sku => sku, :name => itemName)
dbItem = Item.where(:sku => sku).first
dbPrice = Price.create(:price => foundPrice, :quantity => quantity, :status => status)
dbItem.add_price(dbPrice)
store.add_price(dbPrice)
Unfortunately, insert_conflict.insert returns either the key if it is inserted, and nil if it exists.. otherwise I'd be able to use the one call (to get the actual object that was inserted or already existed, see line 2). Is there any other way to reduce this to one call?
As for the last three calls, I believe since I'm adding relationships between three different schemas, there's no way to reduce this. But I'm including it just in case.
follow me on this one...
if i've got a db of movies and i want to search on multiple fields and return the results into a single field, how would i accomplish this?
let me set an example...
my documents have a title and artists.name (array). i want the user to be able to search in both title and artist at the same time so that the results are in the same field. this would be implemented in an 'autocomplete' search scenario where you get results as you type.
so if a user types 'mike' i want to be able to search for actors (artists.name) with the name mike and titles with the word mike in it. in this case, you might return 'magic mike' and 'mike meyers' in the same autocomplete result set. (imdb.com has this implementation)
i understand how to search both of those fields, but how do i return them into one? i believe i'd have to have some knowledge on where my 'hit' came from - title or artists.name. so maybe that's the larger question here - how do i tell which field the hit came from?
I don't think there are any direct ways to determine which field(s) a query matched on. I can think of a few "workaround" approaches that may do it for you- one is by using the multisearch api, and executing separate queries on each field. Another is using highlighting, which will return back the fields that a match was found in.
Example using multi search:
var response = client.MultiSearch(ms => ms
.Search<Artist>("name", s => s.Query(q => q.Match(m => m.OnField(a => a.Name).Query("mike"))))
.Search<Artist>("titles", s => s.Query(q => q.Match(m => m.OnField(a => a.Titles).Query("mike")))));
response.GetResponse<Artist>("name"); // <-- Contains search results from matching on Name
response.GetResponse<Artist>("titles"); // <-- Contains search results from matching on Titles
Example using highlighting:
var response = client.Search<Artist>(s => s
.Query(q => q
.MultiMatch(m => m
.OnFields(a => a.Name, a => a.Titles)
.Query("mike")))
.Highlight(h => h
.OnFields(fs => fs.OnField(a => a.Name),
fs => fs.OnField(a => a.Titles))));
You can then inspect the Highlights object of each hit, or the Highlights object of the response to determine what field the match came from.
There is also the explain api, and you can add explain to your query, but that will return a lot of irrelevant scoring info, which you would have to parse through. Probably too cumbersome for your needs.
As a side note- for autocomplete functionality, if possible I would really try to leverage the completion suggester instead of the above solutions. These are pre-computed suggestions that are created when you index your documents by building up FSTs, which will increase your indexing time as well as index size, but as a result will provide extremely fast suggestions.
Let me preface by saying I'm new to Magento as well as Data Collections in general (only recently begun working with OOP/frameworks).
I've followed the excellent tutorial here and I'm familiar with Alan Storm's overviews on the subject. My aim is to create a custom Magento report which, given a start/end date, will return the following totals:
Taxable Net (SUM subtotal for orders with tax)
Non-Taxable Net (SUM subtotal for orders without tax)
*Total Gross Sales (Grand total)
*Total Net Sales (Grand subtotal)
*Total Shipping
*Total Tax
*For these figures, I realize they are available in existing separate reports or can be manually calculated from them, however the purpose of this report is to give our store owner a single page to visit and file to export to send to his accountant for tax purposes.
I have the basic report structure already in place in Adminhtml including the date range, and I'm confident I can include additional filters if needed for order status/etc. Now I just need to pull the correct Data collection and figure out how to retrieve the relevant data.
My trouble is I can't make heads or tails of how the orders data is stored, what Joins are necessary (if any), how to manipulate the data once I have it, or how they interface with the Grid I've set up. The existing tutorials on the subject that I've found are all specifically dealing with product reports, as opposed to the aggregate sales data I need.
Many thanks in advance if anyone can point me in the right direction to a resource that can help me understand how to work with Magento sales data, or offer any other insight.
I have been working on something extremely similar and I used that tutorial as my base.
Expanding Orders Join Inner
Most of the order information you need is located in sales_flat_order with relates to $this->getTable('sales/order')
This actually already exists in her code but the array is empty so you need to populate it with the fields you want, here for example is mine:
->joinInner(
array('order' => $this->getTable('sales/order')),
implode(' AND ', $orderJoinCondition),
array(
'order_id' => 'order.entity_id',
'store_id' => 'order.store_id',
'currency_code' => 'order.order_currency_code',
'state' => 'order.state',
'status' => 'order.status',
'shipping_amount' => 'order.shipping_amount',
'shipping_tax_amount' => 'order.shipping_tax_amount',
'shipping_incl_tax' => 'base_shipping_incl_tax',
'subtotal' => 'order.subtotal',
'subtotal_incl_tax' => 'order.subtotal_incl_tax',
'total_item_count' => 'order.total_item_count',
'created_at' => 'order.created_at',
'updated_at' => 'order.updated_at'
))
To find the fields just desc sales_flat_order in mysql.
Adding additional Join Left
Ok so if you want information from other tables you need to add an ->joinLeft() for example I needed the shipment tracking number:
Create the Join condition:
$shipmentJoinCondition = array(
$orderTableAliasName . '.entity_id = shipment.order_id'
);
Perform the join left:
->joinLeft(
array('shipment' => $this->getTable('sales/shipment_track')),
implode(' AND ', $shipmentJoinCondition),
array(
'track_number' => 'shipment.track_number'
)
)
Sorry I couldn't go into more depth just dropping the snippet for you here.
Performing Calculations
To modify the data returned to the grid you have to change addItem(Varien_Object $item) in your model, basically whatever is returned from here get put in the grid, and well I am not 100% sure how it works and it seems a bit magical to me.
Ok first things first $item is an object, whatever you do to this object will stay with the object (sorry terrible explanation): Example, I wanted to return each order on a separate line and for each have (1/3, 2/3, 3/3), any changes I made would happen globally to the order object so they would all show (3/3). So keep this in mind, if funky stuff starts happening use PHP Clone.
$item_array = clone $item;
So now onto your logic, you can add any key you want to the array and it will be accessible in Grid.php
For example(bad since subtotal_incl_tax exists) :
$item_array['my_taxable_net_calc'] = $item['sub_total'] + $item['tax'];
Then at the end do:
$this->_items[] = $item_array;
return $this->_items;
You can also add more rows based on the existing by just adding more data to $this->_items[];
$this->_items[] = $item_array;
$this->_items[] = $item_array;
return $this->_items;
Would return same item on two lines.
Sorry I have started to lose the plot, if something doesn't make sense just ask, hope this helped.
Oh and to add to Block/Adminhtml/namespace/Grid.php
$this->addColumn('my_taxable_net_calc', array(
'header' => Mage::helper('report')->__('Taxable Net'),
'sortable' => false,
'filter' => false,
'index' => 'my_taxable_net_calc'
));
I'm using the following with datamapper to create/get a new user from my db:
user = User.first_or_create({:id => data['id']})
This gets the user with id = data['id'] or creates it if it doesn't already exist.
I want to know how to set other attributes/fields of the returned object regardless of whether it is a new record or existing?
Is the only way to do this to then call user.update({:field => value ...}) or is there a better way to achieve this?
Well, you could write it as one line:
(User.first_or_create(:id => data['id'])).update(:field => value)
with hashes for the parameters if you wish (or if you need to specify more than one); however, it's worth noting that this will only work if the model as specified by the first_or_create is valid. If :name were a required field, for instance, then this wouldn't work:
(User.first_or_create({:id => data['id'], :name => "Morse"})).update(:name => "Lewis")
as the creation in the first part would fail.
You could get around this by specifying the parameters needed for a new record with something like
(User.first_or_create({:id => data['id'], :name => "Morse"}, {:name => "Lewis"})).update(:name => "Lewis")
but this is unusually painful, and is difficult to read.
Also note that using first_or_create with an :id will attempt to create a model with that specific :id, if such a record doesn't exist. This might not be what you want.
Alternatively, you can use first_or_new. You can't call update on an object created using this, however, as the record won't exist (although I believe this might have worked in previous versions of DataMapper).
Just for anyone coming across this answer, User.first_or_create({:id => data['id']}) does NOT "get the user with id = data['id'] or creates it if it doesn't already exist." It actually gets the first record in the table and changes its id t0 data['id'].
To actually get the first record with that id, or create it if it doesn't exist, you need to use a where clause:
User.where(id: data['id]).first_or_create
I have a mongodb collection that has documents like the ones below:
[
{
:event => {:type => 'comment_created'},
:item => {:id => 10},
:created_at => {:t => '11:19:03 +0100 2010', :d=> 'Fri, 19 Nov 2010'}
}
,
{
:event => {:type => 'vote_created'},
:item => {:id => 10},
:created_at => {:t => '11:19:03 +0100 2010', :d => 'Fri, 19 Nov 2010'}
}
]
What I need is to build a 'dashboard' aggregating latest activity (on current day) for each item. The result should be something like:
{
:item_id => 10,
:events => {
:vote_created => [.. ordered list with latest 3 vote_created events/documents],
:comment_created => [.. ordered list with latest 3 comment_created events/documents ],
}
}
The result would be used to construct a 'Facebook-style' syntax like: 'Mike, John and 3 others added comments on your item today.'
How can I aggregate this data using a group or a map-reduce function?
OK, there are two ways to do this:
Method #1: Map-Reduce
So first, you'll want to run a map-reduce, not a group.
Use Map-Reduce with the "out" variable which will generate a new collection. You'll then be able to run the summary queries against that new collection.
The reason you'll do this is that you're asking for an expensive query, so it's much more reasonable to access it in "not-quite" real-time.
Method #2: Double-writes
You can basically maintain two collections "details" (top one) and "summary" (bottom one). Whenever you do a write to the details, also perform an update to the summary.
MongoDB has several array methods ($push, $pull, $slice), that should make it possible to keep the "vote_created" array up-to-date.
Preferences
The method you select completely depends on the type of architecture you have and the user experience that you want. Personally, I would just use Method #2 and just keep appending to the "vote_created" array. I would put the 'Mike, John and 3 others...' syntax somewhere on the view, b/c it's really view logic not DB logic.
Yes method #2 takes more space, but it also gives you quick answers to the questions you ask alot. So you're going to have to sacrifice space to get that speed.
http://rickosborne.org/download/SQL-to-MongoDB.pdf