OrientDB Version: 3.2.6
I wish to create an OrientDB server-side function which zips two arrays into an object, but also format the values (which are always record ids) slightly.
Function name:
idToRidMap
Takes args:
keys - An array of strings representing the keys of the resulting object, Example: ["id1", "id2"]
values - An array of record ids, Example: [#15:1, #15:2]
Returns an object with the id array elements as keys and the record ids as string values without leading "#", i.e. from examples above it would be:
{
"id1": "15:1",
"id2": "15:2"
}
I wish to use this function in queries like this one:
SELECT idToRidMap(configIds, configRids) as configs, * FROM (
SELECT outE("HasConfig").id as configIds, out("HasConfig").#rid as configRids, * FROM 12:0
)
So, depending on the id of the linking Edges and the record-id of the linked Vertices I wish to build one property showing all those relations in the returned record:
{
"#rid": "#12:0",
"someNativeProp": "Hello",
"configs": {
"id1": "15:1",
"id2": "15:2"
},
...
}
Note though, that this would also require me to drop the projections for the intermediate array results as well, extending the query to be something like this:
SELECT idToRidMap(configIds, configRids) as configs, !configIds, !configRids, * FROM (
SELECT outE("HasConfig").id as configIds, out("HasConfig").#rid as configRids, * FROM 12:0
)
The OrientDB JS function definition I've tried (among many others) is:
var result = {};
for (i = 0; i < keys.length; i++) {
result[keys[i]] = String(values[i]).replace('#', '');
}
return result;
But I realized that length is not available (it is undefined) on the keys argument. When testing by using keys.size() (guessing it was java.util.arraylist) I was given an error:
com.orientechnologies.orient.core.command.script.OCommandScriptException: Error on parsing script at position #0: Error on execution of the script Script: idToRidMap ------^ DB name="test" --> javax.script.ScriptException: org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (size) on java.util.ArrayList#1b395482 failed due to: Unknown identifier: size --> org.graalvm.polyglot.PolyglotException: TypeError: invokeMember (size) on java.util.ArrayList#1b395482 failed due to: Unknown identifier: size
Which seems to indicate that it has something to do with graalvm polyglot and that it indeed has to do with java.util.ArrayList. I did check https://www.graalvm.org/22.1/reference-manual/js/JavaInteroperability/#access-java-from-javascript but I'm not sure how relevant it is and I didn't find anything that helped me.
So, to sum up. Basically there's two parts to this question:
Is there any documentation of how the JavaScript server-side functions work type-wise and syntax-wise, etc? It seems really picky to what kind of JavaScript I write as well. How can I do the desired iterations to implement my function?
Is there a better way of achieving my end goal?
Thankful for any insight, I've always had a hard time with OrientDB custom functions.
Related
I have a set of functions at the server side which each return a list of objects of the same type based on the passed parameters to the resolvers in the GraphQL query-
query {
objListQuery {
objResolver1(params) {
obj-id
}
objResolver2(different params) {
obj-id
}
...
}
}
Here, objResolver1 and objResolver2 send back a list of obj objects.
Server side -
function objResolver1(params) -> returns list of obj
function objResolver2(different params) -> returns list of obj
...
I want to perform a logical AND between the results of the resolvers that is, find out the common objects in the results of the different resolvers.
Instead of getting the individual lists, I only want the combined list.
One way is to aggregate the results at the client side but this will increase the amount of duplicated data sent by the server.
What is the best way to achieve this at the server side? What changes are required in the schema?
--------------------EDIT--------------------
The data source is a JSON array of obj objects which is obtained from an external service at the server. Data source is not a database.
Parameters in each resolver can be one or many. It is used for filtering the objects. For example, the data store will have the structure as:
[
{"dateCreated":"2011-08-12T20:17:46.384Z",
"type":"customer",
....
},
{"dateCreated":"2011-08-14T20:17:46.384Z",
"type":"test",
....
}
]
resolvers will be of the form:
dateResolver(String startDate, String endDate) -> returns list of obj whose dateCreated is within the range
typeResolver(String[] type) -> returns list of obj whose type is anyone of the values passed in the array.
Assumed you're using a database you're somehow asking how to shift constraints from database- or repository-layer on controller-level.
While this has some weakness on model-level perhaps, it might depend on the class-implementation if you can easily change the objResolver in the kind that you just build one that allows more parameters like this:
query {
objListQuery {
objResolver(params1, params2, constraint) {
...
}
}
}
Like this you could create a database-query that is directly fetching the right result or you can perform several queries and resolve them inside the objResolver. If the constraint is always AND you could leave the parameter away, but perhaps you like to offer the possibility to use also OR, XOR, or others.
If the amount of parameter-sets is always 2, then it's simple like my code above, also considering the optional constraint. If the amount of parameter-sets might be variable, i.e. 4 or 5, then it's getting complicated if you still want to offer the constraint-parameter(s). Without constraint-parameter(s) it's simple, you just could note the function without parameters but check for the amount of parameters in the caller and handle them accordingly, in the caller you just use so many parameters as required.
query {
objListQuery {
objResolver() {
paramArray = getArguments();
}
}
}
Like written above it's getting hard, if you still want to offer constraint-parameters here, but I'd suggest that would be material for another question.
You can implement a Connection interface, with a single resolver to allow a one-step querying mechanism. You can reduce query endpoints using this technique.
E.g, an example query would look like:
allObjects(start: "01-01-2019", end: "04-29-2019", types:["test", "sales"]){
nodes {
id,
dateCreated,
type
}
}
In the resolver, you can use this criteria to prepare and return the data.
Benefits:
Less query endpoints.
Filtering and pagination.
Your filter interface can be quite fancy:
allObjects(
dateCreated: {
between:{
start,
end
},
skipWeekends: true
},
types: {
include:[],
exclude: []
}
)
Add new criteria as your needs grow. Start with what you want and take it from there.
I can't perform a wildcard-query on an embedded-list property of vertex (or edge).
For example:
Assume we have a Person class with a multi-value property named Nicknames and one instance of it:
{
"#type": "d",
"#rid": "#317:0",
"#version": 1,
"#class": "Person",
"Nicknames": [
"zito",
"ziton",
"zitoni"
]
}
then,
Select FROM Person WHERE Nicknames like "zit%"
returns empty result-set, while:
Select FROM Person WHERE Nicknames ="zito" returns 1 item correctly.
There's a NOTUNIQUE_HASH_INDEX index on the field Nicknames.
I've tried many ways (contains, index-query...) with no luck :(
I'm probably missing something basic.
I know is not an ideal solution what i'm going to write but, to stay stuck with your requirement of "query by wildcard" this is the only way that worked for me, as AVK stated is a better idea work with a Lucene index, but with the standard implementation i was unable to let it work, now here what i've done:
Use studio to create a javascript function with 2 parameter with name "array" and "rule", lets name the function "wildcardSearch"
past this code in the body of the function (is just simple javascript change it if it dosent do the job) :
for(i=0; i<array.length ; i++){
rule= rule.split("*").join(".*");
rule= rule.split("*").join(".*");
rule= "^" + ruleValue + "$";
var regex = new RegExp(rule);
if (regex.test(array[i]))
return true;
}
return false;
Remember to save the fucntion
now you can query:
Select from Person where wildcardSearch(nicknames,'zit*')=true
CONSIDERATIONS: is a brute force method, but show how "funny" can be play around with the "stored procedure" in OrientDb so i've decided to share it anyway, if performance are your main goal this things is not for you, it scan all the class and do the loop on the array to apply the regex. An Index is a way better solution, or change your db with a different data structure.
You can try this:
select from Person where Nicknames containstext 'zit'
Hope that helps
I want to use an array-like result from a query, but am having trouble.
I have:
class case extends V
class doc extends V
class filedIn extends E
So, a document is filed in a class via an edge of class filedIn.
My first query pulls a set a vertices:
.select().from('case').one().then(function(result){...
I then want to select all the filedIn edges linking to any of those case vertices, but how?
JSON.stringify(result)
{"#type":"d","#class":"matter","title":"my case","in_filedIn":["#17:7","#17:8","#17:9"],"#rid":"#12:3","#version":12}
looks like result['in_filedIn'] is an array,
but
.select().from(result['in_filedIn']).all()
gets me this error from back from the database:
Error on parsing command at position #0: Error parsing query: \nSELECT * FROM [object Object]\nEncountered \
select().from('[#17:7,#17:8,#17:9]')
(hard coded) works.
.select.from("[" + ["#17:1","#17:2].join(',') + "]").all()
also works.
but
select.from("[" + result['in_filedIn'].join(,)+"]").all()
throws
result.in_filedIn.join is not a function
I.e. whatever kind of object it is, it's not inheriting from the Array prototype.
console.log(result['in_filedIn'])
produces:
Bag {
serialized: >'AQAAAAUAEQAAAAAAAAAHABEAAAAAAAAACAARAAAAAAAAAAkAEQAAAAAAAAAKABEAAAAAAAAADwAAAAA>AAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA',
uuid: null,
_content:
[ { [String: '#17:7'] cluster: 17, position: 7 },
...
So I'm rather puzzled what to do.
I want to select all the edges whose #rid is listed in result.in_filedIn
Or have I misunderstood something?
Hi oliver in_filedIn is not an Array is a structure called RidBag in Orientjs the implementation is done in Bag.js.
If yo want to get the array you should do
result['in_filedIn'].all()
to get the array.
The following worked, though it's a bit dirty.
.from(JSON.stringify(result['in_filedIn']).replace(/"/g,""))
I'm still looking for a better answer!
I had a similar problem actually. When you instantiate your db connection, there's a config field they don't list in their docs called enableRIDBags.
So your config would look like:
{
host: 'localhost',
port: 2424,
username: 'blah',
password: 'blooh',
enableRIDBags: false
}
I find that it's better to just disable RIDBags as they're a bit finicky in node. The .all() trick listed above worked fine for me up until my vertices had more than 40 edges, as then they were all converted to SBTree bags instead of Embedded bags. If you check line 122 here, you'll see that they can't convert the trees to JSON when you call .all()
I am trying to insert a vertex with orientjs(previously oriento) query builder. My class has a link type property pointing to another class.
I know I can get it to work with a raw query string but I would love to use the query builder.
Here is what I've tried so far :
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"})
db.insert()
.into('VertexClassName')
.set({"prop":"value", "linkProperty":"#33:1289287"})
I get the following error :
Error on saving record in cluster #13
Am I setting properties in the right way ?
Could the error be related to somtehing else ?
I have sucessfully ran an insert query in the cluster #13 with a raw query string in the studio...
According to the official documentation it seems that the problem might be at the end of your statement
db.insert().into('VertexClassName')
.set({"prop":"value", "linkProperty":"33:1289287"}).one()
.then(function (data) {
// callback
});
Check if your code works adding one() to the pipe line
EDITED: I found this method in orientjs.
db.create('VERTEX', 'V')
.set({
key: 'value',
foo: 'bar'
})
.one()
.then(function (vertex) {
console.log('created vertex', vertex);
});
When using Tinkerpop API they recommend using createVertex instead of insert, because createVertex is intended for graphs and insert for Documents... Could you try with the create() method instead?
I am using SQL and it worked.
sql = "INSERT INTO Station set linked = (select from LinkedClass where LinkedProb = 'value'), prop = 'value'"
OrientVertex vertex = new OrientVertex();
vertex = graph.command(new OCommandSQL(sql)).execute();
I don't think that's possible unless you've added a proper field with the right type 'Link' in your schema. (which I rarely do).
Now instead of having the right 'link' type inserted you can do the opposite, store is as a String, and leverage the query functions to use it correctly:
db.insert().into('table').set({prop: '#15:14'}).one();
And it will be converted as String (which is a bit sad) but then you can use that in your queries:
SELECT eval(prop) FROM table;
And it will be 'eval'-ed to a Node RecordID that you can directly use and call functions like expand() on.
For example:
SELECT name FROM (SELECT expand(eval(prop)) FROM table);
Will eval the node stored in the insert(), grab the node, expand it and collect its name property.
I have not clearly issue during query using two criterials like Id and Other. I use a Repository storing some data like id,iso,value. I have created an index("_id","Iso") to performs queries but queries are only returning my cursor if i use only one criterial like _id, but is returning nothing if a use two (_id, Iso) (commented code).
Are the index affecting the response or the query method are failing?
use :v1.6.5 and C# official.
Sample.
//Getting Data
public List<BsonObject> Get_object(string ID, string Iso)
{
using (var helper = BsonHelper.Create())
{
//helper.Db.Repository.EnsureIndex("_Id","Iso");
var query = Query.EQ("_Id", ID);
//if (!String.IsNullOrEmpty(Iso))
// query = Query.And(query, Query.EQ("Iso", Iso));
var cursor = helper.Db.Repository.FindAs<BsonObject>(query);
return cursor.ToList();
}
}
Data:
{
"_id": "2345019",
"Iso": "UK",
"Data": "Some data"
}
After that I have Updated my data using Update.Set() methods. I can see the changed data using MongoView. The new data are correct but the query is always returning the sames olds values. To see these values i use a page that can eventually cached, but if add a timestamp at end are not changing anything, page is always returning the same olds data. Your comments are welcome, thanks.
I do not recall offhand how the C# driver creates indexes, but the shell command for creating an index is like this:
db.things.ensureIndex({j:1});
Notice the '1' which is like saying 'true'.
In your code, you have:
helper.Db.Repository.EnsureIndex("_Id","Iso");
Perhaps it should be:
helper.Db.Repository.EnsureIndex("_Id", 1);
helper.Db.Repository.EnsureIndex("Iso", 1);
It could also be related to the fact that you are creating indexes on "_Id" and the actual id field is called "_id" ... MongoDB is case sensitive.
Have a quick look through the index documentation: http://www.mongodb.org/display/DOCS/Indexes