I have been looking at the documentation but I can't seem to figure out how to correctly create a filter object for the buildfire.datastore.search query.
I have an address property on my object and I want to be able to type in a partial amount of the address and have it return. Below are the filter objects I have tried to pass into the search query:
search = {filter: {"$json.address": {"$regex": `/${this.state.search}/`}}};
search = {filter: {'$regex': {'$json.address': this.state.search}}};
Neither have worked. End goal is:
buildfire.datastore.search(search, 'location', cb);
EDIT:
I even tried to hardcode the regex in the docs:
"$or" : [
{"description": {"$regex":"/new /"}}
]
and it didn't work (I replaced 'new' with a string I knew would show).
I just inserted the following on the control side:
for(let i = 0 ; i < 50 ; i++) {
buildfire.datastore.insert({
name: "Address" + i
,address: i + " " + (i % 2 ? "Main ":"4th ") + (i % 3 ? "ave":"st" )
},function(){});
}
then did a search on the widget side like this:
<body>
<input type="text" id="criteria" /><button onclick="search()">Search</button>
<div id="results"></div>
<script>
function search(){
var cri = document.getElementById("criteria").value;
buildfire.datastore.search( {filter:{"$json.name": {"$regex": cri } } } , function(err,results){
document.getElementById("results").innerHTML = JSON.stringify(results);
});
}
</script>
</body>
Works fine. Given if you want the search to be more complex then you need to modify the regex statement for example case insensitivity.
Hope this helps
Related
I have this in my algolia file for my jekyll site.
<script>
const search = instantsearch({
appId: '{{ site.algolia.application_id }}',
apiKey: '{{ site.algolia.search_only_api_key }}',
indexName: '{{ site.algolia.index_name }}',
searchParameters: {
restrictSearchableAttributes: [
'title',
'content'
],
facetFilters: ['type:post']
},
});
const hitTemplate = function(hit) {
let date = '';
if (hit.date) {
date = moment.unix(hit.date).format('L');
// date = moment.unix(hit.date).format('MMM Do YY');
modifiedDate = moment.unix(hit.last_modified_at).format('MMM Do YY');
}
const url = hit.url;
const title = hit._highlightResult.title.value;
const content = hit._highlightResult.html.value;
return `
<div class="post-list">
<span class="post-date-list-wrap">
<span class="post-date-list">${date}
<span class="post-title"> ${title} </span>
</span>
${content}
</div>
`;
}
const hitTemplateTrans = function(hit) {
let date = '';
if (hit.date) {
date = moment.unix(hit.date).format('MMM DD YYYY');
}
const url = hit.url;
const title = hit._highlightResult.title.value;
const content = hit._highlightResult.html.value;
return `
<div class="post-list">
<span class="post-date-list-wrap">
<span class="post-date-list">${date}
<span class="post-title"> ${title}</span>
</span>
</span>
</div>
`;
}
search.addWidget(
instantsearch.widgets.searchBox({
container: '#search-searchbar',
placeholder: 'search notes',
autofocus: true
})
);
search.addWidget(
instantsearch.widgets.hits({
container: '#search-hits',
templates: {
empty: 'No results',
item: hitTemplate
},
})
);
search.start();
</script>
Without typing anything in the search box I have the list of articles
with the excerpt, the short introduction of the article.
That's because I have ${content} to show the highlights when someone
types the search term.
That's fine and everything is working but... I don't want to show the contents of each item when the search box is empty.
If the search box is empty I would like to keep only the title and the date
but if the search box is not empty just show the title/date and the excerpt as it's usual.
It seems like an easy task but I'm stuck right now, I tried removed the content tag and put in the hit transformation function, but it doesn't work.
The instantsearch.js library has a function hook, called searchFunction, you can define when instanciating the library. That is called right before any search is performed. You can use it to check if the current query is empty or not, and adapt your layout based on this knowledge.
Here is a slightly edited script (irrelevant parts removed) that should let you do what you're looking for:
let additionalClass = '';
const search = instantsearch({
[…]
searchFunction: function(helper) {
if (helper.getState().query === '') {
additionalClass = 'post-item__empty-query';
} else {
additionalClass = '';
}
helper.search()
}
});
[…]
const hitTemplate = function(hit) {
return
`<div class="post-item ${additionalClass}">
[…]
</div>`
;
}
.post-item__empty-query .post-snippet {
display: none;
}
What it does is defining a global variable (additionalClass) that will be used in the hit template (added alongside item-post, at the root level).
Right before everysearch, we check if the query is empty. If so, we set additionalClass to item-post__empty_query. We also defined in CSS that when this class is applied, the content should be hidden.
All of that together makes the title + date displayed when no search is performed, and the content displayed only when an actual keyword is searched for.
Hope that helps,
I have two inputs.
I want the two inputs to have the same look and feel see below:
The first input use autocomplete and allows the user to select a list of terms => I use p:autocomplete (see Primefaces documentation on autocomplete)
This input works fine.
For the second input, I would like to have the same display but without any autocompletion : the user just enter a list of terms with no autocompletion at all.
I tried to have a fake autocomplete that return the value given by the user but it is too slow and the behaviour is not correct when the user quit the input.
Any idea is welcome.
After a quick look at the PrimeFaces javascript code of the autoComplete and a few hours experimenting with it, I came up with a solution. It involves overriding the bindKeyEvents and in it deciding to call the original one or not, adding detection for the space key ('selecting a tag') and when pressed, add the tag and fire the selectionEvent (if ajax is used). Place the following code in your page or in an external javascript file
<script>
//<![CDATA[
if(PrimeFaces.widget.AutoComplete) {
PrimeFaces.widget.AutoComplete = PrimeFaces.widget.AutoComplete.extend ( {
bindKeyEvents: function() {
if (this.input.attr('data-justTags')) {
var $this = this;
this.input.on('keyup.autoComplete', function(e) {
var keyCode = $.ui.keyCode,
key = e.which;
}).on('keydown.autoComplete', function(e) {
var keyCode = $.ui.keyCode;
$this.suppressInput = false;
switch(e.which) {
case keyCode.BACKSPACE:
if ($this.cfg.multiple && !$this.input.val().length) {
$this.removeItem(e, $(this).parent().prev());
e.preventDefault();
}
break;
case keyCode.SPACE:
if($this.cfg.multiple) {
var itemValue = $this.input.val();
var itemDisplayMarkup = '<li data-token-value="' +itemValue + '"class="ui-autocomplete-token ui-state-active ui-corner-all ui-helper-hidden">';
itemDisplayMarkup += '<span class="ui-autocomplete-token-icon ui-icon ui-icon-close" />';
itemDisplayMarkup += '<span class="ui-autocomplete-token-label">' + itemValue + '</span></li>';
$this.inputContainer.before(itemDisplayMarkup);
$this.multiItemContainer.children('.ui-helper-hidden').fadeIn();
$this.input.val('').focus();
$this.hinput.append('<option value="' + itemValue + '" selected="selected"></option>');
if($this.multiItemContainer.children('li.ui-autocomplete-token').length >= $this.cfg.selectLimit) {
$this.input.css('display', 'none').blur();
$this.disableDropdown();
}
$this.invokeItemSelectBehavior(e, itemValue);
}
break;
};
});
} else {
//console.log("Original bindEvents");
this._super();
}
}
});
}
//]]>
</script>
For deciding on when to call the original one or not, I decided to use a passThrough attribute with a data-justTags name. e.g. pt:data-justTags="true" (value does not matter, so pt:data-justTags="false" is identical to pt:data-justTags="true"). A small html snippet of this is:
<p:autoComplete pt:data-justTags="true" multiple="true" value="#{myBean.selectedValues}">
And do not forget to add the xmlns:pt="http://xmlns.jcp.org/jsf/passthrough" namespace declaration.
I found a component that could do the job : http://www.butterfaces.org/tags.jsf
I have a simple search form that looks like this:
<form action="http://www.theurltosearch.com" method="post">
<input class="search-box" name="query" type="text" value="search all reports" />
<input type="submit" name="search" />
</form>
What I'm trying to accomplish
The search is pointing to whats really a filtering system using tags.
In order for the user to properly see the results of what they queried the query url has to look something like this http://www.theurltosearch.com/#/Kboxes the # and the K are important as its how the tagging system returns results where K stands for keyword.
For multi term queries the url has to look like this separated by a comma http://www.theurltosearch.com/#/Kboxes,Kmoving
A user should also get results when they enter a string query something like http://www.theurltosearch.com/#/K%22more%20stuff%22
Right now if someone used the search it would just take them to the url and not actually display any results matching their query.
How can I manipulate the url string to return the results how I've shown above?
My actual attempt
<script type="text/javascript">
window.onload = function(){
var form = document.getElementById("reports-search");
form.onsubmit = function(){
var searchText = document.getElementById("search-reports");
window.location = "http://www.urltosearch.com/#/K" + searchText.value;
return false;
};
};
</script>
<form id="reports-search" method="get">
<input class="search-box" id="search-reports" type="text" value="search all reports" /><!--search term was analysis-->
<input type="submit" name="search" />
</form>
returns
http://www.urltosearch.com/#/Kanalysis
and displays all results with the analysis tag
This attempt works succesfully if someone is searching a single keyword but not if the user is searching multiple or a string
How do I change the JS to achieve the other options?
Okay, here's a dog'n'bird implementation (ruff,ruff, cheap,cheap).
I've allowed the user to enter multiple terms, each separated with the pipe character | If you wish to allow the user to enter a url in essentially the same format as they'd receive by 'normal' keywords, you may wish to check the entered text first and if found, simply pass it straight through without changing it.
You'll notice, I've wrapped all search terms with " ", regardless of whether the term is multi-word or not. You could easily differentiate between a single-word term and a multi, by searching the string for a space character after the string.trim has removed leading/trailing spaces. I.e
if (trimmedTerm.indexOf(' ') == -1)
{
// single word search term
}
else
{
// multi-word search term here
}
Anyway, here's a working demo, hope it gives insight.
function byId(id){return document.getElementById(id)}
// useful for HtmlCollection, NodeList, String types
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
byId('goBtn').addEventListener('click', onGoBtnClicked);
}
function onGoBtnClicked(evt)
{
// get the user input
var inputString = byId('userInput').value;
// split it into an array of terms, based on the | char
var searchTerms = inputString.split('|');
// init the result
var result ='';
// for each element in the array of search terms, call the function to trim wrap with "" and encode
forEach(searchTerms, addCurTermToResult);
// update the output display
byId('output').textContent = 'http://www.theurltosearch.com/#/' + result;
function addCurTermToResult(curTerm, index)
{
if (index != 0) // put a comma before all terms except the first one
result += ',';
var trimmedTerm = curTerm.trim(); // remove leading/trailing spaces
result += 'K' + encodeURI('"' + trimmedTerm + '"' ); // wrap with "" then URI encode it, suitable for use as a URL
}
}
.panel
{
border: solid 1px black;
border-radius: 8px;
padding: 8px;
background-color: #eef;
display:inline-block;
}
.panel textarea
{
width: 500px;
height: 200px;
}
<div class='panel'>
<textarea type='text' id='userInput' placeholder='Enter tags or a url. tags should be seperated with the | character'></textarea>
<div style='text-align: center'><button id='goBtn'>Submit</button></div>
<hr>
<label>URL: <span id='output'></span></label>
</div>
I am trying to implement simple search form for users to look up other users.. I have been looking around the web for quite some time, but lot of the resources seem to be outdated, either for rails 3 or retired gems...
Can anyone pin point me to a recent rails 4 resources for simple search or show me the skeleton code to start? thank you in advance!
https://github.com/jhund/filterrific
scope :search_query, lambda { |query|
return nil if query.blank?
terms = query.to_s.downcase.split(/\s+/)
# replace "*" with "%" for wildcard searches,
# append '%', remove duplicate '%'s
terms = terms.map { |e|
(e.gsub('*', '%') + '%').gsub(/%+/, '%')
}
# configure number of OR conditions for provision
# of interpolation arguments. Adjust this if you
# change the number of OR conditions.
num_or_conds = 2
sql = "(LOWER(foo.first_name) LIKE ? OR LOWER(foo.last_name LIKE ?)"
where(
terms.map { |term| sql }.join(' AND '), *terms.map { |e| [e] * num_or_conds }.flatten
)
}
That would be a simple example of searching a user by either first_name or last_name.
Filterrific is quite good, but can be heavy on the back side when it does the query if you have many records.
For a very simple search on Users username you can do in your view :
<%= form_tag users_path, :method => 'get', :id => 'users_search' do %>
<p>
<%= text_field_tag :search, params[:search] %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
In your User model you must define a 'search' method :
#users_controller.rb
def self.search(user_name)
if user_name
user_name.downcase!
where('LOWER(name) LIKE ?', "%#{user_name}%")
else
all
end
end
and finally in your controller you can call this method :
def index
#users = User.search(params[:search])
end
The route can be defined as a GET like your default route for the page :
#routes.rb
resources :users
I'm trying to run a barely modified version of SlickGrid's example1-simple.html. I'm on Mac OS X Lion, seeing the same behavior with Chrome/Safari/Firefox. Once I have all of the CSS/JS dependencies in place I can directly load the example HTML page (and my slightly modified version) without any trouble. However when I try to serve basically the same page with node/express/jade (using res.render()), the header (column names) row loads, and looking through the rendered HTML I can see that the first row of my data loads but I don't see it in the browser (I'm trying to load 10 rows of data). All of the relevant CSS/JS files seem to be loading properly and I don't see any errors either in the browser console or my node console. I've copied the entirety of the grid-canvas div below.
<div class="grid-canvas" style="height: 250px; width: 240px; ">
<div class="ui-widget-content slick-row even" row="0" style="top:0px">
<div class="slick-cell l0 r0">Battery test #1.csv</div>
<div class="slick-cell l1 r1">1024</div>
<div class="slick-cell l2 r2">1335237255112</div>
</div>
</div>
I'm pretty sure the JavaScript is all right, as it pretty much exactly matches the example HTML page and runs fine when I substitute it in the example1-simple.html document and access it directly. Also when rendering the page with express I can set a breakpoint in my browser at the new Slick.Grid() call and see that the 10-item array of row data is being sent, just not ultimately rendered.
I have basic old-school debug skills (once upon a time I did a lot of Windows programming in VB) but I'm very open to suggestions as to relevant debug tools and techniques I could bring to bear on this type of problem.
Here is my layout.jade:
!!!
html
head
title= title
link(rel="stylesheet", href="/stylesheets/slick.grid.css", type="text/css")
link(rel="stylesheet", href="/stylesheets/css/smoothness/jquery-ui-1.8.19.custom.css", type="text/css")
body!= body
And the jade page with my test code:
#content
#fileGrid
script(src="/javascripts/lib/jquery-1.7.2.js")
script(src="/javascripts/lib/jquery.event.drag-2.0.min.js")
script(src="/javascripts/lib/slick.core.js")
script(src="/javascripts/lib/slick.grid.js")
script
var grid;
var columns = [
{id: "fileName", name: "File Name", field: "fileName"},
{id: "fileSize", name: "File Size", field: "fileSize"},
{id: "lastUpdate", name: "Last Updated", field: "lastUpdate"} // use mtime
];
var options = {
enableCellNavigation: true,
enableColumnReorder: false
};
$(function() {
var timeStamp = new Date();
var numRows = 10;
var data = [];
for (var i = 1 ; i <= numRows ; i++) {
data[i-1] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString()
}
};
grid = new Slick.Grid("#fileGrid", data, columns, options);
});
I figured it out. The problem was that I didn't specify a size for my #fileGrid div either in my jade/html or anywhere else in the css, and SlickGrids seemed to assume that the size was zero, and thus didn't render any rows. I changed the jade declaration to #fileGrid(style="width:500px; height:600px) and that fixed it. In retrospect that was really the only thing different between my jade page and the html example.
This looks fishy:
for (var i = 0 ; i <= numRows ; i++)
data[i-1] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString(),
};
Why data[i-1] and i <= numRows? You're trying to assign to data[-1] up until data[numRows], where you should assign to data[0] until data[numRows - 1].
On a side note, I would wrap that for loop in braces, even though it's not strictly necessary. Also, the trailing comma (on the last row) will break in Internet Explorer:
for (var i = 0 ; i < numRows ; i++) {
data[i] = {
fileName: "Battery test #" + i + ".csv",
fileSize: i * 1024,
lastUpdate: Date.now().toString()
};
}
You also have a trailing comma in your columns assignment.
#BRValentine, you are right. Slickgrid grid container element must be given an height (width is optional - by default uses 100% available). If you do not provide an height to the container element, Slickgrid does not render the rows.
specifying the target grid div dimensions solved my problem. This is nowhere in instructions... I'm leaving it here so that others can find it.
<div id="classGrid" style="width:500px; height:600px;"></div>