Activity stream design with RavenDb - ravendb

This is more of a design pattern / document design question than a technical one...
I want to display a activity feed on my website which will list all the latest happenings users have been doing on my site...here are some of the activities i would like to display:
New media uploaded (Bob has uploaded a new track)
Comments on a profile (Paul has commented on Bob's profile)
Comments on media (Steve has commented on Paul's track 'my track name')
Status updates (Steve can write any status update he wishes)
Each activity will need to have it's own set of data, such as the new media uploaded activity I would like to include details about the media such as image, title, description etc).
This activity will mostly be used as a global feed so it's the same for all users, although I need the option for users to only show feed items from users they are following (like twitter).
I think I have 2 options:
1) Pull in all the data Ad-Hoc with an index so the information is always up to date, even if a user alters his media title name...i'm not sure how well this will scale though??
2) Have an ActivityFeed document which contains a Sub-Document such as NewMediaUploadActivity (shown below).
ActivityFeed
- DateTime
- AccountId
- ActivityType
- Activity (this is a polymorphic object)
NewMediaUploadActivity : Activity
- MediaTitle
- MediaDescription
- GenreName
StatusUpdateActivity : Activity
- StatusText
ProfileCommentActivity : Activity
- CommentText
- ProfileAccountId
- ProfileUsername
Etc...
If anybody has any experience, or any input on the best way to do this in RavenDB I would be grateful, my live website built with SQL Server currently does what I need using a slightly modified option 2.
Paul

I would model this as:
public class ActivityTracking<TActivity>
{
public string[] AffectedUsers {get;set;}
public TActivity Activity {get;set;}
}
You can have different activities (not required to be in an inheritance hierarchy) that are "attached" to different users.
For example, Bob commenting on Jane's photo would show up in both streams.
You then can just query for activities for that user.

Related

RESTful API - How do I return different results for the same resource?

Question
How do I return different results for the same resource?
Details
I have been searching for some time now about the proper way to build a RESTful API. Tons of great information out there. Now I am actually trying to apply this to my website and have run into a few snags. I found a few suggestions that said to base the resources on your database as a starting point, considering your database should be structured decently. Here is my scenario:
My Site:
Here is a little information about my website and the purpose of the API
We are creating a site that allows people to play games. The API is supposed to allow other developers to build their own games and use our backend to collect user information and store it.
Scenario 1:
We have a players database that stores all player data. A developer needs to select this data based on either a user_id (person who owns the player data) or a game_id (the game that collected the data).
Resource
http://site.com/api/players
Issue:
If the developer calls my resource using GET they will receive a list of players. Since there are multiple developers using this system they must specify some ID by which to select all the players. This is where I find a problem. I want the developer to be able to specify two kinds of ID's. They can select all players by user_id or by game_id.
How do you handle this?
Do I need two separate resources?
Lets say you have a controller name 'Players', then you'll have 2 methods:
function user_get(){
//get id from request and do something
}
function game_get(){
//get id from request and do something
}
now the url will look like: http://site.com/api/players/user/333, http://site.com/api/players/game/333
player is the controller.
user/game are the action
If you use phil sturgeon's framework, you'll do that but the url will look like:
http://site.com/api/players/user/id/333, http://site.com/api/players/game/id/333
and then you get the id using : $this->get('id');
You can limit the results by specifying querystring parameters, i.e:
http://site.com/api/players?id=123
http://site.com/api/players?name=Paolo
use phil's REST Server library: https://github.com/philsturgeon/codeigniter-restserver
I use this library in a product environment using oauth, and api key generation. You would create a api controller, and define methods for each of the requests you want. In my case i created an entirely seperate codeigniter instance and just wrote my models as i needed them.
You can also use this REST library to insert data, its all in his documentation..
Here is a video Phil threw together on the basics back in 2011..
http://philsturgeon.co.uk/blog/2011/03/video-set-up-a-rest-api-with-codeigniter
It should go noted, that RESTful URLs mean using plural/singular wording e.g; player = singular, players = all or more than one, games|game etc..
this will allow you to do things like this in your controller
//users method_get is the http req type.. you could use post, or put as well.
public function players_get(){
//query db for players, pass back data
}
Your API Request URL would be something like:
http://api.example.com/players/format/[csv|json|xml|html|php]
this would return a json object of all the users based on your query in your model.
OR
public function player_get($id = false, $game = false){
//if $game_id isset, search by game_id
//query db for a specific player, pass back data
}
Your API Request URL would be something like:
http://api.example.com/player/game/1/format/[csv|json|xml|html|php]
OR
public function playerGames_get($id){
//query db for a specific players games based on $userid
}
Your API Request URL would be something like:
http://api.example.com/playerGames/1/format/[csv|json|xml|html|php]

RavenDB - retrieving part of document

