I have a working cypher query but can't get it to work in Neo4jclient.
My cypher query is
start n=node(*) where has(n.Name) and n.Name =~ 'X.*' return n;
which returns all nodes with a Name property which starts with X.
I am on purpose not using indices or relations here.
My first childish attempt (not even using regex I plan to use) fails with timeout(!) on res.Results
var res = _client.RootNode
.StartCypher("n")
.Where<Meeting>(m => m.Name == "X")
.Return<Meeting>("m");
Try:
var query = _client.Cypher
.Start("n", graphClient.RootNode)
.Where("has(n.Name)")
.And()
.Where("n.Name =~ 'X.*'")
.Return<Meeting>("n");
This worked on my machine, you'll get the results like:
var results = query.Results;
Edit:
I think I've realised why yours had problems, in the beginning, you put StartCypher("n") and subsequently, use m instead of n. So Return<Meeting>("m") should be Return<Meeting>("n")
Related
I'm using tdbc::odbc to connect to a Pervasive (btrieve type) database, and am unable to pass variables to the driver. A short test snippet:
set customer "100000"
set st [pvdb prepare {
INSERT INTO CUSTOMER_TEMP_EMPTY
SELECT * FROM CUSTOMER_MASTER
WHERE CUSTOMER = :customer
}]
$st execute
This returns:
[Pervasive][ODBC Client Interface]Parameter number out of range.
(binding the 'customer' parameter)
Works fine if I replace :customer with "100000", and I have tried using a variable with $, #, wrapping in apostrophes, quotes, braces. I believe that tdbc::tokenize is the answer I'm looking for, but the man page gives no useful information on its use. I've experimented with tokenize with no progress at all. Can anyone comment on this?
The tdbc::tokenize command is a helper for writing TDBC drivers. It's used for working out what bound variables are inside an SQL string so the binding map can be supplied to the low level driver or, in the case of particularly stupid drivers, string substitutions performed (I hope there's no drivers that need to do this; it'd be annoyingly difficult to get right). The parser knows enough to handle weird cases like things that look like bound variables in strings and comments (those aren't bound variables).
If we feed it (it's calling syntax is trivial) the example SQL you've got, we get this result:
{
INSERT INTO CUSTOMER_TEMP_EMPTY
SELECT * FROM CUSTOMER_MASTER
WHERE CUSTOMER = } :customer {
}
That's a list of three items (the last element just has a newline in it) that simplifies processing a lot; each item is either trivially a bound variable or trivially not.
Other examples (bear in mind in the second case that bound variables may also start with $ or #):
% tdbc::tokenize {':abc' = :abc = ":abc" -- :abc}
{':abc' = } :abc { = ":abc" -- :abc}
% tdbc::tokenize {foo + $bar - #grill}
{foo + } {$bar} { - } #grill
% tdbc::tokenize {foo + :bar + [:grill]}
{foo + } :bar { + [:grill]}
Note that the tokenizer does not fully understand SQL! It makes no attempt to parse the other bits; it's just looking for what is a bound variable.
I've no idea what use the tokenizer could be to you if you're not writing a DB driver.
Still could not get the driver to accept the variable, but looking at your first example of the tokenized return, I came up with:
set customer "100000"
set v [tdbc::tokenize "$customer"]
set query "INSERT INTO CUSTOMER_TEMP_EMPTY SELECT * FROM CUSTOMER_MASTER WHERE CUSTOMER = $v"
set st [pvdb prepare $query]
$st execute
as a test command, and it did indeed successfully pass the command through the driver
I have to match records in SQL around a zip code with a min/max range
the challenge is that the data qualities are bad, some zipcodes are not numbers only
so I try to match "good zip codes" either by discarding bad ones or even keeping only digits
I dont know how to use Regex.Replace(..., #"[^\d]", "") instead of Regex.Match(..., #"\d") to fit in the query bellow
I get an error with the code bellow at runtime
I tried
Regex.IsMatch
SqlFunctions.IsNumeric
they all cause errors at runtime, here is the code :
var data = context.Leads.AsQueryable();
data = data.Include(p => p.Company).Include(p => p.Contact);
data = data.Where(p => Regex.IsMatch(p.Company.ZipCode, #"\d"));
data = data.Where(p => Convert.ToInt32(p.Company.ZipCode) >= range.Min);
data = data.Where(p => Convert.ToInt32(p.Company.ZipCode) <= range.Max);
here is the error :
System.InvalidOperationException: The LINQ expression 'DbSet<Lead>
.Join(
outer: DbSet<Company>,
inner: l => EF.Property<Nullable<int>>(l, "CompanyId"),
outerKeySelector: c => EF.Property<Nullable<int>>(c, "Id"),
innerKeySelector: (o, i) => new TransparentIdentifier<Lead, Company>(
Outer = o,
Inner = i
))
.Where(l => !(Regex.IsMatch(
input: l.Inner.ZipCode,
pattern: "\d")))' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
I am not sure how to solve this. I really don't see how AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync() could help here
what do I do wrong ?
thanks for your help
Wnen you use querable list, Ef Core 5 is always trying to translate query to SQl, so you have to use code that SQL server could understand. If you want to use C# function you will have to download data to Server using ToList() or ToArray() at first and after this you can use any c# functions using downloaded data.
You can try something like this:
var data = context.Leads
.Include(p => p.Company)
.Include(p => p.Contact)
.Where(p =>
p.Company.Zipcode.All(char.IsDigit)
&& (Convert.ToInt32(p.Company.ZipCode) >= range.Min) //or >=1
&& ( Convert.ToInt32(p.Company.ZipCode) <= range.Max) ) // or <=99999
.ToArray();
I tried everything imaginable
all sorts of linq/ef trickeries, I even tried to define a DBFunction that was never found
once I had a running stored procedure written dirrectly in SQL, I ended up with a list, not with an IQueryable, so I was back to #1
finaly, I just created a new field in my table :
ZipCodeNum
which holds a filtered , converted version of the zipcode string
I come up with solution to programmaticlly create query to search for phrase with wildcards using this code:
public static Query createPhraseQuery(String[] phraseWords, String field) {
SpanQuery[] queryParts = new SpanQuery[phraseWords.length];
for (int i = 0; i < phraseWords.length; i++) {
WildcardQuery wildQuery = new WildcardQuery(new Term(field, phraseWords[i]));
queryParts[i] = new SpanMultiTermQueryWrapper<WildcardQuery>(wildQuery);
}
return new SpanNearQuery(queryParts, //words
0, //max distance
true //exact order
);
}
Example creation and call toString() method will output:
String[] phraseWords = new String[]{"foo*", "b*r"};
Query phraseQuery = createPhraseQuery(phraseWords, "text");
System.out.println(phraseQuery.toString());
outputs:
spanNear([SpanMultiTermQueryWrapper(text:foo*), SpanMultiTermQueryWrapper(text:b*r)], 0, true)
Which works great, and fast enough for most cases. For instance, if I create such query and search with it, It will output desired results, for example:
Sentence with foo bar.
Foolies beer drinkers.
...
And not something like:
Bar fooes.
Foo has bar.
I have mentioned that query work fast enough in most cases. Currently I have an index with size of aprox. 200GB and on average searching time is between 0.1 to 3 seconds. Depending on many factors like: cache, size of subsets of documents matching single word in phrase since lucene will perform set intersections between founded terms.
Example:
Let supose I want to query phrase "an* karenjin*" (which I will split into ["an*", "karenjin*"] and than create query using createPhraseQuery method) and I want that it matches sentences containing: "ana karenjina", "ani karenjinoj", "ane karenjine", ... (different cases due croatian grammar).
This query is very slow that I haven't waited long enough to get results (over 1h) and sometimes causes GC overhead limit exceeded exception.
This behaviour is somewhat expected since "an*" itself matches a huge number of documents. I am aware of that I could query "an? karanjin*" which giver results in 30-40sec (faster but still slow).
This is where I am confused.
If I query just "karenjin*" it gives results in 1 sec. Therefore I have tried to query "an* karenjin*" and using a Filter "karenjin*" using WildcardQuery and QueryWrapperFilter. And it is still unacceptable slow (I killed process before it returned anythong).
Documentation says that Filter reduces search space of Query. So I tried to use filter:
Filter filter = new QueryWrapperFilter(new WildcardQuery(new Term("text", "karanjin*")));
And query:
Query query = createPhraseQuery(new String[]{"an*", "karenjin*"}, "text");
Than search, (after several warm-up queries):
Sort sort = new Sort(new SortField("insertTime", SortField.Type.STRING, true));
TopDocs docs = searcher.search(query, filter, 100, sort);
OK, what is my question?
How come is quering:
Query query = new WildcardQuery(new Term("text", "karanjin*"));
is fast, but using Filter described above is still slow?
Yes, wildcards can be performance hogs, especially if they match a lot of terms, but what you describe does seem surprisingly so. Hard to say for sure why that is occuring, but for an attempt.
I'll assume:
Query query = new WildcardQuery(new Term("text", "an*"));
On it's own, is performing very badly, as described. Since the wildcards you are looking for are both prefix style queries, it's a better idea to use a PrefixQuery instead.
Query query = new PrefixQuery(new Term("text", "an"));
Though I don't think that will make much of a difference if any at all. What might just make a different is changing you rewrite method. You could try limiting the number of Terms the query is rewritten into:
Query query = new PrefixQuery(new Term("text", "an"));
//or
//Query query = new WildcardQuery(new Term("text", "an*"));
query.setRewriteMethod(new MultiTermQuery.RewriteMethod.TopTermsRewrite(10));
I have a Neo4j database graphDb where nodes have a property 'label'. I have a Lucene index 'my_nodes' with key 'label' which indexes the values of node property 'label'. Now I want to retrieve nodes which have property 'label' equal to a value from a list of possible values labellist. To accomplish this, I wrote a Cypher query the following way:
cypherQ = """START n=node:my_nodes('"""
+' OR '.join(['label:'+str(i) for i in labellist]) + """')
RETURN n"""
result = graphDb.query(cypherQ)
That works fine, but I wonder whether there is a way to write a parameterized query anyhow?
I tried something like:
cypherQ = """START n=node:my_nodes('label:{params}')
RETURN n"""
result = graphDb.query(cypherQ, params = labellist)
But this surely does not work, though if there is one value in labellist it works. And the neo4j tutorial does not provide much material on this issue.
Once again I am using a python binding for Neo4j.
The parameter is working for the whole query part of the index, so this would be
cypherQ = """START n=node:my_nodes({queryParam})
RETURN n"""
and you construct the query in your client code and pass it into Cypher as one parameter.
I have tried to find an answer to this, but could not find one in google. Probably not searching the correct terms, so thought I would ask here.
The following returns all my contacts, not the ones that equal the adjusterType sent in.
var contacts = from c in session.Linq<Contact>() select c;
contacts.Where(c => c.ContactAdjuster.AdjusterType == adjusterType);
The following does return the expected results. It does return only the contacts that meet the adjusterType. I believe it is my lack of understanding of LINQ.
var contacts = from c in session.Linq<Contact>() select c;
contacts = contacts.Where(c => c.ContactAdjuster.AdjusterType == adjusterType);
Thanks in advance.
the Where clause returns an IEnumerable in your case an IEnumerable. This is the standard LiNQ and C# behavior. Instead of modifying your collection it is returning a new collection based on your where clause.
I suppose NHibernate LiNQ should mimic this.
CatZ is absolutely right, you are not modifying the "contacts" collection/enumerable you are creating a new based on the existing, which is why your second statement works.
But instead of just repeating CatZ statement, here is a little add-on:
You can write this in one statement though
var contacts =
from c in session.Linq<Contact>()
where c.ContactAdjuster.AdjusterType == adjusterType
select c;
Or simply
var contacts = session.Linq<Contact>().Where(c => c.ContactAdjuster.AdjusterType == adjusterType);