Extract & Filter in Cypher - filter

I am trying to solve this problem but still it has problem.. Well, my query a bit complex because I use extract, shortpath and filter. When I try this code below, It returns filter requires where..
match (actor:Actor{name:"XXXX"}),(reeves:Actor{name:"YYYY"}),
p= shortestPath ((actor)-[*..20]-(reeves))
where "Reeves, Keanu"=filter(n2 in nodes(p):n.name)
return distinct extract(n in nodes(p)|n.name) as Names;
but as you see there is "where" keyword.
and I tried like that
match (actor:Actor{name:"XXXX"}),(reeves:Actor{name:"YYYY"}),
p= shortestPath ((actor)-[*..20]-(reeves))
WHERE NOT ALL (x IN nodes(p)
WHERE x.name<> "YYYY")
return distinct extract(n in nodes(p)|n.name) as Names;
but this time, I still get "Reeves, Keanu" name.. I want to filter this name from my result..
where am I doing a mistake?

If you want to find the shortest paths that contain the name "YYYY", try:
MATCH (actor:Actor{name:"XXXX"}), (reeves:Actor{name:"YYYY"}),
p = shortestPath ((actor)-[*..20]-(reeves))
WHERE any(n2 in nodes(p) WHERE n2.name="YYYY")
RETURN distinct extract(n in nodes(p) | n.name) as Names;
On the other hand, if you want to ignore the shortest paths that contain the name "YYYY":
MATCH (actor:Actor{name:"XXXX"}), (reeves:Actor{name:"YYYY"}),
p = shortestPath ((actor)-[*..20]-(reeves))
WHERE none(n2 in nodes(p) WHERE n2.name="YYYY")
RETURN distinct extract(n in nodes(p) | n.name) as Names;
By the way, notice how the any and none predicates internally require a WHERE clause.

Related

Neo4j Filter function is no longer supported

I want to compare two lists in order to find the values in the first list which are not in the second and return them. thanks in advance guys
the code returns: Filter is no longer supported
is there any alternative way to do this
MATCH (cu:Customer{name: "myCustomer"})-[pu:PURCHASED]->(o:Order)-[*]->(cat:Category)
MATCH (b:Book)-[:IS_a]->(cat)
WITH COLLECT(DISTINCT pu.ISBN) AS purchasedbooks,COLLECT(DISTINCT b.ISBN) AS booksFromTheSameCategory
RETURN FILTER( n IN booksFromTheSameCategory WHERE NOT n IN purchasedbooks ) as listC
You can use the list comprehension syntax instead of the obsolete filter function:
MATCH (cu:Customer{name: "myCustomer"})-[pu:PURCHASED]->(o:Order)-[*]->(cat:Category)<-[:IS_a]-(b:Book)
WITH COLLECT(DISTINCT pu.ISBN) AS purchasedbooks, COLLECT(DISTINCT b.ISBN) AS booksFromTheSameCategory
RETURN [n IN booksFromTheSameCategory WHERE NOT n IN purchasedbooks] as listC
You can see Cypher syntax change documentation on this page.

Neo4J: 3-Level Expand node with filter