I am playing with Raven DB for few days and I would like to use it as a storage for my Web chat application. I have document which contains some user data and chat history - which is big collection chat messages.
Each time I load user document chat history is also loaded, even if I need only few fields like: user name, password and email.
My question is: how to load only part of document from database ?
Tomek,
You can't load a partial document, but you can load a projection.
session.Query<User>()
.Where(x=>x.Name == name)
.Select( x=> new { x.Name, x.Email });
That will load only the appropriate fields
From what I've seen you can do this (based on the original "User" scenario above):
public class UserSummary
{
public string Name { get; set; }
public string Email { get; set; }
}
Then you can do this:
documentSession.Query<User>().AsProjection<UserSummary>();
Looking at the Raven server it spits this out as part of the query:
?query=&pageSize=128&fetch=Name&fetch=Email&fetch=Id
So it looks like it is querying and returning only a subset of the original object, which is good.
This also works:
documentSession.Query<User>().Select( x=> new User { Name = x.Name, Email= x.Email })
But I don't think that is as clean as returning a UserSummary object.
Some follow up questions to those who have posted responses:
The link to RaccoonBlog has this example:
https://github.com/ayende/RaccoonBlog/blob/master/RaccoonBlog.Web/Infrastructure/Indexes/PostComments_CreationDate.cs
Would that method be preferred over the .AsProjection()? What is the difference between the two approaches?
Tomek, you cannot load only a part of the document.
However, I understand the problem in your case. I recommend to use two seperate documents for each user: One that actually contains the users data (name, passwordhash, email, etc.) and one that contains all the users messages. That way, it is still very cheap to load all the messages of a user and also to load a list of user for general purposes.
This is actually quite similar to how one would model a blog-domain, where you have a post and the posts comments. Take a look at RaccoonBlog to see how this works.

NHibernate AliasToBeanResultTransformer & Collections

I would like to return a DTO from my data layer which would also contain child collections...such as this:
Audio
- Title
- Description
- Filename
- Tags
- TagName
- Comments
- PersonName
- CommentText
Here is a basic query so far, but i'm not sure how to transform the child collections from my entity to the DTO.
var query = Session.CreateCriteria<Audio>("audio")
.SetProjection(
Projections.ProjectionList()
.Add(Projections.Property<Audio>(x => x.Title))
.Add(Projections.Property<Audio>(x => x.Description))
.Add(Projections.Property<Audio>(x => x.Filename))
).SetResultTransformer(new AliasToBeanResultTransformer(typeof(AudioDto)))
.List<AudioDto>();
Is this even possible, or is there another reccomended way of doing this?
UPDATE:
Just want to add a little more information about my scenario...I want to return a list of Audio items to the currently logged in user along with some associated entities such as tags, comments etc...these are fairly straight forward using MultiQuery / Future.
However, when displaying the audio items to the user, i also want to display 3 other options to the user:
Weather they have added this audio item to their list of favourites
Weather they have given this audio the 'thumbs up'
Weather the logged in user is 'Following' the owner of this audio
Favourites : Audio -> HasMany -> AudioUserFavourites
Thumbs Up : Audio -> HasManyToMany -> UserAccount
Following Owner : Audio -> References -> UserAccount ->
ManyToMany -> UserAccount
Hope this makes sense...if not i'll try and explain again...how can I eager load these extra details for each Audio entity returned...I need all this information in pages of 20 also.
I looked at Batch fetching, but this appears to fetch ALL thumbs ups for each Audio entity, rather than checking if only the logged in user has thumbed it.
Sorry for rambling :-)
Paul
If you want to fetch your Audio objects with both the Tags collection and Comments collections populated, have a look at Aydende Rahien's blog: http://ayende.com/blog/4367/eagerly-loading-entity-associations-efficiently-with-nhibernate.
You don't need to use DTOs for this; you can get back a list of Audio with its collections even if the collections are lazily loaded by default. You would create two future queries; the first will fetch Audio joined to Tags, and the second will fetch Audio joined to Comments. It works because by the time the second query result is being processed, the session cache already has the Audio objects in it; NHibernate grabs the Audio from the cache instead of rehydrating it, and then fills in the second collection.
You don't need to use future queries for this; it still works if you just execute the two queries sequentially, but using futures will result in just one round trip to the database, making it faster.

Siebel - How to get all accounts of an employee with eScript?

