Neo4j.rb How to use it with Rails - cypher

I am using neo4j version 3.X, I am using searchkick
currently using
User.search(params[:term], operator: 'or',
fields: [:first_name, :last_name],
misspellings: { below: 5 },
match: :word_start,
page: params[:page], per_page: params[:rpp], padding: params[:offset])
instead of
User.where('(sp.first_name + sp.last_name) =~ ?', /.*#{params[:term].sub(/\s/, '')}.*/i)
But I have problem where I have to make more cypher queries at the same time with searching How to do that?
For exmaple
Neo4j::ActiveBase.new_query.match(n: {User: { uuid: current_user.uuid }}).break
.match('(n)-[:connected_to {status: 2}]-(sp:User)')
.return('DISTINCT sp')
I want to seach in this query with elasctic search with first name & last name
In my model I have defined searchkick word_start: [:first_name, :last_name]

It's been a while since I've used searchkick, but I would suggest trying to use the ActiveNode syntax instead of the neo4j-core Query syntax so that you can append .search on the end:
current_user.connected_users.rel_where(status: 2).distinct.search(...)
This is assuming that there is a connected_users association which uses the connected_to relationship type. You may have another, similar, association in your User model but I didn't know what it was.
I'm not 100% sure if the distinct will work with the search or not, but you could perhaps drop the .distinct part and searchkick might return you a distinct set anyway.

Related

Rails - Regex to remove accent from bank word

I'm new to Rails and I'm using Google Translate to post here. I have a doubt.
I have the following scope:
I'm trying to remove the accent from the bank words when performing a search.
In the parameters it already works with parameterize, but how do I query the bank (inside the where) to remove the accent?
I'm using postgresql.
Initially I tried to use regex_replace, but apparently it didn't work, could I be applying it wrong?
scope :filter_occupation, -> (params) {
params[:occupation].present? ?
where("lower(regexp_replace(occupation, '[^\w]+','')) LIKE ?",
# where("lower(occupation) LIKE ?",
"%#{params[:occupation].parameterize(separator: ' ')}%")
:
all
}

Why does Rails .select alias change attributes to lowercase?

In our controller, we are trying to show a video series, which should return JSON similar to this:
{
id: 1,
name: "Series Name",
videos: [
id: 2,
name: "Video Name",
isInPlaylist: true,
isFavorite: false
]
}
We are adding the isInPlaylist and isInFavorite attributes via another table where we store data if a user has acted upon a video (rated it, favorited it, etc.).
videos = series.videos
.where('videos.is_live = true')
.joins("some join to user_videos join_table")
.select(
'videos.*,
coalesce(user_videos.rating, 0.0) as user_rating,
coalesce(user_videos.enqueue, \'false\') as isInPlaylist,
coalesce(user_videos.favorite, \'false\') as isFavorite'
)
Note that in our select statement those attributes are explicitly aliased as camel-cased values. However when we execute this query, these attributes are returned lower case instead:
{
isinplaylist: true,
isfavorite: false
}
This is not a Rails behavior, but rather a SQL behavior. Alias's are folded to lower case unless explicitly quoted. For an example, here is the output of a simple query in psql (the Postgres CLI program).
=# select created_at as theTimeNow from users limit 5;
thetimenow
----------------------------
2013-03-05 18:45:11.127092
2013-09-07 16:43:01.349823
2013-03-05 18:53:35.888306
2013-09-07 16:53:06.553129
2013-10-29 00:38:56.909418
(5 rows)
=# select created_at as "theTimeNow" from users limit 5;
theTimeNow
----------------------------
2013-03-05 18:45:11.127092
2013-09-07 16:43:01.349823
2013-03-05 18:53:35.888306
2013-09-07 16:53:06.553129
2013-10-29 00:38:56.909418
(5 rows)
Notice the column name outputs
Wrapping the alias in double quotes preserves case-sensitivity.
.select('foo as Bar') # => { bar: true }
.select('foo as "Bar"') # => { Bar: true }
The change to lower case is not an issue with the Rails .select() method but is enforced by the DB, in our case PostgreSQL, and is a practice called "Folding". Its worth noting that while PSQL will fold to lowercase letters, mySQL will fold to upper case letters.
I would argue however that it should still be included in the Rails API docs
¯\_(ツ)_/¯
I like you answer. The behaviour you see is rails default. As an alternative and
a more classic 'rails way' would be to use a json serializing library like jBuilder. It gives you lot more control over your API but your problem would be easy to fix in that using:
json.key_format! camelize: :lower
json.first_name 'David'
# => { "firstName": "David" }
To use something like this you would alias the columns as is_in_playlist format.
Here's a good place to start with jBuilder learning:
http://railscasts.com/episodes/320-jbuilder
Good tutorial on more json serializers:
http://railscasts.com/episodes/409-active-model-serializers

Rails, Ransack: How to search HABTM relationship for "all" matches instead of "any"

I'm wondering if anyone has experience using Ransack with HABTM relationships. My app has photos which have a habtm relationship with terms (terms are like tags). Here's a simplified explanation of what I'm experiencing:
I have two photos: Photo 1 and Photo 2. They have the following terms:
Photo 1: A, B, C
Photo 2: A, B, D
I built a ransack form, and I make checkboxes in the search form for all the terms, like so:
- terms.each do |t|
= check_box_tag 'q[terms_id_in][]', t.id
If I use: q[terms_id_in][] and I check "A, C" my results are Photo 1 and Photo 2. I only want Photo 1, because I asked for A and C, in this query I don't care about B or D but I want both A and C to be present on a given result.
If I use q[terms_id_in_all][] my results are nil, because neither photo includes only A and C. Or, perhaps, because there's only one term per join, so no join matches both A and C. Regardless, I want just Photo 1 to be returned.
If I use any variety of q[terms_id_eq][] I never get any results, so I don't think that works in this case.
So, given a habtm join, how do you search for models that match the given values while ignoring not given values?
Or, for any rails/sql gurus not familiar with Ransack, how else might you go about creating a search form like I'm describing for a model with a habtm join?
Update: per the answer to related question, I've now gotten as far as constructing an Arel query that correctly matches this. Somehow you're supposed to be able to use Arel nodes as ransackers, or as cdesrosiers pointed out, as custom predicates, but thus far I haven't gotten that working.
Per that answer, I setup the following ransack initializer:
Ransack.configure do |config|
config.add_predicate 'has_terms',
:arel_predicate => 'in',
:formatter => proc {|term_ids| Photo.terms_subquery(term_ids)},
:validator => proc {|v| v.present?},
:compounds => true
end
... and then setup the following method on Photo:
def self.terms_subquery(term_ids)
photos = Arel::Table.new(:photos)
terms = Arel::Table.new(:terms)
photos_terms = Arel::Table.new(:photos_terms)
photos[:id].in(
photos.project(photos[:id])
.join(photos_terms).on(photos[:id].eq(photos_terms[:photo_id]))
.join(terms).on(photos_terms[:term_id].eq(terms[:id]))
.where(terms[:id].in(term_ids))
.group(photos.columns)
.having(terms[:id].count.eq(term_ids.length))
).to_sql
end
Unfortunately this doesn't seem to work. While terms_subquery produces the correct SQL, the result of Photo.search(:has_terms => [2,5]).result.to_sql is just "SELECT \"photos\".* FROM \"photos\" "
With a custom ransack predicate defined as in my answer to your related question, this should work with a simple change to your markup:
- terms.each do |t|
= check_box_tag 'q[id_has_terms][]', t.id
UPDATE
The :formatter doesn't do what I thought, and seeing as how the Ransack repo makes not a single mention of "subquery," you may not be able to use it for what you're trying to do, after all. All available options seem to be exhausted, so there would be nothing left to do but monkey patch.
Why not just skip ransack and query the "photos" table as you normally would with active record (or even with the Arel query you now have)? You already know the query works. Is there a specific benefit you hoped to reap from using Ransack?

Rails 3 ActiveRecord query using both SQL IN and SQL OR operators

I'm writing a Rails 3 ActiveRecord query using the "where" syntax, that uses both the SQL IN and the SQL OR operator and can't figure out how to use both of them together.
This code works (in my User model):
Question.where(:user_id => self.friends.ids)
#note: self.friends.ids returns an array of integers
but this code
Question.where(:user_id => self.friends.ids OR :target => self.friends.usernames)
returns this error
syntax error, unexpected tCONSTANT, expecting ')'
...user_id => self.friends.ids OR :target => self.friends.usern...
Any idea how to write this in Rails, or just what the raw SQL query should be?
You don't need to use raw SQL, just provide the pattern as a string, and add named parameters:
Question.where('user_id in (:ids) or target in (:usernames)',
:ids => self.friends.ids, :usernames => self.friends.usernames)
Or positional parameters:
Question.where('user_id in (?) or target in (?)',
self.friends.ids, self.friends.usernames)
You can also use the excellent Squeel gem, as #erroric pointed out on his answer (the my { } block is only needed if you need access to self or instance variables):
Question.where { user_id.in(my { self.friends.ids }) |
target.in(my { self.friends.usernames }) }
Though Rails 3 AR doesn't give you an or operator you can still achieve the same result without going all the way down to SQL and use Arel directly. By that I mean that you can do it like this:
t = Question.arel_table
Question.where(t[:user_id].in(self.friends.ids).or(t[:username].in(self.friends.usernames)))
Some might say it ain't so pretty, some might say it's pretty simply because it includes no SQL. Anyhow it most certainly could be prettier and there's a gem for it too: MetaWhere
For more info see this railscast: http://railscasts.com/episodes/215-advanced-queries-in-rails-3
and MetaWhere site: http://metautonomo.us/projects/metawhere/
UPDATE: Later Ryan Bates has made another railscast about metawhere and metasearch: http://railscasts.com/episodes/251-metawhere-metasearch
Later though Metawhere (and search) have become more or less legacy gems. I.e. they don't even work with Rails 3.1. The author felt they (Metawhere and search) needed drastic rewrite. So much that he actually went for a new gem all together. The successor of Metawhere is Squeel. Read more about the authors announcement here:
http://erniemiller.org/2011/08/31/rails-3-1-and-the-future-of-metawhere-and-metasearch/
and check out the project home page:
http://erniemiller.org/projects/squeel/
"Metasearch 2.0" is called Ransack and you can read something about it from here:
http://erniemiller.org/2011/04/01/ransack-the-library-formerly-known-as-metasearch-2-0/
Alternatively, you could use Squeel. To my eyes, it is simpler. You can accomplish both the IN (>>) and OR (|) operations using the following syntax:
Question.where{(:user_id >> my{friends.id}) | (:target >> my{friends.usernames})}
I generally wrap my conditions in (...) to ensure the appropriate order of operation - both the INs happen before the OR.
The my{...} block executes methods from the self context as defined before the Squeel call - in this case Question. Inside of the Squeel block, self refers to a Squeel object and not the Question object (see the Squeel Readme for more). You get around this by using the my{...} wrapper to restore the original context.
raw SQL
SELECT *
FROM table
WHERE user_id in (LIST OF friend.ids) OR target in (LIST OF friends.usernames)
with each list comma separate. I don't know the Rails ActiveRecord stuff that well. For AND you would just put a comma between those two conditions, but idk about OR

Encapsulating SQL in a named_scope

I was wondering if there was a way to use "find_by_sql" within a named_scope. I'd like to treat custom sql as named_scope so I can chain it to my existing named_scopes. It would also be good for optimizing a sql snippet I use frequently.
While you can put any SQL you like in the conditions of a named scope, if you then call find_by_sql then the 'scopes' get thrown away.
Given:
class Item
# Anything you can put in an sql WHERE you can put here
named_scope :mine, :conditions=>'user_id = 12345 and IS_A_NINJA() = 1'
end
This works (it just sticks the SQL string in there - if you have more than one they get joined with AND)
Item.mine.find :all
=> SELECT * FROM items WHERE ('user_id' = 887 and IS_A_NINJA() = 1)
However, this doesn't
Items.mine.find_by_sql 'select * from items limit 1'
=> select * from items limit 1
So the answer is "No". If you think about what has to happen behind the scenes then this makes a lot of sense. In order to build the SQL rails has to know how it fits together.
When you create normal queries, the select, joins, conditions, etc are all broken up into distinct pieces. Rails knows that it can add things to the conditions without affecting everything else (which is how with_scope and named_scope work).
With find_by_sql however, you just give rails a big string. It doesn't know what goes where, so it's not safe for it to go in and add the things it would need to add for the scopes to work.
This doesn't address exactly what you asked about, but you might investigate 'contruct_finder_sql'. It lets you can get the SQL of a named scope.
named_scope :mine, :conditions=>'user_id = 12345 and IS_A_NINJA() = 1'
named_scope :additional {
:condtions => mine.send(:construct_finder_sql,{}) + " additional = 'foo'"
}
sure why not
:named_scope :conditions => [ your sql ]