Match landing stories to catalog navigation
Build and deploy Docker image / build (push) Successful in 1m10s
Build and deploy Docker image / build (push) Successful in 1m10s
This commit is contained in:
+1
-1
Submodule graphql-contracts updated: ca08538d38...e96e5afd01
+142
-44
@@ -127,9 +127,31 @@ export const typeDefs = `#graphql
|
||||
distanceKm: Float
|
||||
}
|
||||
|
||||
type LandingNetworkOffer {
|
||||
uuid: String!
|
||||
productUuid: String!
|
||||
productName: String!
|
||||
supplierUuid: String!
|
||||
supplierName: String!
|
||||
country: String
|
||||
latitude: Float!
|
||||
longitude: Float!
|
||||
distanceKm: Float
|
||||
pricePerUnit: String
|
||||
currency: String
|
||||
}
|
||||
|
||||
type LandingNetworkProduct {
|
||||
uuid: String!
|
||||
name: String!
|
||||
}
|
||||
|
||||
type LandingNetworkStory {
|
||||
focus: LandingNetworkPoint!
|
||||
neighbors: [LandingNetworkPoint!]!
|
||||
offers: [LandingNetworkOffer!]!
|
||||
hubs: [LandingNetworkPoint!]!
|
||||
products: [LandingNetworkProduct!]!
|
||||
suppliers: [LandingNetworkPoint!]!
|
||||
}
|
||||
|
||||
type OfferNode {
|
||||
@@ -197,7 +219,7 @@ export const typeDefs = `#graphql
|
||||
nearestHubs(lat: Float!, lon: Float!, radius: Float, productUuid: String, limit: Int): [Node!]!
|
||||
nearestOffers(lat: Float!, lon: Float!, radius: Float, productUuid: String, hubUuid: String, limit: Int): [OfferWithRoute!]!
|
||||
nearestSuppliers(lat: Float!, lon: Float!, radius: Float, productUuid: String, limit: Int): [Supplier!]!
|
||||
landingNetworkStory(focusKind: LandingNetworkKind!, neighborLimit: Int): LandingNetworkStory!
|
||||
landingNetworkStory(focusKind: LandingNetworkKind!, itemLimit: Int): LandingNetworkStory!
|
||||
routeToCoordinate(offerUuid: String!, lat: Float!, lon: Float!): ProductRouteOption
|
||||
hubsList(limit: Int, offset: Int, country: String, transportType: String, west: Float, south: Float, east: Float, north: Float): [Node!]!
|
||||
suppliersList(limit: Int, offset: Int, country: String, west: Float, south: Float, east: Float, north: Float): [Supplier!]!
|
||||
@@ -272,30 +294,87 @@ function mapLandingNetworkPoint(doc: ArangoDoc, kind: LandingNetworkKind) {
|
||||
}
|
||||
}
|
||||
|
||||
async function resolveLandingNetworkStory(focusKind: LandingNetworkKind, neighborLimit?: number) {
|
||||
function mapLandingNetworkOffer(doc: ArangoDoc) {
|
||||
return {
|
||||
uuid: doc._key,
|
||||
productUuid: doc.product_uuid,
|
||||
productName: doc.product_name,
|
||||
supplierUuid: doc.supplier_uuid,
|
||||
supplierName: doc.supplier_name,
|
||||
country: doc.country ?? null,
|
||||
latitude: doc.latitude,
|
||||
longitude: doc.longitude,
|
||||
distanceKm: doc.distance_km ?? null,
|
||||
pricePerUnit: doc.price_per_unit ?? null,
|
||||
currency: doc.currency ?? null,
|
||||
}
|
||||
}
|
||||
|
||||
function landingProductsFromOffers(offers: ArangoDoc[], limit: number) {
|
||||
const products = new Map<string, { uuid: string; name: string }>()
|
||||
for (const offer of offers) {
|
||||
if (!products.has(offer.product_uuid)) {
|
||||
products.set(offer.product_uuid, { uuid: offer.product_uuid, name: offer.product_name })
|
||||
}
|
||||
}
|
||||
return [...products.values()].slice(0, limit)
|
||||
}
|
||||
|
||||
function landingSuppliersFromOffers(offers: ArangoDoc[], limit: number) {
|
||||
const suppliers = new Map<string, ReturnType<typeof mapLandingNetworkPoint>>()
|
||||
for (const offer of offers) {
|
||||
if (!suppliers.has(offer.supplier_uuid)) {
|
||||
suppliers.set(offer.supplier_uuid, mapLandingNetworkPoint({
|
||||
_key: offer.supplier_uuid,
|
||||
name: offer.supplier_name,
|
||||
country: offer.country,
|
||||
latitude: offer.latitude,
|
||||
longitude: offer.longitude,
|
||||
distance_km: offer.distance_km,
|
||||
}, 'SUPPLIER'))
|
||||
}
|
||||
}
|
||||
return [...suppliers.values()].slice(0, limit)
|
||||
}
|
||||
|
||||
async function resolveLandingNetworkStory(focusKind: LandingNetworkKind, itemLimit?: number) {
|
||||
const db = getDb()
|
||||
const limit = Math.max(1, Math.min(neighborLimit ?? 3, 6))
|
||||
const focusCursor = await db.query(focusKind === 'HUB' ? `
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == null
|
||||
FILTER hub.latitude != null AND hub.longitude != null
|
||||
SORT RAND()
|
||||
LIMIT 1
|
||||
RETURN hub
|
||||
` : `
|
||||
const limit = Math.max(1, Math.min(itemLimit ?? 3, 6))
|
||||
|
||||
if (focusKind === 'SUPPLIER') {
|
||||
const focusCursor = await db.query(`
|
||||
FOR supplier IN nodes
|
||||
FILTER supplier.node_type == 'supplier'
|
||||
FILTER supplier.latitude != null AND supplier.longitude != null
|
||||
FILTER LENGTH(
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid == supplier._key
|
||||
FILTER offer.product_uuid != null AND offer.product_name != null
|
||||
LIMIT 1
|
||||
RETURN 1
|
||||
) > 0
|
||||
SORT RAND()
|
||||
LIMIT 1
|
||||
RETURN supplier
|
||||
`)
|
||||
const focus = await focusCursor.next() as ArangoDoc | undefined
|
||||
if (!focus) throw new Error(`No ${focusKind.toLowerCase()} is available for the landing network story`)
|
||||
if (!focus) throw new Error('No supplier with offers is available for the landing network story')
|
||||
|
||||
if (focusKind === 'SUPPLIER') {
|
||||
const neighborsCursor = await db.query(`
|
||||
const offersCursor = await db.query(`
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid == @supplier_uuid
|
||||
FILTER offer.product_uuid != null AND offer.product_name != null
|
||||
FILTER offer.supplier_name != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
SORT RAND()
|
||||
LIMIT @limit
|
||||
RETURN MERGE(offer, {distance_km: 0})
|
||||
`, { supplier_uuid: focus._key, limit })
|
||||
const offers = await offersCursor.all() as ArangoDoc[]
|
||||
|
||||
const hubsCursor = await db.query(`
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == null
|
||||
@@ -305,43 +384,62 @@ async function resolveLandingNetworkStory(focusKind: LandingNetworkKind, neighbo
|
||||
LIMIT @limit
|
||||
RETURN MERGE(hub, {distance_km: dist})
|
||||
`, { lat: focus.latitude, lon: focus.longitude, limit })
|
||||
const neighbors = await neighborsCursor.all()
|
||||
const hubs = await hubsCursor.all()
|
||||
return {
|
||||
focus: mapLandingNetworkPoint(focus, 'SUPPLIER'),
|
||||
neighbors: neighbors.map((neighbor: ArangoDoc) => mapLandingNetworkPoint(neighbor, 'HUB')),
|
||||
offers: offers.map(mapLandingNetworkOffer),
|
||||
hubs: hubs.map((hub: ArangoDoc) => mapLandingNetworkPoint(hub, 'HUB')),
|
||||
products: [],
|
||||
suppliers: [],
|
||||
}
|
||||
}
|
||||
|
||||
const neighborsCursor = await db.query(`
|
||||
const sourceOfferCursor = await db.query(`
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.supplier_uuid != null
|
||||
FILTER offer.product_uuid != null AND offer.product_name != null
|
||||
FILTER offer.supplier_uuid != null AND offer.supplier_name != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
SORT RAND()
|
||||
LIMIT 1
|
||||
RETURN offer
|
||||
`)
|
||||
const sourceOffer = await sourceOfferCursor.next() as ArangoDoc | undefined
|
||||
if (!sourceOffer) throw new Error('No offer is available for the landing network story')
|
||||
|
||||
const focusCursor = await db.query(`
|
||||
FOR hub IN nodes
|
||||
FILTER hub.node_type == 'logistics' OR hub.node_type == null
|
||||
FILTER hub.product_uuid == null
|
||||
FILTER hub.latitude != null AND hub.longitude != null
|
||||
LET dist = DISTANCE(hub.latitude, hub.longitude, @lat, @lon) / 1000
|
||||
SORT dist ASC
|
||||
LIMIT 1
|
||||
RETURN hub
|
||||
`, { lat: sourceOffer.latitude, lon: sourceOffer.longitude })
|
||||
const focus = await focusCursor.next() as ArangoDoc | undefined
|
||||
if (!focus) throw new Error('No hub is available for the landing network story')
|
||||
|
||||
const offersCursor = await db.query(`
|
||||
FOR offer IN nodes
|
||||
FILTER offer.node_type == 'offer'
|
||||
FILTER offer.product_uuid != null AND offer.product_name != null
|
||||
FILTER offer.supplier_uuid != null AND offer.supplier_name != null
|
||||
FILTER offer.latitude != null AND offer.longitude != null
|
||||
LET dist = DISTANCE(offer.latitude, offer.longitude, @lat, @lon) / 1000
|
||||
COLLECT supplier_uuid = offer.supplier_uuid INTO offers
|
||||
LET first_offer = FIRST(offers).offer
|
||||
LET supplier_dist = DISTANCE(first_offer.latitude, first_offer.longitude, @lat, @lon) / 1000
|
||||
LET supplier_node = FIRST(
|
||||
FOR supplier IN nodes
|
||||
FILTER supplier._key == supplier_uuid
|
||||
FILTER supplier.node_type == 'supplier'
|
||||
RETURN supplier
|
||||
)
|
||||
SORT supplier_dist ASC
|
||||
LIMIT @limit
|
||||
RETURN {
|
||||
_key: supplier_uuid,
|
||||
name: supplier_node != null ? supplier_node.name : first_offer.supplier_name,
|
||||
country: supplier_node != null ? supplier_node.country : first_offer.country,
|
||||
latitude: supplier_node != null ? supplier_node.latitude : first_offer.latitude,
|
||||
longitude: supplier_node != null ? supplier_node.longitude : first_offer.longitude,
|
||||
distance_km: supplier_dist
|
||||
}
|
||||
`, { lat: focus.latitude, lon: focus.longitude, limit })
|
||||
const neighbors = await neighborsCursor.all()
|
||||
FILTER dist <= 500
|
||||
SORT dist ASC
|
||||
LIMIT @offer_limit
|
||||
RETURN MERGE(offer, {distance_km: dist})
|
||||
`, { lat: focus.latitude, lon: focus.longitude, offer_limit: limit * 4 })
|
||||
const offers = await offersCursor.all() as ArangoDoc[]
|
||||
const visibleOffers = offers.slice(0, limit)
|
||||
return {
|
||||
focus: mapLandingNetworkPoint(focus, 'HUB'),
|
||||
neighbors: neighbors.map((neighbor: ArangoDoc) => mapLandingNetworkPoint(neighbor, 'SUPPLIER')),
|
||||
offers: visibleOffers.map(mapLandingNetworkOffer),
|
||||
hubs: [],
|
||||
products: landingProductsFromOffers(visibleOffers, limit),
|
||||
suppliers: landingSuppliersFromOffers(visibleOffers, limit),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,8 +666,8 @@ export const resolvers = {
|
||||
},
|
||||
|
||||
Query: {
|
||||
landingNetworkStory: async (_: unknown, args: { focusKind: LandingNetworkKind; neighborLimit?: number }) => {
|
||||
return resolveLandingNetworkStory(args.focusKind, args.neighborLimit)
|
||||
landingNetworkStory: async (_: unknown, args: { focusKind: LandingNetworkKind; itemLimit?: number }) => {
|
||||
return resolveLandingNetworkStory(args.focusKind, args.itemLimit)
|
||||
},
|
||||
|
||||
node: async (_: unknown, args: { uuid: string }) => {
|
||||
|
||||
Reference in New Issue
Block a user