How should I write the query for ElasticSearch in Rails? - ruby-on-rails

I have to write a query in my SortBuilder.rb in which I want the count of occurrence of a word (that is coming in this method in the variable value) in the results and sort the results according to the word count.
I also want to display the count later so I want to store them in a variable.
My current logic is --
sort: [
query: value,
aggs: {
my_terms: {
filters: {
value: { term: { "title" => "#{value}" }}
}
}
}
]

Related

Nested query GraphQL : Syntax error - iOS

I am working with Apollo GraphQL and have to call nested query .But while call the Query in .graphql file it showing
Syntax error : Expected Name, found {
Let me know how to call Nested query of GraphQL.
I have to call getAllproduct{....} query with the specified parameters.Here the FilterInput having the parameter as location with another pattern of query , so I don't know how to call this nested query.Anyone please help me to find out the solution.Thanks...
If an argument is an Input Object Type (as opposed to a Scalar), you can include the fields of the Input Object Type by using curly brackets.
query MyProductsQuery {
allProducts(
pageNumber: "someString"
filter: {
title: "someOtherString"
yearFrom: 1900
location: {
city: "yetAnotherString"
state: "FL"
}
}
) {
id
# other product fields
}
}
Of course, hardcoding those values in a .graphql file is not very helpful. You probably want to be able to swap those values out programatically. So here's what that same query looks like with variables:
query MyProductsQuery($pageNumber: String, $filter: FilterInput) {
allProducts(pageNumber: $pageNumber, filter: $filter) {
id
# other product fields
}
}
Your variables are passed in separately from your query and unlike your query, are not a GraphQL document. They are just JSON:
{
"pageNumber": "someString",
"filter": {
"title": "someOtherString",
"yearFrom": 1900,
"location": {
"city": "yetAnotherString",
"state": "FL"
}
}
}

Merging elastic search query and filter doesn't work correctly

I have to merge to query and filter. we did this in elastic search 2.4 using filter_mode(:must) after merging query and filter.
Now filter_mode and query_mode in incompatible. I am merging query using 'and'. I am using chewy for using elastic search in rails framework
query_string = index.query(bool: { should: [{ term: { title: query } }, { term: { tags: query } }] })
domain_filter = index.filter(term: { domain_id: domain_id })
merged_query = query_string.and(domain_filter)
now query_string product 1 result. so filter should produce a subset of that result.
instead when I do query_string.filter(term: { domain_id: domain_id })
It produces 44 results. My main goal is to merge both the query to filter results on domain_id. when i write domain_filter as query it produced a filterd result
domain_filter = index.query(term: { domain_id: domain_id })
merged_query = query_string.and(domain_filter)
the above-merged query produces the correct result. but I think its wrong to use it like this. I want to use ES filter. can someone help me with this?
Detailed query
produced with query_string.and(domain_filter)
{:index=>["resource_domain"], :type=>["resource_domain"], :body=>{:query=>{:bool=>{:should=>[{:term=>{:title=>"club-4"}}, {:term=>{:tags=>"club-4"}}], :filter=>{:term=>{:domain_id=>6}}}}}}
the above one produces wrong result
Expected:
{:index=>["resource_domain"], :type=>["resource_domain"], :body=>{:query=>{:bool=>{:must=>[{:bool=>{:should=>[{:term=>{:title=>"club-4"}}, {:term=>{:tags=>"club-4"}}]}}, {:term=>{:domain_id=>6}}]}}}}
You need to shove your domain filter into a bool/filter clause, like this
query = index.query(bool: { minimum_should_match: 1, should: [{ term: { title: query } }, { term: { tags: query } }], filter: [{term: { domain_id: domain_id }}] })

Grails namedQuery sort order by multiple columns

Given a namedQuery:
class MyDomainObject {
String someProperty
static namedQueries = {
myNamedQuery {
// some implementation here
}
}
}
I can use it to generate a list, sorted by a single key, like this (documentation for 2.4.3 here):
def resultsList = MyDomainObject.myNamedQuery.list(sort: "someProperty", order: "desc")
How do I order the results by multiple columns? I'd like to be able to define the sort parameters dynamically, not define them in the query.
I'm sure there's a better way, but I ended up creating another named query that I can concatenate onto my chosen one (I could always incorporate into the original query too).
// expects to be passed a List containing a series of Maps
orderByMultipleColumns { List columnsToSortBy ->
columnsToSortBy.each { Map field ->
order("${field.fieldName}", field.fieldOrder)
}
}
// usage:
List orderByList = []
// ...
// within some loop that I use:
orderByList << [fieldName: someValue, fieldOrder: dir] // dir == 'asc' or 'desc'
// ...
MyDomainObject.myNamedQuery().orderByMultipleColumns(orderList).listDistinct(max: length, offset: start)

Mongoid max and embeded collections

I have a Collection Report embeds submissions
class Report
embeds_many :submissions
class Submission
embedded_in :report
field :date_submitted, type: TimeWithZone
field :mistakes, type: Integer
I am trying to create a scope on Report
I want to add a scope query with two parts
get the latest submission (given by max date_submitted) that also has zero mistakes
I can create a scope for the mistakes part, but cannot work out how to get the latest submission
scope :my_scope, where("submissions.mistakes" => 0)
So this report would be returned as it's last enter in submissions has zero mistakes
Report
"submissions" : [
{
"date_submitted" : ISODate("2014-01-28T13:00:00Z"),
"mistakes" : 11
},
{
"date_submitted" : ISODate("2014-03-08T13:00:00Z"),
"mistakes" : 0
}
]
where this one wouldn't be returned
Report
"submissions" : [
{
"date_submitted" : ISODate("2014-01-28T13:00:00Z"),
"mistakes" : 0
},
{
"date_submitted" : ISODate("2014-03-08T13:00:00Z"),
"mistakes" : 11
}
]
This is because you are not filtering the element of the embedded array but the document that contains that element.
There could be an $elemMatch clause here which allows you to combine the conditions on a single element. But find does not have any operation for getting the max value as it were. This is not to be confused with the $max query modifier, which actually clips the index in use to not search beyond those bounds.
So here you use aggregate:
db.collection.aggregate([
// Optionally query to match and filter your documents.
//{ "$match: { /* Same conditions as find */ } },
// Unwind the array
{ "$unwind": "$submissions" },
// Filter all but 0 mistakes
{ "$match": { "submissions.mistakes": 0 } },
// Group the results, taking the max entry and presuming by document `_id`
{ "$group": {
"_id": "$_id",
"date_submitted": { "$max": "$submissions.date_submitted" }
}}
])
That is the general process for filtering the elements of an array. You may look into your driver implementation of aggregate, but the form is always the pipeline represented as an array of documents (hashes) in this form. Possibly using the moped form for getting the collection method. So something like:
Report.collection.aggregate([ /* stages */ ])
For more information on returning the original document form if that is what your requirement is then see here.

MongoDB/Mongoid: search for documents matching first item in array

I have a document that has an array:
{
_id: ObjectId("515e10784903724d72000003"),
association_chain: [
{
name: "Product",
id: ObjectId("4e1e2cdd9a86652647000003")
}
],
//...
}
I'm trying to search the collection for documents where the name of the first item in the association_chain array matches a given value.
How can I do this using Mongoid? Or if you only know how this can be done using MongoDB, if you post an example, then I could probably figure out how to do it with Mongoid.
Use the positional operator. You can query the first element of an array with .0 (and the second with .1, etc).
> db.items.insert({association_chain: [{name: 'foo'}, {name: 'bar'}]})
> db.items.find({"association_chain.0.name": "foo"})
{ "_id" : ObjectId("516348865862b60b7b85d962"), "association_chain" : [ { "name" : "foo" }, { "name" : "bar" } ] }
You can see that the positional operator is in effect since searching for foo in the second element doesn't return a hit...
> db.items.find({"association_chain.1.name": "foo"})
>
...but searching for bar does.
> db.items.find({"association_chain.1.name": "bar"})
{ "_id" : ObjectId("516348865862b60b7b85d962"), "association_chain" : [ { "name" : "foo" }, { "name" : "bar" } ] }
You can even index this specific field without indexing all the names of all the association chain documents:
> db.items.ensureIndex({"association_chain.0.name": 1})
> db.items.find({"association_chain.0.name": "foo"}).explain()
{
"cursor" : "BtreeCursor association_chain.0.name_1",
"nscanned" : 1,
...
}
> db.items.find({"association_chain.1.name": "foo"}).explain()
{
"cursor" : "BasicCursor",
"nscanned" : 3,
...
}
Two ways to do this:
1) if you already know that you're only interested in the first product name appearing in "association_chain", then this is better:
db.items.find("association_chain.0.name":"something")
Please note that this does not return all items, which mention the desired product, but only those which mention it in the first position of the 'association_chain' array.
If you want to do this, then you'll need an index:
db.items.ensureIndex({"association_chain.0.name":1},{background:1})
2) if you are looking for a specific product, but you are not sure in which position of the association_chain it appears, then do this:
With the MongoDB shell you can access any hash key inside a nested structure with the '.' dot operator! Please note that this is independent of how deeply that key is nested in the record (isn't that cool?)
You can do a find on an embedded array of hashes like this:
db.items.find("association_chain.name":"something")
This returns all records in the collection which contain the desired product mentioned anywhere in the association_array.
If you want to do this, you should make sure that you have an index:
db.items.ensureIndex({"association_chain.name":1},{background: 1})
See "Dot Notation" on this page: http://docs.mongodb.org/manual/core/document/
You can do this with the aggregation framework. In the mongo shell run a query that unwinds the documents so you have a document per array element (with duplicated data in the other fields), then group by id and any other field you want to include, plus the array with the operator $first. Then just include the $match operator to filter by name or mongoid.
Here's the query to match by the first product name:
db.foo.aggregate([
{ $unwind:"$association_chain"
},
{
$group : {
"_id" : {
"_id" : "$_id",
"other" : "$other"
},
"association_chain" : {
$first : "$association_chain"
}
}
},
{ $match:{ "association_chain.name":"Product"}
}
])
Here's how to query for the first product by mongoid:
db.foo.aggregate([
{ $unwind:"$association_chain"
},
{
$group : {
"_id" : {
"_id" : "$_id",
"other" : "$other"
},
"association_chain" : {
$first : "$association_chain"
}
}
},
{ $match:{ "association_chain.id":ObjectId("4e1e2cdd9a86652647000007")}
}
])

Resources