neo4j return map of properties+ID - dictionary

I want to get back a map (dictionary) of the properties of my nodes in cypher along with the ID of the nodes. I have the following code that does not work:
match (n:person)
with n limit 5
with properties(n)+oldID:ID(n) as info
return info
This does not create a single dictionary with the properties and the ID. I have tried different formats but nothing seems to work. What am I doing wrong here?

You can return the properties along with the id like this :
MATCH (n:Person)
RETURN n{.*, oldID: id(n)} AS info
LIMIT 5
Result :
╒═══════════════════════════════════════════════════╕
│"info" │
╞═══════════════════════════════════════════════════╡
│{"born":1964,"name":"Keanu Reeves","oldID":1} │
├───────────────────────────────────────────────────┤
│{"born":1967,"name":"Carrie-Anne Moss","oldID":2} │
├───────────────────────────────────────────────────┤
│{"born":1961,"name":"Laurence Fishburne","oldID":3}│
├───────────────────────────────────────────────────┤
│{"born":1960,"name":"Hugo Weaving","oldID":4} │
├───────────────────────────────────────────────────┤
│{"born":1967,"name":"Lilly Wachowski","oldID":5} │
└───────────────────────────────────────────────────┘
Useful resource for learning Map projections https://neo4j.com/developer-blog/a-comprehensive-guide-to-cypher-map-projection/

You could just do
match (n:person)
with n limit 5
with {props:properties(n), oldID:ID(n)} as info
return info

Related

Rename Dexterity object (id) after copy

It's simple to choose the object ID at creation time with INameChooser.
But we also want to be able to choose the object ID after a clone (and avoid copy_of in object ID).
We tried several different solutions :
subscribers on events :
OFS.interfaces.IObjectClonedEvent
zope.lifecycleevent.interfaces.IObjectAddedEvent
...
manage_afterClone method on content class
Every time, we get a traceback because we changed the ID "too soon". For example when using Plone API :
File "/Users/laurent/.buildout/eggs/plone.api-2.0.0a1-py3.7.egg/plone/api/content.py", line 256, in copy
return target[new_id]
File "/Users/laurent/.buildout/eggs/plone.folder-3.0.3-py3.7.egg/plone/folder/ordered.py", line 241, in __getitem__
raise KeyError(key)
KeyError: 'copy_of_87c7f9b7e7924d039b832d3796e7b5a3'
Or, with a Copy / Paste in the Plone instance :
Module plone.app.content.browser.contents.paste, line 42, in __call__
Module OFS.CopySupport, line 317, in manage_pasteObjects
Module OFS.CopySupport, line 229, in _pasteObjects
Module plone.folder.ordered, line 73, in _getOb
AttributeError: 'copy_of_a7ed3d678f2643bc990309cde61a6bc5'
It's logical, because the ID is stored before events notification / manage_afterClone call for later use.
Even defining a _get_id on containers cannot work to define the ID, because we don't have the object to get attributes from (and generate the ID).
But then, how could we achieve this in a clean way ?
Please tell me there is a better solution than redefining _pasteObjects (OFS.CopySupport) !
Thank you for your inputs !
So unfortunately not...
But you can access the original object from within _get_id.
For example:
from OFS.CopySupport import _cb_decode
from plone import api
...
def _get_id(self, id_):
# copy_or_move => 0 means copy, 1 means move
copy_or_move, path_segments = _cb_decode(self.REQUEST['__cp']
source_path = '/'.join(path_segments[0]) # Imlement for loop for more than one copied obj.
app = self.getPhysicalRoot()
# access to source obj
source_obj = app.restrictedTraverse(source_path)
# Do whatever you need - probably using INameChooser
....
To have a canonical way of patching this I use collective.monkeypatcher
Once installed add the following in ZCML:
<include package="collective.monkeypatcher" />
<monkey:patch
class="OFS.CopySupport.CopyContainer"
original="_get_id"
replacement="patches.patched_get_id"
/>
Where patches.py is your module containing the new method patched_get_id, which replaces _get_id.
I'm sorry I don't have better news for you, but this is how I solved a similar requirement.
This code (patched _get_id) adds a counter at the end of a id if already there.
def patched_get_id(self, id_)
match = re.match('^(.*)-([\d]+)$', id_)
if match:
id_ = match.group(1)
number = int(match.group(2))
else:
number = 1
new_id = id_
while new_id in self.objectIds():
new_id = '{}-{}'.format(id_, number)
number += 1
return new_id

ArangoDB copy Vertex and Edges to neighbors

I'm trying to copy a vertex node and retain it's relationships in ArangoDB. I'm getting a "access after data-modification" error (1579). It doesn't like it when I iterate over the source node's edges and insert an edge copy within the loop. This makes sense but I'm struggling to figure out how to do what I'm wanting within a single transaction.
var query = arangojs.aqlQuery`
let tmpNode = (FOR v IN vertices FILTER v._id == ${nodeId} RETURN v)[0]
let nodeCopy = UNSET(tmpNode, '_id', '_key', '_rev')
let nodeCopyId = (INSERT nodeCopy IN 'vertices' RETURN NEW._id)[0]
FOR e IN GRAPH_EDGES('g', ${nodeId}, {'includeData': true, 'maxDepth': 1})
let tmpEdge = UNSET(e, '_id', '_key', '_rev')
let edgeCopy = MERGE(tmpEdge, {'_from': nodeCopyId})
INSERT edgeCopy IN 'edges'
`;
This quesion is somewhat similar to 'In AQL how to re-parent a vertex' - so let me explain this in a similar way.
One should use the ArangoDB 2.8 pattern matching traversals to solve this.
We will copy Alice to become Sally with similar relations:
let alice=DOCUMENT("persons/alice")
let newSally=UNSET(MERGE(alice, {_key: "sally", name: "Sally"}), '_id')
let r=(for v,e in 1..1 ANY alice GRAPH "knows_graph"
LET me = UNSET(e, "_id", "_key", "_rev")
LET newEdge = (me._to == "persons/alice") ?
MERGE(me, {_to: "persons/sally"}) :
MERGE(me, {_from: "persons/sally"})
INSERT newEdge IN knows RETURN newEdge)
INSERT newSally IN persons RETURN newSally
We therefore first load Alice. We UNSET the properties ArangoDB should set on its own. We change the properties that have to be uniq to be uniq for Alice so we have a Sally afterwards.
Then we open a subquery to traverse ANY first level relations of Alice. In this subequery we want to copy the edges - e. We need to UNSET once more the document attributes that have to be autogenerated by ArangoDB. We need to find out which side of _from and _to pointed to Alice and relocate it to Sally.
The final insert of Sally has to be outside of the subquery, else this statement will attempt to insert one Sally per edge we traverse. We can't insert Saly in front of the query as you already found out - no subsequent fetches are allowed after the insert.

Alfresco FTS searches

Is there a way, using a FTS search, to limit the result list to specific model types?
Also, is there a way to group the results of a FTS search?
Example:
Node 1
prop_A: ABC
prop_B: 1/1/2013
prop_C: this is some string description specific to node 1
prop_D: 1
Node 2
prop_A: ABC
prop_B: 1/1/2013
prop_C: this is some string description specific to node 2
prop_D: 1
Node 3
prop_A: DEF
prop_B: 1/2/2013
prop_C: this is some string description specific to node 3
prop_D: 1
Can you create a query that returns just Node 1 and Node 3? Node 1 and Node 2 should be grouped together because of the same prop_A and prop_B property values.
I'm using the javascript webscript search object.
var queryDef =
{
query: "=prop_A:ABC",
language: "fts-alfresco",
onerror: "exception"
};
try
{
model.articles = [];
var nodes = search.query( queryDef );
}
catch(e) {}
Two questions here:
"Is there a way, using a FTS search, to limit the result list to specific model types?"
use the TYPE:"cm:content" clause to narrow results by type.
For the grouping question, you should look for an example of the solr facet support in Alfresco and check if this fits your requirements.

CouchDB View with 2 Keys

I am looking for a general solution to a problem with couchdb views.
For example, have a view result like this:
{"total_rows":4,"offset":0,"rows":[
{"id":"1","key":["imported","1"],"value":null},
{"id":"2","key":["imported","2"],"value":null},
{"id":"3","key":["imported","3"],"value":null},
{"id":"4","key":["mapped","4"],"value":null},
{"id":"5,"key":["mapped","5"],"value":null}
]
1) If I want to select only "imported" documents I would use this:
view?startkey=["imported"]&endkey=["imported",{}]
2) If I want to select all imported documents with an higher id then 2:
view?startkey=["imported",2]&endkey=["imported",{}]
3) If I want to select all imported documents with an id between 2 and 4:
view?startkey=["imported",2]&endkey=["imported",4]
My Questtion is: How can I select all Rows with an id between 2 and 4?
You can try to extend the solution above, but prepend keys with a kind of "emit index" flag like this:
map: function (doc) {
emit ([0, doc.number, doc.category]); // direct order
emit ([1, doc.category, doc.number]); // reverse order
}
so you will be able to request them with
view?startkey=[0, 2]&endkey=[0, 4, {}]
or
view?startkey=[1, 'imported', 2]&endkey=[1, 'imported', 4]
But 2 different views will be better anyway.
I ran into the same problem a little while ago so I'll explain my solution. Inside of any map function you can have multiple emit() calls. A map function in your case might look like:
function(doc) {
emit([doc.number, doc.category], null);
emit([doc.category, doc.number], null);
}
You can also use ?include_docs=true to get the documents back from any of your queries. Then your query to get back rows 2 to 4 would be
view?startkey=[2]&endkey=[4,{}]
You can view the rules for sorting at CouchDB View Collation

