How to execute local queries with breeze EntityManager in Edmunds sample? - breeze

I'm a newby to breeze and wonder if it is possible to run local queries against entities that have been fetched using a REST service like in Edmunds sample.
Is it possible to execute local queries using breeze EntityManager after reading the entities from a remote REST service?
I tried to extend Edmunds sample app with a local query that will be called after all Makes have been loaded:
var query = breeze.EntityQuery
.from("Make:#Edmunds")
.where("niceName", "startsWith", "A")
return manager.executeQueryLocally(query).then(returnResults);
When I execute the query I get the following Exception:
"Cannot find an entityType for resourceName: 'Make:#Edmunds'.
Consider adding an 'EntityQuery.toType' call to your query or calling
the MetadataStore.setEntityTypeForResourceName method to register an
entityType for this resourceName."
What is wrong or missing here? How could I make the local query run?

Breeze is interpretting your query .from() parameter as a resource instead of as a type. (which you appear to be trying to set)
To let Breeze know which type of entity you are trying to query, simply do as it suggest - chain a toType call onto your query -
var query = breeze.EntityQuery .from("Make:#Edmunds").where("niceName", "startsWith", "A").toType('Make')
return manager.executeQueryLocally(query).then(returnResults);

Related

spring-data-neo4j query using dynamic key and dynamic value

spring-data-neo4j query using dynamic key and dynamic value,
like following code:
public interface NodeReposity extends Neo4jRepository<Node,Long> {
#Query("MATCH(n:Node{{key}={value}})return n")
Iterable<Node> queryByProperty(#Param("key")String key,#Param("value") String value);
}
But it says the {key} must be something like variable in string, such as MATCH(n:Node{name={value}})return n.Can't be {key}. But My property's key is dynamic like the value, how to implement it and is it possible?
Short answer: The query will be send "as it is" to the database and because cypher does only support placeholders for values, this will cause an error.
Slightly longer answer: When it comes to executing the method Spring Data Neo4j will look if it has already pre-processed the query and either process and cache it or just load it from the cache. This is done to improve the time it takes to execute the method from the application.
Pre-processing means SDN knows what parameters are in there and just adds the values in the right place when the method is called.
If SDN would provide more features for the query than cypher, the query would have to be processed every time the method gets called to create a new query that can be used with Neo4j.

Using navigation property in orderby clause of breeze query

I am using breeze to call a web api method which is:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => sa.Appeal).Include("Case");
My breeze query looks like:
var query = EntityQuery.from('GetEditShipmentAppeals')
.withParameters({ shipmentID: shipmentID, caseID: caseID })
.orderByDesc("Case.ID")
GetEditShipmentAppeals is a web api method that contains the first query. In spite of using .Include("Case") in the query I am not able to use "Case.ID" in the order by clause of breeze query.
var query = EntityQuery.from('Appeals')
.expand("Case,Patient")
.orderByDesc("Case.ID").inlineCount();
Even if I use navigation property on a breeze query that does not involve a EF query in web api, it does not work. In above query Case is a navigation property in Appeal table.
If I understand your example correctly, then I think that this is an Entity Framework issue. My understanding is that Entity Framework does not support "Includes" on a projection. See http://connect.microsoft.com/VisualStudio/feedback/details/347543/entity-framework-eager-loading-not-working-in-some-projection-scenarios.
To confirm this, I would try executing your EF query in isolation and see if the "Include" is actually doing anything. My guess is that it isnt.
However, you can still accomplish what you want with a slightly different server side projection. ( I'm not sure what object 'Case' is a property of and the syntax may be a bit off but...) Something like:
Repository.ShipmentAppeals.Where(sa => sa.ShipmentID == shipmentID).Select(sa => new
{ Appeal: sa.Appeal, Case: sa.Appeal.Case, CaseId: sa.Appeal.Case.Id });
Note that Breeze will return a collection of 'anonymous' javascript objects from this query, but each of the 'entities' within each of these objects (i.e. Appeals and Cases) will be full Breeze entities and will be part of the EntityManager cache.

breezejs: confused with fetching entity from database vs from cache

consider the following code:
function getPersonById(personId, type) {
var p1 = new breeze.Predicate("Id", "==", personId);
var p2 = new breeze.Predicate("Type", "==", type);
var query = breeze.EntityQuery.from("Contacts").where(p1.and(p2))
if (!manager.metadataStore.hasMetadataFor(service.serviceName)) {
return manager.fetchMetadata().then(function () {
return manager.executeQuery(query.using(service));
});
} else {
var fromCache = manager.getEntityByKey('Contact', personId);
if (fromCache)
return Q.resolve(fromCache);
return manager.executeQuery(query.using(service));
}
}
Am I doing things the right way ? It seems to me that I have to write a lot of boiler-plate code just for fetching an entity. I had to make sure the metadata was known, and then if the entity is already in cache or not.
I'm facing an issue because if executeQuery is called, then the return value is an array. However if getEntityByKey is called, then the return value is an Entity. How can I deal with that in an elegant way ? Is there a way to force executeQuery to return a single Entity rather than an array ? (I'm expecting only one returned value anyway)
Your metadata test shouldn't be necessary for each query. If you add a fail method that handles any errors (such as no metadata) you can write that only once, but in reality if whatever service type JavaScript file you are using is loaded metadata should always be there. If you are moving datacalls to the view models then I would recommend rethinking that strategy.
the way you are doing your cache check is optional. Remember that there are two ways to query from cache - executeQueryLocally and setting the fetchStrategy. There are some instances where you will want to go refresh data from the server so I would definitely recommend trying to pull from cache first in each query and only going to the database on a needed basis. Generally I only have two methods for all my data retrieval, for each entity, although if you are tricky you can probably reduce that to sharing queries as well. Your queries are most efficient when you can reuse them for different orderBy's, where clauses, etc...
Last, if you want to return only a single entity just do it lklike you would any other array - catch the returned array results before sending them back and filter it down to something like data.results[0]. You could also query and then use a filter to find the first entity that meets sine criteria.