For a new POC I have the following use case:
For a given node do a 3-Level expand but apply also a filter on all expanded node (means I want to filter all resulting nodes for certain properties)
Test set:
nodes: ~ 17 Mio
edges: ~ 40 Mio
Properties: ~ 2650 Mio
My first solution looks like this:
MATCH path=(startNode:Entity {id:'RVDJRcV_yfXbG0-syGKp3Q..'})-[*..3]-(endNode:Entity)
WITH path
WHERE ALL (n IN nodes(path)[1..]
WHERE n.key = '1' AND n.domain = 'facebook.com' AND n.investigationID='any')
RETURN path
LIMIT 100
This does the job, but it is not very fast. Avg. query times in my test set are 2-3 seconds but with many timeouts (time > 30 seconds). I assume the problem is the path handling and that my node has lots of properties...
Explain plan:
Variant 1: i removed the "with path"
Solution:
Based on the tip that i should avoid [1..] in the query
MATCH path=(startNode:Entity {id:'v-jXIO7kozAa35gMUpUkvg..'})-[*..3]-(endNode:Entity)
WHERE ALL (n IN nodes(path)
WHERE n=startNode OR (n.key = '1' AND n.domain = 'facebook.com' AND n.investigationID='any'))
RETURN path
LIMIT 100
While you can filter during expansion with variable-length paths, Cypher currently can't apply that filter during expansion when you're working with a slice of the list instead of the whole list. It will fall back to doing the full var-length expansion, and then applying the filter to all results found.
We need to only use ALL (n IN nodes(path) ..., we can't use the slice of the path.
To do this, we need to add one more predicate within the all() function. Since the start node probably doesn't meet the current predicate, we'll create an exception for it:
MATCH path=(startNode:Entity {id:'RVDJRcV_yfXbG0-syGKp3Q..'})-[*..3]-(endNode:Entity)
WHERE ALL (n IN nodes(path)[1..]
WHERE n=startNode OR (n.key = '1' AND n.domain = 'facebook.com' AND n.investigationID='any'))
RETURN path
LIMIT 100

Phrase matching with Sitecore ContentSearch API

I am using Sitecore 7.2 with a custom Lucene index and Linq. I need to give additional (maximum) weight to exact matches.
Example:
A user searches for "somewhere over the rainbow"
Results should include items which contain the word "rainbow", but items containing the exact and entire term "somewhere over the rainbow" should be given maximum weight. They will displayed to users as the top results. i.e. An item containing the entire phrase should weigh more heavily than an item which contains the word "rainbow" 100 times.
I may need to handle ranking logic outside of the ContentSearch API by collecting "phrase matches" separately from "wildcard matches", and that's fine.
Here's my existing code, truncated for brevity. The code works, but exact phrase matches are not treated as I described.
using (var context = ContentSearchManager.GetIndex("sitesearch-index").CreateSearchContext())
{
var pred = PredicateBuilder.False<SearchResultItem>();
pred = pred
.Or(i => i.Name.Contains(term)).Boost(1)
.Or(i => i["Field 1"].Contains(term)).Boost(3)
.Or(i => i["Field 2"].Contains(term)).Boost(1);
IQueryable<SearchResultItem> query = context.GetQueryable<SearchResultItem>().Where(pred);
var hits = query.GetResults().Hits;
...
}
How can I perform exact phrase matching and is it possible with the Sitecore.ContentSearch.Linq API?
Answering my own question. The problem was with the parenthesis syntax. It should be
.Or(i => i.Name.Contains(term).Boost(1))
rather than
.Or(i => i.Name.Contains(term)).Boost(1)
The boosts were not being observed.
I think if you do the following it will solve this:
Split your search string on space
Create a predicate for each split with an equal boost value,
Create an additional predicate with the complete search string and
with higher boost value
combine all these predicates in one "OR" predicate.
Also I recommend you to check the following:
Sitecore Solr Search Score Value
http://sitecoreinfo.blogspot.com/2015/10/sitecore-solr-search-result-items.html

Querying with multiple predicates on sub-objects in a collection with MongoDB (official c# driver)

I've the following data structure:
A
_id
B[]
_id
C[]
_id
UserId
I'm trying to run the following query:
where a.B._id == 'some-id' and a.B.C.UserId=='some-user-id'.
That means I need to find a B document that has a C document within with the relevant UserId, something like:
Query.And(Query.EQ("B._id", id), Query.EQ("B.C.UserId", userId));
This is not good, of course, as it may find B with that id and another different B that has C with that UserId. Not good.
How can I write it with the official driver?
If the problem is only that your two predicates on B are evaluated on different B instances ("it may find B with that ID and another different B that has C with that UserId"), then the solution is to use an operator that says "find me an item in the collection that satisfies both these predicates together".
Seems like the $elemMatch operator does exactly that.
From the docs:
Use $elemMatch to check if an element in an array matches the specified match expression. [...]
Note that a single array element must
match all the criteria specified; [...]
You only need to use this when more than 1 field must be matched in the array element.
Try this:
Query.ElemMatch("B", Query.And(
Query.EQ("_id", id),
Query.EQ("C.UserId", userId)
));
Here's a good explanation of $elemMatch and dot notation, which matches this scenario exactly.

Recursively (?) compose LINQ predicates into a single predicate

(EDIT: I have asked the wrong question. The real problem I'm having is over at Compose LINQ-to-SQL predicates into a single predicate - but this one got some good answers so I've left it up!)
Given the following search text:
"keyword1 keyword2 keyword3 ... keywordN"
I want to end up with the following SQL:
SELECT [columns] FROM Customer
WHERE
(Customer.Forenames LIKE '%keyword1%' OR Customer.Surname LIKE '%keyword1%')
AND
(Customer.Forenames LIKE '%keyword2%' OR Customer.Surname LIKE '%keyword2%')
AND
(Customer.Forenames LIKE '%keyword3%' OR Customer.Surname LIKE '%keyword3%')
AND
...
AND
(Customer.Forenames LIKE '%keywordN%' OR Customer.Surname LIKE '%keywordN%')
Effectively, we're splitting the search text on spaces, trimming each token, constructing a multi-part OR clause based on each token, and then AND'ing the clauses together.
I'm doing this in Linq-to-SQL, and I have no idea how to dynamically compose a predicate based on an arbitrarily-long list of subpredicates. For a known number of clauses, it's easy to compose the predicates manually:
dataContext.Customers.Where(
(Customer.Forenames.Contains("keyword1") || Customer.Surname.Contains("keyword1")
&&
(Customer.Forenames.Contains("keyword2") || Customer.Surname.Contains("keyword2")
&&
(Customer.Forenames.Contains("keyword3") || Customer.Surname.Contains("keyword3")
);
but I want to handle an arbitrary list of search terms. I got as far as
Func<Customer, bool> predicate = /* predicate */;
foreach(var token in tokens) {
predicate = (customer
=> predicate(customer)
&&
(customer.Forenames.Contains(token) || customer.Surname.Contains(token));
}
That produces a StackOverflowException - presumably because the predicate() on the RHS of the assignment isn't actually evaluated until runtime, at which point it ends up calling itself... or something.
In short, I need a technique that, given two predicates, will return a single predicate composing the two source predicates with a supplied operator, but restricted to the operators explicitly supported by Linq-to-SQL. Any ideas?
I would suggest another technique
you can do:
var query = dataContext.Customers;
and then, inside a cycle do
foreach(string keyword in keywordlist)
{
query = query.Where(Customer.Forenames.Contains(keyword) || Customer.Surname.Contains(keyword));
}
If you want a more succinct and declarative way of writing this, you could also use Aggregate extension method instead of foreach loop and mutable variable:
var query = keywordlist.Aggregate(dataContext.Customers, (q, keyword) =>
q.Where(Customer.Forenames.Contains(keyword) ||
Customer.Surname.Contains(keyword));
This takes dataContext.Customers as the initial state and then updates this state (query) for every keyword in the list using the given aggregation function (which just calls Where as Gnomo suggests.

Resources