The Neop4jClient cypher wiki (https://github.com/Readify/Neo4jClient/wiki/cypher) contains an example of using lambda expressions to return multiple projections...
var query = client
.Cypher
.Start(new { root = client.RootNode })
.Match("root-[:HAS_BOOK]->book-[:PUBLISHED_BY]->publisher")
.Return((book, publisher) => new {
Book = book.As<Book>(),
Publisher = publisher.As<Publisher>(),
});
So the query will return details of both book nodes and publisher nodes. But I want to do something slightly different. I want to combine the contents of a single node type with a property of the matched path. Lets say I have Person nodes with a property 'name', and a class defined so,,,
public class descendant
{
public string name { get; set; }
public int depth { get; set; }
}
A cypher query like this will return what I want, which is all descendants of a given node with the depth of the relationship...
match p=(n:Person)<-[*]-(child:Person)
where n.name='George'
return distinct child.name as name, length(p) as depth
If I try a Neo4jClient query like this...
var query =
_graphClient.Cypher
.Match("p=(n:Person)<-[*]-(child:Person)")
.Where("n.name='George'")
.Return<descendant>("child.name, length(p)") ;
I get an error that the syntax is obsolete, but I can't figure out how should I project the cypher results onto my C# POCO. Any ideas anyone?
The query should look like this:
var query =
_graphClient.Cypher
.Match("p=(n:Person)<-[*]-(child:Person)")
.Where((Person n) => n.name == "George")
.Return((n,p) => new descendant
{
name = n.As<Person>().Name,
depth = p.Length()
});
The Return statement should have the 2 parameters you care about (in this case n and p) and project them via the lambda syntax (=>) to create a new descendant instance.
The main point this differs from the example, is that the example creates a new anonymous type, whereas you want to create a concrete type.
We then use the property initializer (code inside the { } braces) to set the name and depth, using the As<> and Length extension methods to get the values you want.
As a side note, I've also changed the Where clause to use parameters, you should always do this if you can, it will make your queries both faster and safer.
Related
I am using Apache Ignite as the back-end data store in a SpringBoot Application.
I have a requirement where I need to get all the entities whose name matches one of the names from a set of names.
Hence i am trying to get it implemented using a #Query configuration and a method named findAllByName(Iterable<String> names)as below:
Here on the Query, I am trying to use the 'IN' clause and want to pass an array of names as an input to the 'IN' clause.
#RepositoryConfig(cacheName = "Category")
public interface CategoryRepository extends IgniteRepository<Category, Long>
{
List<Category> findByName(String name);
#Query("SELECT * FROM Category WHERE name IN ( ? )")
Iterable<Category> findAllByName(Iterable<String> names); // this method always returns empty list .
}
In this the method findAllByName always returns empty list, even when ignite has Categories for which the name field matches the data passed in the query.
I am unable to figure out if there is a problem with the Syntax or the query of the method signature or the parameters.
Please try using String[] names instead for supplying parameters.
UPDATE: I have just checked the source, and we don't have tests for such scenario. It means that you're on uncharted territory even if it is somehow possible to get to work.
Otherwise looks unsupported currently.
I know your question is more specific to Spring Data Ignite feature. However, as an alternate, you can achieve it using the SqlQuery abstraction of Ignite.
You will form your query like this. I have pasted the sample below with custom sql function inSet that you will write. Also, the below tells how this is used in your sql.
IgniteCache<String, MyRecord> cache = this.ignite
.cache(this.environment.getProperty(Cache.CACHE_NAME));
String sql = "from “my-ignite-cache”.MyRecord WHERE
MyRecord.city=? AND inSet(?, MyRecord.flight)"
SqlQuery<String, MyRecord> sqlQuery = new SqlQuery<>(MyRecord.class,
sql);
sqlQuery.setArgs(MyCity, [Flight1, Flight2 ] );
QueryCursor<Entry<String, MyRecord>> resultCursor = cache.query(sqlQuery);
You can iterate the result cursor to do something meaningful from the extracted data.
resultCursor.forEach(e -> {
MyRecord record = e.getValue();
// do something with result
});
Below is the Ignite Custom Sql function which is used in the above Query - this will help in replicating the IN clause feature.
#QuerySqlFunction
public static boolean inSet(List<String> filterParamArgIds, String id) {
return filterParamArgIds.contains(id);
}
And finally, as a reference MyRecord referred above can be defined something like this.
public class MyRecord implements Serializable {
#QuerySqlField(name = "city", index = true)
private String city;
#QuerySqlField(name = "flight", index = true)
private String flight;
}
I have a field in my database with duplicates. I want to use it in a dropdown list, which has to return distinct data.
Here is the method that I created to do this:
public IEnumerable<SelectListItem> GetBranches(string username)
{
using (var objData = new BranchEntities())
{
IEnumerable<SelectListItem> objdataresult = objData.ABC_USER.Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
}).Distinct(new Reuseablecomp.SelectListItemComparer());
return objdataresult;
}
}
Here is the class I am using:
public static class Reuseablecomp
{
public class SelectListItemComparer : IEqualityComparer<SelectListItem>
{
public bool Equals(SelectListItem x, SelectListItem y)
{
return x.Text == y.Text && x.Value == y.Value;
}
public int GetHashCode(SelectListItem item)
{
int hashText = item.Text == null ? 0 : item.Text.GetHashCode();
int hashValue = item.Value == null ? 0 : item.Value.GetHashCode();
return hashText ^ hashValue;
}
}
}
Nothing is returned and I get the error below. When I try a basic query without Distinct, everything works fine.
{"The operation cannot be completed because the DbContext has been disposed."}
System.Exception {System.InvalidOperationException}
Inner exception = null
How can I return distinct data for my dropdown?
Technically, your problem can be solved simply by appending .ToList() after your Distinct(...) call. The problem is that queries are evaluated JIT (just in time). In other words, until the actual data the query represents is needed, the query is not actually sent to the database. Calling ToList is one such thing that requires the actual data, and therefore will cause the query to be evaluated immediately.
However, the root cause of your problem is that you are doing this within a using statement. When the method exits, the query has not yet been evaluated, but you have now disposed of your context. Therefore, when it comes time to actually evaluate that query, there's no context to do it with and you get that exception. You should really never use a database context in conjuction with using. It's just a recipe for disaster. Your context should ideally be request-scoped and you should use dependency injection to feed it to whatever objects or methods need it.
Also, for what it's worth, you can simply move your Distinct call to before your Select and you won't need a custom IEqualityComparer any more. For example:
var objdataresult = objData.ABC_USER.Distinct().Select(c => new SelectListItem
{
Value = c.BRANCH_CODE.ToString(),
Text = c.BRANCH_CODE
});
Order of ops does matter here. Calling Distinct first includes it as part of the query to the database, but calling it after, as you're doing, runs it on the in-memory collection, once evaluated. The latter requires, then, custom logic to determine what constitutes distinct items in an IEnumerable<SelectListItem>, which is obviously not necessary for the database query version.
In my Grails webapp I have the following domain classes:
class Customer {
static hasMany = [
addresses: Address
]
static mapping = {
addresses (cascade: "all-delete-orphan", joinTable: [name: "bs_core_customer_addresses"])
}
}
class Address {
...
}
Now I want to implement the abillity to filter the 1:n relation like addresses for things like country (should be dynamic, that means the user can add different filters by itself).
What is the best way to accomplish this?
Filtering the collection from the Customer object? e.g. customer.addresses.findAll{...}
Direct query from the database? How can I add the restriction for the Customer<->Address relation. belongsTo at the Address domain class is no option because the Address object is used in several 1:n relations. e.g. Customer.findAll(...)
Any other option?
you should be able to get away with
static constraints = {
addresses(validator: checkAddress)
}
// This is a static method which is used for validation
// and can be used for when inserting a record to check how many
// existing addresses exist for the end user that has countryCode of US
// This is directly bound to all the object the user and will
// will not be searching the entire DB (A local find limited to user records)
static def checkAddress={val,obj,errors->
if (!obj?.addresses.findAll{it.countryCode=='US'}?.size() >= 2) {
return errors.rejectValue('adress','exceeds limit')
}
}
The above should be self explanatory, but having read through your post a few times now I think I have a better understanding of what you are trying to achieve and there are probably a few different ways of doing it. So let's explore some of them:
Using HQL query, you could change this to another method, I prefer HQL.
class Customer {
def userService
//UserAddress does not have setter transients not essential
static transients = ['userAddress','userService']
//This is a protected function that will return an address
// object given a countryCode
// Execute like this:
// Customer cm = Customer.get(customer.id as Long)
//Address usa = cm.getCountry('US')
protected Address getUserAddress(String countryCode) {
return userService.findAddress(countryCode, this.id)
}
}
Now the service but actually you don't need to execute in domain class unless there is some other need, for displaying etc you could always call this sort of service from within a controller call to render for display purposes
class UserSerice {
// This should return the entire address object back to the domain class
// that called it and will be quicker more efficient than findAll
Address findAddress(String countryCode, Long customerId) {
String query="""
select address from Address a
where a.id :id and countryCode = :code
"""
def inputParams=[code:countryCode, id:customerId]
return Address.executeQuery(query,inputParams,[readOnly:true,timeout:15])
}
Another approach could be a 3rd table that gets updated upon each address added that would give a quick lookup:
class Customer {
static hasMany = [
addresses: Address
//probably don't even need this
//, userCountries:UserCountries
]
}
Class UserCountries {
// register customer
Customer customer
String CountryCode
//maybe address object or Long addressId - depending on if how plain you wanted this to be
Address address
}
Then register the address id and countryCode to this domainclass each time you add a new address and I guess you would need to write some backward compatible code to add existing records to this table for it to work properly.
I left a comment and then removed it for you to expand further on what or how the filtering was taking place. since although you talk of countryCode there is no actual code to show how it all fits in.
I still think something as simple as this would work
//This would only be doing a find with all the bound objects of addresses bound to this customer. so a find within the hasMany relationship elements of this specific customer
protected def getCustomAddress(String countryCode) {
return addresses.findAll{it.code==countryCode}
}
Other far out ideas could be something like this
class Customer {
String _bindAddress
List bindAddress=[]
static transients = [ 'bindAddress' ]
static constraints = {
_bindAddress(nullable:true)
}
//you store a flat CSV as _bindAddress
//you need to work out your own logic to ammend to existing CSV each time address is added
// you will also update _bindAddress of this domainClass each time customer gets a hasMany address added
// so no need for setBindAddress
// void setBindAddress(String b) {
// bindAddress=b.split(',')
// }
//Inorder to set a list back to flat file
//pass in list object
void setBindAddress(List bindAddress) {
_bindAddress=bindAddress.join(',')
/for 1 element this captures better
//_bindAddress=bindAddress.tokenize(',').collect{it}
}
//This is now your object as a list that you can query for what you are querying.
List getBindAdress() {
return _bindAddress.split(',')
}
}
If your actual csv list contained a listing of 'COUNTRY_CODE-ADDRESS_ID' then you could query like this
def found = customer.bindAddress.find{it.startsWith('US-')}
Address usaAddress= Address.get(found.split('-')[1] as Long)
//Slightly longer explaining above:
def found = customer.bindAddress.find{it.startsWith('US-')}
def record = found.split('-')
String countryCode=record[0]
Long addressId=record[1] as Long
Address usaAddress= Address.get(addressId)
I am developing a site in which nhibernate is using. that is working fine for static mapping. but problem that i apply this application on existing database. so is there any way that mapping of classes took place at run time. i mean user provide tables and column names for mapping. Thanks
From your question I interpret you saying that the POCO classes exists, but you don't know the table or column names at build time.
So, if you already had this class:
public class MyGenericClass
{
public virtual long Id { get; set; }
public virtual string Title { get; set; }
}
You could bind it to a table and columns at runtime:
string tableName; // Set somewhere else by user input
string idColumnName; // Set somewhere else by user input
string titleColumnName; // Set somewhere else by user input
var configuration = new NHibernate.Cfg.Configuration();
configuration.Configure();
var mapper = new NHibernate.Mapping.ByCode.ModelMapper();
mapper.Class<MyGenericClass>(
classMapper =>
{
classMapper.Table(tableName);
classMapper.Id(
myGenericClass => myGenericClass.Id,
idMapper =>
{
idMapper.Column(idColumnName);
idMapper.Generator(Generators.Identity);
}
);
classMapper.Property(c => c.Title,
propertyMapper =>
{
propertyMapper.Column(titleColumnName);
}
);
}
);
ISessionFactory sessionFactory = configuration.BuildSessionFactory();
ISession session = sessionFactory.OpenSession();
////////////////////////////////////////////////////////////////////
// Now we can run an SQL query over this newly specified table
//
List<MyGenericClass> items = session.QueryOver<MyGenericClass>().List();
I don't think that could be possibly with NHibernate, but you could use a workaround.
You could use a view instead a table for the NHibernate mapping.
And in runtime, you could create that View or update it with the especified user mapping you need.
For example, you define a mapping in NHibernate to a view named ViewMapped with two columns Name and Mail.
And in the other hand, the user has a table with three columns Name, SecondName, EMail.
you can create a view on runtime with the following select:
(SELECT Name + ' ' + SecondName as Name, EMail as Mail FROM tableName) AS ViewMapped
I hope that helps you, or at least leads you to a solution.
I have a very strange issue that I cannot explain. I have my base mapping with this
//This will automatically cast the row into the correct object type based on the value in AccountType
DiscriminateSubClassesOnColumn<string>("AccountType")
.Formula(String.Format("CASE AccountType WHEN {0} THEN '{1}' WHEN {2} THEN '{3}' ELSE '{4}' END",
(int)PaymentMethodType.CheckingAccount,
typeof(ACH).Name,
(int)PaymentMethodType.SavingsAccount,
typeof(ACH).Name,
typeof(CreditCard).Name));
I have looked in the logs, I have executed the sql that nhibernate is generating, and all records have the same data. There is not difference in them that would denote why this should not work.
The base class is PaymentMethodBase. I have 2 subclasses, CreditCard and ACH, which inherit from PaymentMethodBase.
Then, I have this extension
public static string PaymentMethodName(this PaymentMethodBase paymentMethod)
{
if (paymentMethod is ACH)
{
var ach = (ACH)paymentMethod;
return String.Format("{0} {1}", ach.BankName, String.Format("XXXX{0}", ach.AccountNumber.Substring(ach.AccountNumber.Length - 4)));
}
if (paymentMethod is CreditCard)
{
var creditCard = (CreditCard)paymentMethod;
return String.Format("{0} {1}", creditCard.Name, creditCard.CreditCardNumber);
}
return "Unknown Payment Method";
}
Which I call like this.
public SelectList PaymentMethodsSelectList
{
get
{
var methods = (from p in PaymentMethods
where p != null
select new
{
id = p.PaymentMethodId,
name = p.PaymentMethodName()
}).OrderBy(x => x.name);
var results = methods.ToList();
results.Insert(0, new { id = (int)NewPaymentMethods.ACH, name = "<New eCheck Account...>" });
results.Insert(0, new { id = (int)NewPaymentMethods.CreditCard, name = "<New Credit Card...>" });
return new SelectList(results, "id", "name");
}
}
This code is used by 2 models. The collection of payment methods are all coming from the same object - a customer object. The collection is mapped like this.
HasMany<PaymentMethodBase>(x => x.PaymentMethods)
.KeyColumn("CustomerId")
.Where(y => y.AccountType < 10)
.Inverse()
.Cascade.All();
So, I get the customer 2 different ways. One is that I get the customer through another object (main site). The other has the object being pulled directly by id (in an iframe). The direct by id method works every time. The other method, where I get the customer through another object, causes only the first and last payment method to cast correctly. If there are more than 2, they are left in the base class and appear in the middle of the list after the sort.
I have tried changing the parent object to just mapping the id and then getting the customer record by the ID. Failure. There is something else in the way that is causing this to happen, but only on that one model.
I suspect that the issue here is Fetching issue.
Since the extension method is running on the .Net side rather than in the "NHibernate level" it cannot run properly when the collection of payment methods is not available.
Perhaps in the direct by Id methos you have Fetching set up for the payments whereas in the indirect method the automatic fetching goes only to the Customer object but stops short before fetching the Payment methods.
Try to instruct NHibernate to pre-fetch the Payment methods for you in the indirect method.
Something like:
Session.Query<SomeObject>.Where(.....).Fetch(x => x.Customer).ThenFetch(c => c.PaymentMethods)
The problem here appeared to be the usage of extension methods within a linq statement to return a value based on the type. The issue is that it would work some places but not others, probably due to some fetching issue, as suggested by Variant.
I solved this problem by making a property in my base class like this
public virtual string DisplayName { get { return "Unknown"; } }
Then I overrode the property in my child class and added the logic that was in the extension method for that type.
public override string DisplayName { get { return String.Format("{0} {1}", Name, AccountMask); } }