BreezeJS malformed OData query Url when using "startsWith"

Hello I am trying to execute a query using breezejs 1.3.4 . My query is the following
function getContacts() {
var query = breeze.EntityQuery
.from("Contacts").where("Desc", "startsWith", "P");
return manager.executeQuery(query)
.then(getSucceeded).fail(getFailed);
}
"Desc" is a string property in my "Contacts" C# backend model. Tha problem is that the Query URL is formatted like this .../api/Application/Contacts?$filter=startswith(Desc%2Ctime'P')%20eq%20true
The word time is added before "P" and I get a this exception in the Response
{"$id":"1","$type":"System.Web.Http.HttpError, System.Web.Http","Message":"The query specified in the URI is not valid.","ExceptionMessage":"Unrecognized 'Edm.Time' literal 'time'P''
If in the comparison I use a lower case "p" then the Url is costructed as it should be like this
"$filter=startswith(Desc%2C'p')%20eq%20true` .
I don't have the same problem when using other UpperCase letters of the english alphabet.
Does anyone have an idea what am I missing, I can't figure out why the word "time" is added in that specific query?
Thank you.
We were able to reproduce the problem.
While the exception message is confusing, I believe you might be getting the error because you have not associated that the resource name 'Contacts' with an EntityType of presumably 'Contact'.
What is happening is that when Breeze tries to build the query it will usually use its metadata to correctly format the url query string to send to the server. The critical part of this process involves determining the EntityType associated with the resourceName given in the 'from' clause of the query ('Contacts' in your case). Breeze uses a resource name to entity type map to do this, but if it cannot find a mapping, it will still continue to build the url because we still need to support ad-hoc requests against endpoints for which we have no metadata.
You can update this map yourself via the MetadataStore.setEntityTypeForResourceName method. If you are using the Entity Framework, this map is initially populated based on the EntityType name/Entity Set name mapping that is part of your EDMX model. So in your case you can either modify your EDMX model or call the setEntityTypeForResourceName method directly.
Unfortunately, without the metadata Breeze has to infer the datatypes in the query. So in your case
"Desc", "startsWith", "P"
since Breeze can't determine that "Desc" is a 'string' property of your Contract EntityType it tries to infer the 'dataType' of "Desc" based on the value 'P'. Unfortunately, 'P' is a valid ISO8601 'duration' value ( a way to express a 'Time' datatype), so Breeze incorrectly tries to construct a query string with 'P' treated as a 'Time' constant. This is why your code works with a lower case 'p' ( not a valid duration value).
This inference logic can certainly be improved and we have a fix that will allow us to do exactly that coming out in the next release. However... the better and more general solution to this type of issue is to get the 'resourceName/entityType' mappings correct in the first place.
Hope this helps.

Breeze manage NODB EntityTypes with DB EntityTypes

i´m using the Papa's course CCJS code to investigate Breeze.js and SPA. Using this code i´m trying to manage aditional information that cames from server but that is not an Entity contained in the Metadata that cames from EntityFramework.
So i created a NO-DB class called Esto and a Server method like Lookups:
[HttpGet]
public object Informacion()
{
var a = new Esto(....);
var b = new Esto(.....);
var c = new Esto(......);
return new {a,b,c};
}
then in model.js inside configureMetadataStore i call:
metadataStore.addEntityType({
shortName: "Esto",
namespace:"CodeCamper",
dataProperties:{
id: {dataType: breeze.DataType.Int32,isPartOfKey: true},
name: {dataType: breeze.DataType.String}
}
};
and also define in the model entityNames array: esto:'Esto' as an Entity
now in the context.js i load this creating a server side method like getLookups but called getInformacion:
function getInformacion(){
return EntityQuery.from('Informacion')
.using(manager).execute()
}
and then inside primeData in the success method call this:
datacontext.informacion = {
esto: getLocal('Esto',nombre)};
where getLocal is:
function getLocal(resource, ordering)
{
var query = EntityQuery.from(resource).orderBy(ordering);
return manager.executeQueryLocally(query);
}
I get an error in the query contained in the getLocal that states that Can not find EntityType for either entityTypeName: 'undefined' or resourceName:'Esto'.
What i´m doing wrong?
Thanks
You were almost there! :-) Had you specified the target EntityType in the query I think it would have worked.
Try this:
var query = EntityQuery.from(resource).orderBy(ordering).toType('Esto');
The toType() method tells Breeze that the top-level objects returned by this query will be of type Esto.
Why?
Let's think about how Breeze interprets a query specification.
Notice that you began your query, as we usually do, by naming the resource which will supply the data. This resource is typically a path segment to a remote service endpoint, perhaps the name of a Web API controller method ... a method named "Foos".
It's critical to understand that the query resource name is rarely the same as the EntityType name! They may be similar - "Foos" (plural) is similar to the type name "Foo" (singular). But the resource name could be something else. It could be "GetFoos" or "GreatFoos" or anything at all. What matters is that the service method returns "Foo" entities.
Breeze needs a way to correlate the resource name with the EntityType name. Breeze doesn't know the correlation on its own. The toType() method is one way to tell Breeze about it.
Why do remote queries work without toType()?
You generally don't add toType() to your queries. Why now?
Most of the time [1], Breeze doesn't need to know the EntityType until after the data arrive from the server. When the JSON query results includes the type name (as they do when they come from a Breeze Web API controller for example), Breeze can map the arriving JSON data into entities without our help ... assuming that these type names are in metadata.
Local cache queries are different
When you query the cache ... say with executeQueryLocally ... Breeze must know which cached entity-set to search before it can query locally.
It "knows" if you specify the type with toType(). But if you omit toType(), Breeze has to make do with the query's resource name.
Breeze doesn't guess. Instead, it looks in an EntityType/ResourceName map for the entity-set that matches the query resource name.
The resource name refers to a service endpoint, not a cached entity-set. There is no entity-set named "Informacion", for example. So Breeze uses an EntityType/ResourceName map to find the entity type associated with the query resource name.
EntityType/ResourceName
The EntityType/ResourceName map is one of the items in the Breeze MetadataStore. You've probably never heard of it. That's good; you shouldn't have to think about it ... unless you do something unusual like define your own types.
The map of a new MetadataStore starts empty. Breeze populates it from server metadata if those metadata contain EntityType/Resource mappings.
For example, the Breeze EFContextProvider generates metadata with mappings derived from DbSet names. When you define a Foo class and exposed it from a DbContext as a DbSet named "Foos", the EFContextProvider metadata generator adds a mapping from the "Foos" resource name to the Foo entity type.
Controller developers tend to use DbSet names for method names. The conventional Breeze Web API controller "Foo" query method looks like this:
[Get]
public IQueryable<Foo> Foos() {...}
Now if you take a query such as this:
var query = EntityQuery.from('Foos').where(...);
and apply it to the cache
manager.query.executeLocally(query).then(...);
it just works.
Why? Because
"Foos" is the name of a DbSet on the server
The EFContextProvider generated metadata mapping ["Foos" to Model.Foo]
The Web API Controller offers a Foos action method.
The BreezeJS query specifies "Foos"
The executeLocally method finds the ["Foos"-to-Model.Foo] mapping in metadata and applies the query to the entity-set for Foo.
The end-to-end conventions work silently in your favor.
... until you mention a resource name that is not in the EntityType/ResourceName map!
Register the resource name
No problem!
You can add your own resource-to-entity-type mappings as follows:
var metadataStore = manager.metadataStore;
var typeName = 'some-type-name';
var entityType = metadataStore.getEntityType(typeName);
metadataStore.setEntityTypeForResourceName(resource, entityType);
Breeze is also happy with just the name of the type:
metadataStore.setEntityTypeForResourceName(resource, typeName);
In your case, somewhere near the top of your DataContext, you could write:
var metadataStore = manager.metadataStore;
// map two resource names to Esto
metadataStore.setEntityTypeForResourceName('Esto', 'Esto');
metadataStore.setEntityTypeForResourceName('Informacion', 'Esto');
Don't over-use toType()
The toType() method is a good short-cut solution when you need to map the top-level objects in the query result to an EntityType. You don't have to mess around with registering resource names.
However, you must remember to add toType() to every query that needs it. Configure Breeze metadata with the resource-to-entity-type mapping and you'll get the desired behavior every time.
Notes
[1] "Most of the time, Breeze doesn't need to know the EntityType until after the data arrive from the server." One important exception - out of scope for this discussion - is when the query filter involves a Date/Time.
I think that the problem here is that you are assuming that entity type names and resource names are the same thing. A resource name is what is used to execute a query
var q = EntityQuery.from(resourceName);
In your case the "resourceName" is "Informacion" and the entityType is actually "Esto". Breeze is able to make this connection on a remote query because it can examine the results returned from the server as a result of querying "Informacion" and seeing that they are actually "Esto" instances. This is not possible for a local query because Breeze doesn't know what local collection to start from.
In this case you need to give Breeze a little more information via the MetadataStore.setEntityTypeForResourceName method. Something like this:
var estoType = manager.metadataStore.getEntityType("Esto");
manager.metadataStore.setEntityTypeForResourceName("Informacion", estoType);
Note that this is not actually necessary if the resource was defined via Entity Framework metadata, because Breeze automatically associates all EF EntitySet names to resource names, but this information isn't available for DTO's.
Note also that a single entity type can have as many resourceNames as you like. Just make sure to register the resourceNames before you attempt a local query.

Resources