how can I get all accounts of am employee?
In the "Siebel Object Interaces Reference" I found an example, how to get all industries of an account:
var myAccountBO = TheApplication().GetBusObject("Account");
var myAccountBC = myAccountBO.GetBusComp("Account");
var myAssocBC = myAccountBC.GetMVGBusComp("Industry");
So I would like to do something like:
var myEmployeeBO = TheApplication().GetBusObject("Employee");
var myEmployeeBC = myAccountBO.GetBusComp("Employee");
var myAssocBC = myAccountBC.GetMVGBusComp("Account");
But I get an error
Semantic Warning around line 23:No such predefined property Account in class BusComp[Employee].MVGFields.
I can see in Tools that there is no Multi Value Link called "Account" in Business Component "Employee", so I can actually understand the error message.
So I wonder how I can get all accounts of an employee.
I found the Business Component "User" which has a Multi Value Link to "Organisation" and another link "User/Account".
Is this what I am looking for?
How can I know? Where is documentation which tells me about the semantics of links? (Is this described in "Siebel data model reference"? I cannot download this document, although I have signed in...) This link could also link a user to the organization it belongs to.
If one of these links IS what I am looking for, what would be the way to go to get the "User" Business Component of a corresponding "Employee" Business Component?
Many questions of a Siebel newb...Thanks for your patience.
Nang. An easy way to approach this (and to learn it) is to figure out how you'd do it in the UI. Then move onto figuring out how to do the same thing in script.
When you say, "get all account of an employee," do you really mean get all accounts where a particular employee is on the account team? In the UI, that would be done by going to: Accounts > All Accounts Across Organizations, and querying for that specific user in the "Account Team" multi-value field.
From that same view, go to Help > About View in the application menu. You'll see in the popup that the view uses the Account business object and the Account business component. A quick examination of the applet you queried on will show you that the "Account Team" field on the applet is really the "Sales Rep" field on the Account business component. Here's how to mimic what we did in the UI, in script:
var boAccount = TheApplication().GetBusObject("Account");
var bcAccount = boAccount.GetBusComp("Account");
bcAccount.SetViewMode(AllView); // like All .. Across Orgs
bcAccount.ClearToQuery();
bcAccount.SetSearchSpec("Sales Rep", "NANG");
bcAccount.ExecuteQuery();
Then you can walk through the list of accounts and do something with each one like this:
// for each account
for (var bIsRowActive = bcAccount.FirstRecord();
bIsRowActive; b = bcAccount.NextRecord())
{
// do something here
}
I hope you're enjoying Siebel.

NHibernate criteria query question

I have 3 related objects (Entry, GamePlay, Prize) and I'm trying to find the best way to query them for what I need using NHibernate. When a request comes in, I need to query the Entries table for a matching entry and, if found, get a) the latest game play along with the first game play that has a prize attached. Prize is a child of GamePlay and each Entry object has a GamePlays property (IList).
Currently, I'm working on a method that pulls the matching Entry and eagerly loads all game plays and associated prizes, but it seems wasteful to load all game plays just to find the latest one and any that contain a prize.
Right now, my query looks like this:
var entry = session.CreateCriteria<Entry>()
.Add(Restrictions.Eq("Phone", phone))
.AddOrder(Order.Desc("Created"))
.SetFetchMode("GamePlays", FetchMode.Join)
.SetMaxResults(1).UniqueResult<Entry>();
Two problems with this:
It loads all game plays up front. With 365 days of data, this could easily balloon to 300k of data per query.
It doesn't eagerly load the Prize child property for each game. Therefore, my code that loops through the GamePlays list looking for a non-null Prize must make a call to load each Prize property I check.
I'm not an nhibernate expert, but I know there has to be a better way to do this. Ideally, I'd like to do the following (pseudocode):
entry = findEntry(phoneNumber)
lastPlay = getLatestGamePlay(Entry)
firstWinningPlay = getFirstWinningGamePlay(Entry)
The end result of course is that I have the entry details, the latest game play, and the first winning game play. The catch is that I want to do this in as few database calls as possible, otherwise I'd just execute 3 separate queries.
The object definitions look like:
public class Entry
{
public Guid Id {get;set;}
public string Phone {get;set;}
public IList<GamePlay> GamePlays {get;set;}
// ... other properties
}
public class GamePlay
{
public Guid Id {get;set;}
public Entry Entry {get;set;}
public Prize Prize {get;set;}
// ... other properties
}
public class Prize
{
public Guid Id {get;set;}
// ... other properties
}
The proper NHibernate mappings are in place, so I just need help figuring out how to set up the criteria query (not looking for HQL, don't use it).
since you are doing this in each request maybe it should be better to set up two formula-properties in your entity.
The first one should fetch the latest Gameplay-Id and the other the first Gameplay-Id with a not Null property
this could be as such in the xml mapping file of Entry
<property name="LatestGameplay" formula="select top(1)gp.Id from Gameplay gp where gp.FK_EntryId = PK_EntryId order by gp.InsertDate desc" />
this leaves you with the Gameplay Id's on the Entry entity and after you fetch it it would require another round trip to the DB to GetById-fetch the gameplay's
Alternatively you could work-around using filters.
Set the collection back to "lazy"
and create these nice filters
Gameplay latest = NHibernateSession.CreateFilter(entry.GamePlays , "order by InsertDate desc").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();
Gameplay winner = NHibernateSession.CreateFilter(entry.GamePlays , "where FK_PrizeId is not null order by InsertDate asc ").SetMaxResults(1).SetFirstResult(1).UniqueResult<Gameplay>();
And IFilters can be used in a multiquery as so have 2 db hits: one for the original Entry and one for the multiquery.
Last but not least, you could define 2 bags in the Entry entity, one IList<GamePlay> Latest and one IList<Gameplay> Winner which in the Entry mapping file would be filtered with the appropriate query (although i don't remember now if you can define TOP clauses in the filters) and set those as non-lazy. Then with a single round-trip you can have all the data you want with the following (ugly) syntax
Entry entry = findEntry(phoneNumber);
Gameplay winner = entry.Winner[0]; //check this if null first
Gameplay Latest = entry.Latest[0]; //ditto
note that of all the solutions the 3rd is the one that provides a mechanism to generate additional queries, as the bag can be used in a Criteria/HQL query