Rails Mongoid search parents elements that have at least one child - search

I have a rails 4 app and I have 2 objects: stores and books (referenced 1:n relation).
A store has many books, each book belongs to one store. Some stores don't have any books.
How can I make a query to find the 3 latest store that has at least 1 book - and the 3 latest stores that have no books ?
#stores = Store.order_by(:created_at => 'desc').limit(4).uniq
#books = Book.order_by(:created_at => 'desc').limit(4).uniq
This work but I don't know how to do a where(book.exists?) for store, or for the #books to make sure each book belongs to a unique store.

Basic CRUD operations in MongoDB operate on a single collection,
"join" capability is not yet implemented that would allow them to operate across two collections.
Once you understand this, you can easily construct two operations that will do what you want.
The following is the most obvious given the schema hints that you supplied.
To do anything more, you probably want to consider schema changes like embedding.
By definition, your supplied schema only allows a book to belong to one (unique by definition) store.
You can see this from the inspection of the books, that there is a single value store_id for each book.
Hope that this helps your understanding.
app/models/store.rb
class Store
include Mongoid::Document
include Mongoid::Timestamps
field :name, type: String
has_many :books
end
app/models/book.rb
class Book
include Mongoid::Document
include Mongoid::Timestamps
field :title, type: String
belongs_to :store
end
test/unit/store_test.rb
require 'test_helper'
require 'pp'
class StoreTest < ActiveSupport::TestCase
def setup
Mongoid.default_session.drop
end
test '0. mongoid version' do
puts "\nMongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}"
end
test 'store query has book, does not have book' do
[
["Amazon.com", ["Outlander", "Taking It All"]],
["Barnes & Noble", ["Big Little Lies"]],
["Goodreads", []],
["Greenlight Bookstore", []],
["Powell's Books", ["Gone Girl", "Dark Skye"]],
["Strand Books", []]
].each do |store, books|
store = Store.create(name: store)
books.each do |title|
store.books << Book.create(title: title)
end
sleep 1
end
assert_equal(6, Store.count)
assert_equal(5, Book.count)
puts
store_ids_with_books = Book.distinct(:store_id)
latest_stores_with_a_book = Store.in(_id: store_ids_with_books).order_by(:created_at => 'desc').limit(3).to_a
puts "three latest stores with a book:"
pp latest_stores_with_a_book
latest_stores_without_a_book = Store.nin(_id: store_ids_with_books).order_by(:created_at => 'desc').limit(3).to_a
puts "three latest stores without a book:"
pp latest_stores_without_a_book
puts "books:"
pp Book.all.to_a
end
end
rake test
Run options:
# Running tests:
[1/2] StoreTest#test_0._mongoid_version
Mongoid::VERSION:3.1.6
Moped::VERSION:1.5.2
[2/2] StoreTest#test_store_query_has_book,_does_not_have_book
three latest stores with a book:
[#<Store _id: 53f257287f11ba75e5000008, created_at: 2014-08-18 19:42:32 UTC, updated_at: 2014-08-18 19:42:32 UTC, name: "Powell's Books">,
#<Store _id: 53f257257f11ba75e5000004, created_at: 2014-08-18 19:42:29 UTC, updated_at: 2014-08-18 19:42:29 UTC, name: "Barnes & Noble">,
#<Store _id: 53f257247f11ba75e5000001, created_at: 2014-08-18 19:42:27 UTC, updated_at: 2014-08-18 19:42:27 UTC, name: "Amazon.com">]
three latest stores without a book:
[#<Store _id: 53f257297f11ba75e500000b, created_at: 2014-08-18 19:42:33 UTC, updated_at: 2014-08-18 19:42:33 UTC, name: "Strand Books">,
#<Store _id: 53f257277f11ba75e5000007, created_at: 2014-08-18 19:42:31 UTC, updated_at: 2014-08-18 19:42:31 UTC, name: "Greenlight Bookstore">,
#<Store _id: 53f257267f11ba75e5000006, created_at: 2014-08-18 19:42:30 UTC, updated_at: 2014-08-18 19:42:30 UTC, name: "Goodreads">]
books:
[#<Book _id: 53f257247f11ba75e5000002, created_at: 2014-08-18 19:42:28 UTC, updated_at: 2014-08-18 19:42:28 UTC, title: "Outlander", store_id: "53f257247f11ba75e5000001">,
#<Book _id: 53f257247f11ba75e5000003, created_at: 2014-08-18 19:42:28 UTC, updated_at: 2014-08-18 19:42:28 UTC, title: "Taking It All", store_id: "53f257247f11ba75e5000001">,
#<Book _id: 53f257257f11ba75e5000005, created_at: 2014-08-18 19:42:29 UTC, updated_at: 2014-08-18 19:42:29 UTC, title: "Big Little Lies", store_id: "53f257257f11ba75e5000004">,
#<Book _id: 53f257287f11ba75e5000009, created_at: 2014-08-18 19:42:32 UTC, updated_at: 2014-08-18 19:42:32 UTC, title: "Gone Girl", store_id: "53f257287f11ba75e5000008">,
#<Book _id: 53f257287f11ba75e500000a, created_at: 2014-08-18 19:42:32 UTC, updated_at: 2014-08-18 19:42:32 UTC, title: "Dark Skye", store_id: "53f257287f11ba75e5000008">]
Finished tests in 6.193234s, 0.3229 tests/s, 0.3229 assertions/s.
2 tests, 2 assertions, 0 failures, 0 errors, 0 skips

Related

How do I format [future, non-current] dates in Eleventy + Nunjucks?

I'm building a site with a CMS (Netlify) for a local band, and they have future gig dates they will put on the site. So far the dates show up as very long non-formatted strings that include the time and time zone. I'm trying to figure out how to format the dates to be simpler (day, date, time for example).
I've tried plugins like nunjucks-date but I'm a little confused about how to use a plugin (and filters) in this case.
My repo: https://github.com/mollycarroll/serapis-eleventy-2
Example gig entry:
---
layout: gig
venue: Cedar Lake Cellars
date: 2022-05-28
time: 6pm
city: Wright City, MO
---
Gig template:
<h2>{{ venue }}</h2>
<h4>{{ city }} {{ date }} {{ time }}</h4>
config.yml for the CMS:
- name: 'gigs'
label: 'Shows'
folder: 'src/gigs'
create: true
slug: '{{month}}-{{day}}-{{venue}}'
fields:
- { label: 'Layout', name: 'layout', widget: 'hidden', default: '_includes/gig.njk' }
- { label: 'Date', name: 'date', widget: 'date', default: '' }
- { label: 'Time', name: 'time', widget: 'string', default: '' }
- { label: 'Venue', name: 'venue', widget: 'string', default: '' }
- { label: 'City', name: 'city', widget: 'string', default: '' }
Thanks for any help.
First, you should create a filter, let's say src/filters/date.js with the following content:
const { DateTime } = require("luxon");
// Add a friendly date filter to nunjucks.
// Defaults to format of LLLL d, y unless an
// alternate is passed as a parameter.
// {{ date | friendlyDate('OPTIONAL FORMAT STRING') }}
// List of supported tokens: https://moment.github.io/luxon/docs/manual/formatting.html#table-of-tokens
module.exports = (dateObj, format = 'LLLL d, y') => {
return DateTime.fromISO(dateObj, { zone: "Europe/Amsterdam", locale: "en" }).toFormat(format);
};
Make sure you check Luxon documentation for details. Then add the filter in .eleventy.js:
module.exports = function(eleventyConfig) {
...
eleventyConfig.addFilter("date", require("./src/filters/date.js"));
...
};
Now you can use it in Nunjacks with a default value {{ date }}, in this example 'LLLL d, y', or any value you need at a certain position on your website {{ date | date('dd. LLLL yyyy.') }}. This can be very useful if you need at some point just month and year or just day and month.
You can even create multiple language filters, like dateEn.js and dateDe.js, and format each to its own language if you have a multilingual site.
Hope this helps.
EDIT: In order for this filter to work the dateObj should be in ISO 8601 format.

Iterating on jade-Array of objects

Please help me in iterating the following array of objects. I'm able see the array of objects in console,But I could not iterate it in jade
[{title: "Stamp. The Hobbit", description: "Stamp. <b>The Hobbit</b>. I know
it was on Friday …n the day when there was not a lot of creativity.",
originalLink: null, summary: null, date: "2017-09-25T06:42:50.000Z"}
{title: "Fall/winter Tolkien/LOTR events: your 'go-to' list",
description: "Audiences will thrill to the music from his legend…bbit</b>
with scores from his films: The ...", originalLink: null, summary:
null, date: "2017-09-25T05:26:15.000Z"},
{title: "Watch what happens when Darth Vader swaps galaxy far, far away ...
for Middle Earth", description: "The narrative of Middle Earth: Shadow of
War is ba…nts of <b>The Hobbit</b> and The Lord of ...", originalLink:
null, summary: null, date: "2017-09-25T03:56:15.000Z"}
{title: "The hobbit sword", description: "Posted by alex in kids / baby
stuff, toys in Munster, Cork. 24 September 2017...210966215.", originalLink:
null, summary: null, date: "2017-09-24T19:30:12.000Z"}
{title: "The hobbit as high fantasy essay", description: "Im science teacher
is stupid hes making me write a… like r u serious. Problems of urbanization
essay", originalLink: null, summary: null, date: "2017-09-24T19:26:02.000Z"}
{title: "Lego The Hobbit Bagend Bundle", description: "Lego <b>The
Hobbit</b> Bagend Bundle, Used Lego &a…, Dublin, Ireland for 130.00 euros on
Adverts.ie.", originalLink: null, summary: null, date: "2017-09-
24T10:42:38.000Z"}]
server code:
res.render('index', { aaa: JSON.stringify(result) });
jade :
extends layout
block content
script.
console.log("hi");
var bbb = !{aaa}
console.log(bbb)
each value in bbb
p= value.title
I think you just have to set value.title like this :
each value in bbb
p !{value.title}
Hope it helps.

Mongoose: moment().format not working

Im having Mongoose schema as follows:
createdOn: {
type: String,
//default: Date.now,
default: moment(new Date(Date.now())).format('MMM Do YY') //npm install moment --save # npm
},
updatedOn: {
type: String,
//default: Date.now
default: moment(new Date(Date.now())).format('MMM Do YY')
}
UPDATE:
After changing the type to String,
I get :
Fri Jul 28 2017 14:43:40 GMT+0530 (IST)
How to remove the time and SMT.
I need to keep only Fri Jul 28 2017
I installed Moment package of node js (http://momentjs.com/)
Whats wrong in my schema above:
I get
MongooseError: Cast to date failed for value "Aug 7th 17" at path "updatedOn"
message: 'Cast to date failed for value "Aug 7th 17" at path "updatedOn"',
name: 'CastError',
stringValue: '"Aug 7th 17"',
kind: 'date',
value: 'Aug 7th 17',
path: 'updatedOn',
reason: undefined }
The double default assignment looks wrong.
default: default: moment(new Date(Date.now())).format('MMM Do YY')
As a better practice, you should consider just using UNIX timestamps rather than formatted date strings. Timestamps will allow you to easily track statistics on the database.
So you could just use the:
Date.now() // returns a UNIX timestamp
In moment.js a UNIX timestamp:
moment().unix()
If for some reason you need the timestamp in a string format you could always just do:
moment().unix() + ''

jsonix properties - ogc-schemas .js aren't same

The manual on jsonix properties at https://github.com/highsource/jsonix/wiki/Properties shows properties as being something like:
name: 'MyModule',
typeInfos: [{
type: 'classInfo',
localName: 'InputType',
propertyInfos: [{
type: 'attribute',
typeInfo: 'Boolean',
name: 'checked'
}]
}],
But then (after npm install ogc-schemas) what I am seeing is:
ln: 'TimeClockPropertyType',
ps: [{
n: 'timeClock',
rq: true,
en: 'TimeClock',
ti: '.TimeClockType'
},
With the abbreviated names.
Which should it be and why doesn't it matter if it doesn't?
Disclaimer: I'm the author of jsonix.
This is what's called compact naming. This is an option of the Jsonix Schema Compiler which generates shorter names in mappings, like n instead of name or dens instead of defaultElementNamespaceURI. The goal is clearly to make mappings smaller and since ogc-schemas are pretty large, they are compiled with compact naming by default.
If you want standard naming, fork and remove
<arg>-Xjsonix-compact</arg>
from all the pom.xmls.
Both compact and standard names work in runtime, I think standard names have higher priority.

Second if statement in jade layout not working

The first if statement is firing corretly and marking the checkbox as checked when the value is true, but the second isn't.
- var profileVal = false
- var logbookVal = false
div.form-group.col-md-6
-if(user.profilePublic){
- profileVal = true
-}
label Public profile
p Would you like your profile to be public?
input(type='checkbox',name='userPublicProfile',class='floatLeftCheckbox',checked=profileVal)
div.form-group.col-md-6.clearfix
label Public logbook
p Would you like your logbook to be public?
-if(user.logPublic){
- logbookVal = true
-}
input(type='checkbox',name='userPublicLogbook',class='floatLeftCheckbox',checked=logbookVal)
Both are set to Boolean in mongoDB. Both are correctly spelled etc.
Result of console.log:
deserializing user: { _id: 5547e77928d405cc236d4a01,
updated_at: 'Mon May 04 2015 17:41:13 GMT-0400 (EDT)',
created_at: 'Mon May 04 2015 17:41:13 GMT-0400 (EDT)',
admin: false,
lastName: '######',
firstName: '######',
email: '######',
password: '######',
callsign: '######',
uid: '$2a$10$/yF1pqR9N5VkXcnLaNDPBea8xH.tO3Tto82W.9wGD3VdZGPAdXID6',
profilePublic: true,
logPublic: true,
__v: 0 }
Also tried:
-if(user.logPublic)
p It's true
Still nothing

Resources