With RavenDB, is it possible to get the IDs of a property within another property? For example, if Foo has a list of Bar objects, and each Bar object has a SnuhId property, can I use an Include that gets the IDs of each Snuh property?
I tried the query below, but I get a RavenDB exception: index out of range. In this query, ApplicationServer is a root element, and it has a list of ApplicationsWithOverrideGroup objects. Each of those objects has an ApplicationId property. It's the ApplicationId that I want to get in the include.
IEnumerable<ApplicationServer> appServers = QueryAndCacheEtags(session =>
session.Advanced.LuceneQuery<ApplicationServer>()
.Include(x => x.CustomVariableGroupIds)
// This is the line I'm trying to make work:
.Include(x => (from item in x.ApplicationsWithOverrideGroup select item.ApplicationId).ToList())
).Cast<ApplicationServer>();
Either of these approaches appears to be working. Need to thoroughly test.
.Include(x => x.ApplicationsWithOverrideGroup)
or
.Include(x => x.ApplicationsWithOverrideGroup[0].ApplicationId)
If that first option is indeed working, then a property, specified in an Include(), will include the ID properties within it. Is that right?
I'm not sure if both of those are really working, but they seem to be. If they both work, I wonder if one is better than the other...
Ok, that's NOT WORKING. The NumberOfRequests is increasing, which I'm guessing means the number of trips to the DB is increasing, instead of just what's in the session.
Ok, none of those suggestions worked in the question above. I think what has to be done is to include the IDs of the nested properties in the root object.
And that's what I did, and I was able to get the NumberOfRequests on the session object down to one. Curiously, I had to change this:
// Not using this, at least for now, because it increased the NumberOfRequests on the session...
appServer.CustomVariableGroups = new ObservableCollection<CustomVariableGroup>(
QueryAndCacheEtags(session => session.Load<CustomVariableGroup>(appServer.CustomVariableGroupIds)).Cast<CustomVariableGroup>());
To this:
// ... however, this kept the NumberOfRequests to just one. Not sure why the difference.
appServer.CustomVariableGroups = new ObservableCollection<CustomVariableGroup>();
foreach (string groupId in appServer.CustomVariableGroupIds)
{
appServer.CustomVariableGroups.Add(QuerySingleResultAndCacheEtag(session => session.Load<CustomVariableGroup>(groupId)) as CustomVariableGroup);
}
Related
I am not sure why I can't get the columns from my other tables via my relations. I was thinking is it because of my scope? After i had a default scope in my models, everything seems to be out of place, even if i use resetscope() at some places. Some sections I can't get to my relation columns; when that happens, I'd have to use Model::model->findbypk(n)->name.. that doesn't look pretty.
the id shows if i don't have the relations, but the name is blank when i put the relation name.
CHtml::listData(Model::model()->findAll(),'product_id','main.product_name'),
my model defaultscope is pretty basic:
return array(
'condition'=>'store_id1=:store_id OR store_id2=:store_id' ,
'params' => array(':store_id' => $store_id)
);
You can change the way you use your model like below:
Model::model()->with('main')->findAll();
I want to get a list of users filtered by a property (string) being null or empty.
I've created an index for this but I'm not sure if my way of implementing it is the right way to do it.
public class Users_Contributors : AbstractIndexCreationTask<User>
{
public Users_Contributors()
{
Map = users => from user in users
where user.ContributionDescription != null && user.ContributionDescription != ""
select new {};
}
}
So I just want raven to "prepare" the list of users for me. I'm just gonna get all user objects out of that index with no additional filtering/where criterias at query time.
Is the above code the way to go or can I achieve the same in a better way? Im feeling Im missing something here. Thanks!
This will work just fine. The result would be that the index only contains users that have something in their ContributionDescription field.
If you want to make it slightly easier to read, you can use string.IsNullOrEmpty, but that won't have any impact on performance.
Map = users => from user in users
where !string.IsNullOrEmpty(user.ContributionDescription)
select new {};
It probably feels strange because of the empty object at the end, but that just defines the index entries. If you aren't sorting or filtering by any other field, then using an empty object is just fine. Keep in mind that the __document_id entry gets created regardless of what fields you map.
Blockquote
We are using the ByCode method of mapping our data..
I have Process (Process table) object which has a List of ProcessStep (ProcessStep table) objects which in turn has a Bag of ProcessStepUser (ProcessStepUser table) objects assigned to each step.
The objects load just fine, but in the web page if I delete a Step from the collection, rehydrate into MVC action as a Process and then save the process, the Step in the database simply has the ProcessId set to null which breaks the link, but leaves the Step in the Database and all its assigned users.
What I want to be able to do is delete a Step and have its ProcessStep and all its ProcessStepUsers deleted.
Likewise, when I edit a ProcessStep (say I change the name of the step) and save, it's saving that, but the ProcessStep users are nulled out (of their ProcessStepId) and recreated, leaving orphaned records.
I do have a column on ProcessStepUser for overall ProcessId as well, so that I can prevent a User from being assigned more than once to any step of the process.
My relevant mapping is as follows:
Process:
List(x => x.ProcessSteps, m =>
{
m.Key(k => k.Column("ProcessId"));
m.Index(i => i.Column("StepOrder"));
m.Lazy(CollectionLazy.NoLazy);
m.Cascade(Cascade.All);
m.Inverse(false);
},
r => r.OneToMany(o => o.Class(typeof (ProcessStep))));
ProcessStep:
Bag(x => x.ProcessStepUsers, m =>
{
m.Key(k => k.Column("ProcessStepId"));
m.Lazy(CollectionLazy.NoLazy);
m.Cascade(Cascade.All);
m.Inverse(false);
},
r => r.OneToMany(o => o.Class(typeof (ProcessStepUser))));
ManyToOne(x => x.Process, m =>
{
m.Column("ProcessId");
m.Class(typeof (Process));
});
ProcessStepUser
ManyToOne(p => p.Step, m =>
{
m.Column("ProcessStepId");
m.Class(typeof (ProcessStep));
});
ManyToOne(p => p.Process, m =>
{
m.Column("ProcessId");
m.Class(typeof (Process));
});
As I said, it saves just fine on creation and loads just fine for display. But the dropping of a step or editing of a step is creating havoc in the Database.
I hacked the step deletion process by flagging them and manually deleting them before saving the Process master object, but I'd like nhibernate to do it all if possible.
Thanks!
Blockquote
First off thanks to Martin who caught the one issue with the Cascade.All.Include(Cascade.DeleteOrphans)... that was the original code I had, I forgot I'd changed it while attempting to solve the problem.
I rewrote some tests and the mapping actually is working just fine, so apologies there. I would delete this question, but the site is not letting me to anything (in Firefox) but edit...
The problem ended up being because I was getting the Process object from the database and then serializing it up to the View as a JSON object. I then passed back the JSON object which was bound back to the Process object.
The long and short is that our repository was calling SaveOrUpdate(obj) on the object, but since it had been disconnected, we needed to call Merge(obj) which we did and worked perfectly.
I leave it to the powers that be as to whether this question has any value remaining.
Thanks!
Try Cascade(Cascade.AllDeleteOrphan) on your associations where you want them to be deleted instead of having the other side nulled...
Starting with a List of entities and needing all dependent entities through an association, is there a way to use the corresponding navigation-propertiy to load all child-entities with one db-round-trip? Ie. generate a single WHERE fkId IN (...) statement via navigation property?
More details
I've found these ways to load the children:
Keep the set of parent-entities as IQueriable<T>
Not good since the db will have to find the main set every time and join to get the requested data.
Put the parent-objects into an array or list, then get related data through navigation properties.
var children = parentArray.Select(p => p.Children).Distinct()
This is slow since it will generate a select for every main-entity.
Creates duplicate objects since each set of children is created independetly.
Put the foreign keys from the main entities into an array then filter the entire dependent-ObjectSet
var foreignKeyIds = parentArray.Select(p => p.Id).ToArray();
var children = Children.Where(d => foreignKeyIds.Contains(d.Id))
Linq then generates the desired "WHERE foreignKeyId IN (...)"-clause.
This is fast but only possible for 1:*-relations since linking-tables are mapped away.
Removes the readablity advantage of EF by using Ids after all
The navigation-properties of type EntityCollection<T> are not populated
Eager loading though the .Include()-methods, included for completeness (asking for lazy-loading)
Alledgedly joins everything included together and returns one giant flat result.
Have to decide up front which data to use
It there some way to get the simplicity of 2 with the performance of 3?
You could attach the parent object to your context and get the children when needed.
foreach (T parent in parents) {
_context.Attach(parent);
}
var children = parents.Select(p => p.Children);
Edit: for attaching multiple, just iterate.
I think finding a good answer is not possible or at least not worth the trouble. Instead a micro ORM like Dapper give the big benefit of removing the need to map between sql-columns and object-properties and does it without the need to create a model first. Also one simply writes the desired sql instead of understanding what linq to write to have it generated. IQueryable<T> will be missed though.
I have a big, flat table:
id
product_id
attribute1
attribute2
attribute3
attribute4
Here is how I want users to get to products:
See a list of unique values for attribute1.
Clicking one of those gets you a list of unique values for attribute2.
Clicking one of those gets you a list of unique values for attribute3.
Clicking one of those gets you a list of unique values for attribute4.
Clicking one of those shows you the relevant products.
I have been coding Rails for about 4 years now. I just can't unthink my current approach to this problem.
I have major writer's block. Seems like such an easy problem. But I either code it with 4 different "step" methods in my controller, or I try to write one "search" method that attempts to divine the last level you selected, and all the previous values that you selected.
Both are major YUCK and I keep deleting my work.
What is the most elegant way to do this?
Here is a solution that may be an option. Just off the top of my head and not tested (so there is probably a bit more elegant solution). You could use chained scopes in your model:
class Product < ActiveRecord::Base
scope :with_capacity, lambda { |*args| args.first.nil? ? nil : where(:capacity=>args.first) }
scope :with_weight, lambda { |*args| args.first.nil? ? nil : where(:weight=>args.first) }
scope :with_color, lambda { |*args| args.first.nil? ? nil : where(:color=>args.first) }
scope :with_manufacturer, lambda { |*args| args.first.nil? ? nil : where(:manufacturer=>args.first) }
self.available_attributes(products,attribute)
products.collect{|product| product.send(attribute)}.uniq
end
end
The code above will give you a scope for each attribute. If you pass a parameter to the scope, then it will give you the products with that attribute value. If the argument is nil, then the scope will return the full set (I think ;-). You could keep track of the attributes they are drilling down in in the session with 2 variables (page_attribute and page_attribute_value) in your controller. Then you call the entire chain to get your list of products (if you want to use them on the page). Next you can get the attribute values by passing in the set of products and the attribute name to Product.available_attributes. Note that this method (Product.available_attributes) is a total hack and would be inefficient for a large set of data, so you may want to make this another scope and use :select=>"DISTINCT(your_attribute)" or something more database efficient instead of iterating thru the full set of products as I did in the hack method.
class ProductsController < ApplicationController
def show
session[params[:page_attribute].to_sym] = params[:page_attribute_value]
#products = Product.all.with_capacity(session[:capacity]).with_weight(session[:weight]).with_color(session[:color]).with_manufacturer(session[:manufacturer])
#attr_values = Product.available_attributes(#products,params[:page_attribute])
end
end
Again, I want to warn you that I did not test this code, so its totally possible that some of the syntax is incorrect, but hopefully this will give you a starting point. Holla if you have any questions about my (psuedo) code.