Mongo full text search with score via Ruby driver - ruby

In the Mongo documentation, it says you can do this:
db.articles.find(
{ $text: { $search: "cake" } },
{ score: { $meta: "textScore" } }
)
That works fine when I run it from the mongo console but I can't figure out how to do that via the Ruby driver.
When I do this:
articles.find('$text': { '$search': 'cake' }, score: { '$meta': 'textScore' })
I get
Mongo::Error::OperationFailure: unknown operator: $meta (2)
When I do
articles.find({ '$text': { '$search': 'cake' } }, score: { '$meta': 'textScore' })
I get results but it doesn't include the score and the log message doesn't show that it's using the score: { $meta': 'textScore' }:
{"find"=>"articles", "filter"=>{"$text"=>{"$search"=>"cake"}}}
I guess I just don't grok how the Ruby driver and Mongo CLI convert those into Mongo queries.
I'm using MongoDB version v3.2.7 and the mongo gem version 2.2.5.

Let's look to structure of mongo command:
db.collection.find(
<query>,
{ score: { $meta: "textScore" } }
)
We see that command contains two parts (in this case find)
<query>
options hash
Structure of command in mongo-driver very similar to mongo. But some things are not simple.
In mongo-driver we have Mongo::Collection::View, check (link to source):
articles.find({ '$text': { '$search': 'cake' } }.class # => Mongo::Collection::View
So after analyzing code, i found that you can use projection option, but it is tricky:
articles.find(
{ "$text" => { "$search" => "cake" } },
projection: {
"score" => { "$meta" => "textScore" },
some_field: 1
})
My test was:
posts.find(
{ "$text" => { "$search" => "house" } },
projection: { "score" => { "$meta" => "textScore" }, rooms: 1 }
)
Results is:
{"_id"=>BSON::ObjectId('53f5c8b54465764a82fb0000'),
"rooms"=>2.0,
"score"=>0.5004448398576512}

#farhatmihalko's answer is spot-on. To add to it, this also works:
posts
.find("$text": { "$search": "house" })
.projection(score: { "$meta": "textScore" }, rooms: 1)

Related

Gatsby graphql query works fine in development environment, but not onPostBuild

Problem occured afer updating to Gatbsy v3 and gatsby-plugin-sitemap v3 In my gatsby-config.js, I have configuration for sitemap:
{
resolve: 'gatsby-plugin-sitemap',
options: getSitemapForLanguage('en'),
},
{
resolve: 'gatsby-plugin-sitemap',
options: getSitemapForLanguage('de'),
},
I got the the following function, which generates sitemap based on language passed:
const getSitemapForLanguage = lang => ({
output: `/${lang === 'en' ? '' : lang}/sitemap.xml`,
query: `
{
site {
siteMetadata {
siteUrl
}
}
allMdx(
filter: {frontmatter: {seo: {exclude_from_sitemap: {ne: true}}, languages: {in: "${lang}"}}}
) {
edges {
node {
frontmatter {
pathname
}
}
}
}
}`,
serialize: ({ site, allMdx }) =>
allMdx.edges.map(edge => ({
url: `${site.siteMetadata.siteUrl}${lang === 'en' ? '' : `/${lang}`}${
edge.node.frontmatter.pathname
}`,
changefreq: 'daily',
priority: 0.7,
})),
});
I created this using an in-browser IDE for graphql, that you get when you run gatsby develop. In that IDE I can see results and everything I need, but when I try to build it, I get:
TypeError: Cannot use 'in' operator to search for 'nodes' in undefined
Error of failed build process
For testing purposes I removed , languages: {in: "${lang}"} part in query, but it still doesn't work.
I don't think it even work in gatsby develop since the plugin does only work in production mode. As you can see in the gatsby-plugin-sitemap docs:
NOTE: This plugin only generates output when run in production mode!
To test your sitemap, run: gatsby build && gatsby serve
In gatsby develop is not even firing the plugin, even the query may work in the localhost:8000/___graphql playground.
In my opinion, the issue relies on the language parameter, you can't pass GraphQL variables like this in a Node file, like gatsby-config.js is. The approach is to create separate queries and serialize them somehow. Your configuration should look like this:
{
resolve: `gatsby-plugin-sitemap`,
options: {
query: `
{
site {
siteMetadata {
siteUrl
}
}
deSitemap: allMdx(
filter: {frontmatter: {seo: {exclude_from_sitemap: {ne: true}}, languages: {in: "de"}}}
) {
edges {
node {
frontmatter {
pathname
}
}
}
}
enSitemap: allMdx(
filter: {frontmatter: {seo: {exclude_from_sitemap: {ne: true}}, languages: {in: "en"}}}
) {
edges {
node {
frontmatter {
pathname
}
}
}
}
}`,
serialize: ({ site, allSitePage }) => enSitemap.edges // here you will need to serialize both or append the language at the end
.map(edge => {
return {
url: site.siteMetadata.siteUrl + path, // https://sitemaps.com/page-path
changefreq: 'daily',
priority: 0.7,
links: [
// https://sitemaps.com/page-path
{ lang: 'en', url: site.siteMetadata.siteUrl + path },
// https://sitemaps.com/es/page-path
{ lang: 'de', url: `${site.siteMetadata.siteUrl}/de${path}` },
// The default in case page for user's language is not localized.
{ lang: 'x-default', url: site.siteMetadata.siteUrl + path }
]
};
})
}
}
Source: https://github.com/gatsbyjs/gatsby/issues/4603
Don't try to split it into a separate function, at least until you ensure that is working as expected.

How to find all documents that don't have an array or it is smaller than

I'm trying to find all documents, which either don't have an array tags or the size of the array is smaller than 2. How do I do this? I'm trying this, but doesn't work:
db.collection.find({
'text' => { '$exists' => true }, # I need this one too
'tags' => {
'$or' => [
{ '$exists' => false },
{ '$lt' => ['$size', 2] }
]
}
})
It's Ruby, btw. MongoDB version is 4.
I'm getting:
unknown operator: $or
You can use below query
db.collection.find({
text: { $exists: true },
$or: [{
tags: { $exists: false }
}, {
$expr: { $lt: [{ $size: '$tags' }, 2] }
}]
})
To slightly modify MauriRamone's answer to a smaller version:
db.getCollection('test').find({
$and:[
{"text":{$exists:true} },
{$where: "!this.tags || this.tags.length < 2"}
]
})
However, $where is slow, and other options (such as Anthony's) should be preferred.
Your original query wasn't working because $or only works in expressions, not in fields, and you need an $expr operator for the size.
try using $were in your query, like this:
db.getCollection('test').find({
$and:[
{"text":{$exists:true} },
{
$or:[
{"tags":{$exists:false}},
{$where: "this.tags.length < 2"}
]
}
]
})
I am using Robomongo to test, you should format the query to Ruby.
regards.

ElasticSearch query for items not in given array

I am trying to write a part of a query to filter out any items with a type as "group" and that have a group id that isn't in a given array of ids. I started writing a bool query with a must and must_not but I was getting tripped up on how to write "id not in the given array.
EDIT:
I am actually converting an outdated query using "and" and "not" to be ES 5.5 compatible. Here is the old query that worked.
:and => [
{
term: {
type: 'group'
}
},
{
:not => {
terms: {
group_id: group_ids
}
}
},
{
:not => {
terms: {
user_id: user_ids
}
}
}
]
group_ids and user_ids are arrays.
You probably have not analyzed the arrays with the IDs. You can use a Bool query with a filter clause, and then within that filter start a new bool query with a mustNot clause and within that clause add a terms query with your IDs.
bool: {
must: {
term: {
kind: 'group'
}
},
must_not: [
{
terms: {
group_id: group_ids
}
},
{
terms: {
user_id: user_ids
}
}
]
}

How to use ReQL filter and match command on arrays

I have a table in rethinkdb where each row has following structure -
{
'name':'clustername',
'services':[
{
'name':'service1'
},
{
'name':'service2'
}
]
}
I am running a query to filter service2 object like this
r.table('clusters').filter({"name": "clustername"})
.pluck('services').filter((service) => {
return service("name").match('service2')
})
But this is not returning anything: No results were returned for this query
Can anyone tell why this is happening?
pluck returns sequence, so this query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')
will return:
{
"services": [
{
"name": "service1"
} ,
{
"name": "service2"
}
]
}
You need get services field from it, it will return array with services field of items found by filter.
And after that you need to use your second filter on each item by using map.
So, correct query:
r.table('clusters').filter({"name": "clustername"}).pluck('services')("services").map(item => {
return item.filter(service => {
return service("name").match("service2");
});
})

"OR" query in KeystoneJS Relationship Filters

Currently, all I see examples for is:
{
team: ':team'
}
But I need to be able to filter by multiple values ($or query)
Something like:
winner: {
type: Types.Relationship,
ref: 'Team',
filters: [
{
_id: ':homeTeam'
},
{
_id: ':awayTeam'
}
]
}
Is this possible?
Try the following code:
filters:{_id:{$in:[id_array]}}

Resources