I can query docs using views just fine, but switching to N1QL gives me Success property as false. What went wrong ?
let cluster = new Cluster()
let bucket = cluster.OpenBucket("mydoc","")
let query = """SELECT * FROM mydoc where SET = 'SET24MM2SCLV01'"""
let result = bucket.Query(query)
Console.WriteLine(result.Success) //would give false
SET is a reserved word in N1QL. In order to use it as an identifier, you need to escape it with backticks, eg:
SELECT * FROM mydoc where `SET` = 'SET24MM2SCLV01'
If you don't you'll get a syntax error:
"errors": [
{
"code": 3000,
"msg": "syntax error - at SET"
}
]
You should change your query to
let query = """SELECT * FROM mydoc where SET = 'SET24MM2SCLV01'"""
let query = "SELECT * FROM mydoc where `SET` = 'SET24MM2SCLV01'"
EDIT
The query result also contains an Error property with the errors that occured in the query. This should be checked always if Success returns false. If the query fails even after escaping SET, this will explain what other error prevents the query from running.
For example, I just noticed that the entire query is enclosed in double quotes. This would send a string literal to the server instead of a query.
Related
I understand that stored procedures run in the scope of a single partition key.
It is also possible to do operations that change data, not just read it.
ID must be string, so I must roll my own autoincrementer for a separate property to use in documents.
I am trying to make a simple autoincrement number generator that runs in a single stored procedure.
I am partitioning data mimicking a file tree, using forward slashes to separate+concatenate significant bits that make my partition names. Like so:
/sometype/foo/bar/
/sometype/ids/
The first item is always the document type, and every document type will have a 'ids' sub-partition.
Instead of holding documents, the /sometype/ids/ partition will hold and reserve all numerical ids that have been created for this document type, for autoincrement purposes.
this satisfies uniqueness within a partition, stored procedure execution scope, and unique document count within a document type, which is good for my purposes.
I got stumped in a stored procedure where I want to get a specified id, or create it if it does not exist.
I can query my partition with the stored procedure, but the upsert throws an error, using the same partition key.
I designed my database with "pkey" as the name of the property that will holds my partition keys.
Here is the code:
//this stored procedure is always called from a partition of type /<sometype>/ids/ , where <sometype> os one of my document types.
//the /sometype/ids/ is a partition to reserve unique numerical ids, as Cosmos DB does not have a numerical increment out of the box, I am creating a facility for that.
//the actual documents of /sometype/ will be subpartitioned as well for performance.
function getId(opkey, n, id) {
// gets the requested number if available, or next one.
//opkey: string - a partition key of cosmos db of the object that is going to consume the generated ID, if known. must start with /<sometype>/ which is the same that is being used to call this SP
//n: integer - a numerical number for the autoincrement
//id = '' : string - a uuid of the document that is using this id, if known
if (opkey === undefined) throw new Error('opkey cannot be null. must be a string. must be a valid partition key on Cosmos DB.');
n = (n === undefined || n === null)?0:n;
id = (id === undefined || id === null)?'':id;
var collection = getContext().getCollection();
//make opkey parameter into an array
var split_pkey = opkey.split('/');
//recreate the pkey /<sometype>/ids/ because I can't find a reference to this string inside the context.
var idpkey = '/'+split_pkey[1]+'/ids/';
//first query as SQL
//get highest numerical value.
var q = 'SELECT TOP 1 * FROM c \
WHERE c.pkey = \''+idpkey+'\' ORDER BY c.n desc';
//helper function to create uuids. can I ditch it?
function CreateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
q
,
function (firstError, feed, options) {
if (firstError) throw "firstError:"+firstError;
//console.log(collection.options.);
console.log(idpkey+', '+n+', '+id+"-");
var maxn = 0;
// take 1st element from feed
if (!feed || !feed.length) {
//var response = getContext().getResponse();
//response.setBody(null);
}
else {
maxn = feed[0].n;
//var response = getContext().getResponse();
//var body = { original: '', document: '', feed: feed[0] };
//response.setBody(JSON.stringify(body));
}
console.log(maxn);
//query for existing numerical value
q = 'SELECT TOP 1 * FROM c \
WHERE c.pkey = \''+idpkey+'\' \
AND \
c.number = '+n+' \
OR \
c.id = \''+id+'\'';
var isAccepted2 = collection.queryDocuments(
collection.getSelfLink(),
q
,
function (secondFetchError, feed2, options2) {
if (secondFetchError) throw "second error:"+secondFetchError;
//if no numerical value found, create a new (autoincrement)
if (!feed || !feed.length) {
console.log("|"+idpkey);
var uuid = CreateUUID();
var newid = {
id:uuid,
pkey:idpkey,
doc_pkey:opkey,
n:maxn+1
};
//here I used the javascript query api
//it throws an error claiming the primary key is different and I don't know why, I am using idpkey all the time
var isAccepted3 = collection.upsertDocument(
collection.getSelfLink(),
newid
,
function (upsertError,feed3,options3){
if (upsertError) throw "upsert error:"+upsertError;
//if (upsertError) console.log("upsert error:|"+idpkey+"|");
//var response = getContext().getResponse();
//response.setBody(feed[0]);
});
if (!isAccepted3) throw new Error('The third query was not accepted by the server.');
console.log(" - "+uuid);
}
else {
//if id found, return it
//maxn = feed[0].n;
var response = getContext().getResponse();
response.setBody(feed[0]);
//var body = { original: '', document: '', feed: feed[0] };
//response.setBody(JSON.stringify(body));
}
});
if (!isAccepted2) throw new Error('The second query was not accepted by the server.');
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
The error message is :
"Requests originating from scripts cannot reference partition keys other than the one for which client request was submitted."
I don't understand why it thinks it is in error, as I am using the variable idpkey in all queries to hold the correct pkey.
Talk about brain fart!
I was violating my own rules because I was misspelling the partition name in the request, making the first part of the partition key /sometype/ different from the parameter sent, causing a mismatch between the execution scope's partition key and the idpkey variable, resulting in the error.
The Cypher official documentation seems to be inconsistent and misleading when it comes to assigning parameter values..we have the following examples in the refcard:
SET n.property1 = $value1
where value1 is a parameter defined
{
"value1": somevalue
}
However if you use this SET syntax you get an error value1 not defined. The correct syntax seems to be:
SET n.property1 = {value1}
also if its a MATCH query the parameter looks like this:
{
email: someemail
}
note no quotes around email
So if you have a MATCH and SET query with parameters you parameters definition looks like this:
{
email: someemail,
"status" : somestatus
}
Can someone explain this apparent inconsistency?
EDIT:
This is also an example from the neo4j docs:
using parameter with SET clause:
{
"surname" : "Taylor"
}
MATCH (n { name: 'Andres' })
SET n.surname = $surname
RETURN n
This returns surname undefined.
You can set params in neo4j desktop browser using :params.
For eg,
Execute, :params {surname: 'Taylor'}
Execute, MATCH (n { name: 'Andres' }) SET n.surname = $surname RETURN n
For more info of params, use :help params
How do I do a query expression similar to a SQL IN-query?
I'm trying to do something along these lines:
let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query {
for c in dataContext.Customers do
where(customerNumbers.Contains(c.CustomerNumber))
select c
}
But I'm getting an error:
System.NotSupportedException: Method 'Boolean Contains(System.String)' has no supported translation to SQL.
Looking at the documentation for query expressions at http://msdn.microsoft.com/en-us/library/hh225374.aspx I should use another query for the contains part but this code doesn't work, the example is broken:
// Select students where studentID is one of a given list.
let idQuery = query { for id in [1; 2; 5; 10] do select id }
query {
for student in db.Student do
where (idQuery.Contains(student.StudentID))
select student
}
idQuery does in fact not contain any "Contains" method.
I have also tried:
let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query {
for c in dataContext.Customers do
where (query { for x in customerNumbers do exists (c.CustomerNumber=x)})
select r
}
But this gives this error message:
System.NotSupportedException: Local sequence cannot be used in LINQ to SQL implementations of query operators except the Contains operator
I noticed after some more testing that the following also works fine in addition to Gene's suggestion:
let customerNumbers = set ["12345"; "23456"; "3456"]
query {
for customer in dataContext.Customer do
where (query { for x in customerNumbers do contains customer.CustomerNumber})
select customer
}
The problem I believe comes from the way F# Set implements method Contains. It belongs to ICollection interface and this fact somehow upsets LINQ-to-SQL query builder.
If you explicitly force your Contains into the extension method of IEnumerable territory everything gets OK:
let customerNumbers = set ["12345"; "23456"; "3456"]
let customerQuery = query {
for c in dataContext.Customers do
where((customerNumbers :> IEnumerable<string>).Contains(c.CustomerNumber))
select c
}
Or, equivalently, you can add non-LINQ-to-SQL query
let idQuery = query { for id in customerNumbers do select id }
which has no problems with enumerating set yielding seq<string> and then use it for Contains as
....
where (idQuery.Contains(c.CustomerNumber))
....
Or, to begin with, you may keep your customerNumbers as seq:
let customerNumbers = set ["12345"; "23456"; "3456"] |> Set.toSeq
and use it as intuition prompts:
....
where(customerNumbers.Contains(c.CustomerNumber))
....
I am trying to get a path from a base node to its root node as 1 row. The Cypher query looks like this:
start n = node:node_auto_index(Name = "user1") match path = (n-[r:IS_MEMBER_OF_GROUP*]->b) return last(collect(distinct path));
But when changing this over to the Neo4JClient syntax:
var k = clientConnection.Cypher
.Start(new { n = "node:node_auto_index(Name = 'user1')" })
.Match("path = (n-[r:IS_MEMBER_OF_GROUP*]->b)")
.ReturnDistinct<Node<Principles>>("last(collect(path))").Results;
It gets an error:
{"Value cannot be null.\r\nParameter name: uriString"}
When continuing on from there:
Neo4jClient encountered an exception while deserializing the response from the server. This is likely a bug in Neo4jClient.
Please open an issue at https://bitbucket.org/Readify/neo4jclient/issues/new
To get a reply, and track your issue, ensure you are logged in on BitBucket before submitting.
Include the full text of this exception, including this message, the stack trace, and all of the inner exception details.
Include the full type definition of Neo4jClient.Node`1[[IQS_Neo4j_TestGraph.Nodes.Principles, IQS Neo4j TestGraph, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
Include this raw JSON, with any sensitive values replaced with non-sensitive equivalents:
{
"columns" : [ "last(collect(path))" ],
"data" : [ [ {
"start" : "http://localhost:7474/db/data/node/3907",
"nodes" : [ "http://localhost:7474/db/data/node/3907", "http://localhost:7474/db/data/node/3906", "http://localhost:7474/db/data/node/3905", "http://localhost:7474/db/data/node/3904" ],
"length" : 3,
"relationships" : [ "http://localhost:7474/db/data/relationship/4761", "http://localhost:7474/db/data/relationship/4762", "http://localhost:7474/db/data/relationship/4763" ],
"end" : "http://localhost:7474/db/data/node/3904"
} ] ]
}
How would one convert the cypher query to Neo4JClient query?
Well, the thing you are returning is a PathsResult not a Node<>, so if you change your query to be:
var k = clientConnection.Cypher
.Start(new { n = "node:node_auto_index(Name = 'user1')" })
.Match("path = (n-[r:IS_MEMBER_OF_GROUP*]->b)")
.ReturnDistinct<PathsResult>("last(collect(path))").Results; //<-- Change here
you will get results, this returns what I get from running your query against my db, if you specifically want the nodes this post: Getting PathsResults covers converting to actual nodes and relationships.
One other thing, (and this will help the query perform better as neo4j can cache the execution plans easier), is that you can change your start to make it use parameters by doing:
.Start(new { n = Node.ByIndexLookup("node_auto_index", "Name", "user1")})
My URLs are as follows:
{controller}/{action}/{id}
In this example, it is:
Blog/Edit/2
In my code, I am trying to get the ID parameter (which is "2") like so:
' get route
Dim routeData = httpContext.Request.RequestContext.RouteData
' get id
Dim id = If(String.IsNullOrEmpty(routeData.Values("id") = False), routeData.Values("id").ToString, Nothing)
However, it is saying the value is empty. The following statement returns true for some reason:
If String.IsNullOrEmpty(id) = True Then
How can I get the value of the ID so it isn't NULL (or "Nothing" in VB.NET)?
The solution was to change how I was using the IsNullorEmpty method:
' get id
Dim id = If(String.IsNullOrEmpty(routeData.Values("id")), Nothing, routeData.Values("id").ToString)
' if no id is set, check to see if the user owns the requested entity (company or blog)
If String.IsNullOrEmpty(id) Then
It wasn't returning nothing.
Your original post reads String.IsNullOrEmpty(routeData.Values("id") = False) - your closing bracket is in the wrong place, and so the String.IsNullOrEmpty will always return false. You should instead have written String.IsNullOrEmpty(routeData.Values("id")) = False.
(In VB, "xyz" = false will convert implicitly to "False".)