MDX - distinct count

I was following this article:
http://msdn.microsoft.com/en-us/library/aa902637%28v=sql.80%29.aspx
and my query for distinct count looks like this:
Count(CrossJoin({[Measures].[Submission Count]}, [Submission].[PK Submission].Members), ExcludeEmpty)
it returns always 1 more than it should (for example it returns 27 instead of 26).
In the same article there is this query (which is suppose to solve this problem):
Count(CrossJoin( {[Sales]},
Descendants([Customers].CurrentMember, [Customer Names])),
ExcludeEmpty)
But I can't get it to work. I've tried these two but second one always returns 1 or 0 while the first one doesn't work (error: I have to explicitly define a level):
Count(CrossJoin( {[Measures].[Submission Count]},
Descendants([Submission].CurrentMember, [Submission].[PK Submission])),
ExcludeEmpty)
Count(CrossJoin( {[Measures].[Submission Count]},
Descendants([Submission].[PK Submission].CurrentMember, [Submission].[PK Submission])),
ExcludeEmpty)
Any idea what am I doing wrong?
Thanks!
The reason the first query returns "1 more than it should" is because the [Submission].[PK Submission].Members tuple set also includes the All member.
If you refer to the [PK Submission] level instead of all the members of the [PK Submission] hierarchy, it doesn't include the All member.
So, the following returns what you're expecting:
Count( CrossJoin( { [Measures].[Submission Count] }
, { [Submission].[PK Submission].[PK Submission] })
, ExcludeEmpty)

Resources