# Changelog of Airbnb Reviews Scraper & Sentiment Analyzer (`integrative_tangent/airbnb-reviews-sentiment`) Actor

- **URL**: https://apify.com/integrative\_tangent/airbnb-reviews-sentiment/changelog.md
- **Full Actor documentation**: https://apify.com/integrative\_tangent/airbnb-reviews-sentiment.md

## Changelog

### 1.1.0 — 2026-08-13 — sentiment that works outside English, and works per aspect

Two defects had been shipping since launch. Both produced output that looked
fine and was wrong, which is the worst kind.

#### Fixed

- **Five of the six advertised languages scored nothing.** Every review went
  through AFINN-165 — an English word list — regardless of the language it was
  written in. Measured against the previous build:

  | Review | Before | After |
  | --- | --- | --- |
  | "El apartamento estaba sucio y había cucarachas por todas partes." | `0` · neutral | `-4.37` · negative |
  | "Appartement très sale, hôte impoli et bruyant toute la nuit." | `0` · neutral | `-4.71` · negative |
  | "A ubicação é fantástica, mas o apartamento estava sujo." | `0` · neutral | `-2.71` · negative |

  A guest reporting cockroaches was being reported as neutral. Spanish and
  Portuguese reviews also picked up noise, because AFINN contains the English
  word "no" at −1 and both languages use it constantly.

  English keeps AFINN-165 (3,382 entries) as its base — no hand-written list
  matches that coverage. Spanish, French, Portuguese, German and Italian now
  have curated lexicons weighted toward the vocabulary that actually shows up in
  rental reviews: cleanliness, noise, host behaviour, pests, value.

- **Topic detection matched substrings.** `TOPIC_KEYWORDS.amenities` contained
  `'ac'`, and the check was `text.toLowerCase().includes(keyword)`. So:

  ```
  AMENITIES  "The place was great"        <- pl-AC-e
  AMENITIES  "Beautiful beach nearby"     <- be-AC-h
  AMENITIES  "Gracias por todo"           <- gr-AC-ias
  AMENITIES  "Muy espacioso"              <- esp-AC-ioso
  AMENITIES  "Hard to contact the host"   <- cont-AC-t
  ```

  Essentially every review was tagged as discussing amenities. `'sale'` (French
  for dirty) matched the English word "sale"; `'key'` matched "monkey".
  Matching is now on whole tokens, with multi-word terms handled as phrases, so
  "the AC was broken" and "el aire acondicionado no funcionaba" still register.

- **Length diluted the score.** The old normalization divided by word count, so
  a long, specific complaint scored closer to neutral than a two-word "Great!".
  That is backwards — detail is signal. Scoring now uses `raw / √(raw² + 15)`,
  which is bounded and unaffected by padding.

- **"Mixed" fired on any two negative words.** A glowing review that called the
  flat "small" once came back mixed. It now requires real weight on both sides.

#### Added

- **Sentiment per aspect, not just per review.** Knowing a review mentions
  cleanliness is close to useless; knowing it complains about cleanliness while
  praising the location is the product:

  ```json
  "sentimentAspects": {
    "location":      { "score":  4.38, "clauses": 1 },
    "communication": { "score":  4.38, "clauses": 1 },
    "cleanliness":   { "score": -4.20, "clauses": 1 },
    "comfort":       { "score": -4.20, "clauses": 1 }
  }
  ```

  Six of the eight aspects are exactly the categories Airbnb itself rates
  (cleanliness, accuracy, check-in, communication, location, value), so a text
  score sits on the same axis as the published number. `comfort` and `amenities`
  are kept because guests write about them constantly.

- **`ratingVsText`** in the per-listing rollup — text sentiment minus the star
  rating, both on −1..1. Airbnb ratings are compressed (most listings sit
  between 4.7 and 5.0) and average the listing's entire history, while text is
  current. A clearly negative gap means recent guests are writing complaints the
  headline rating has not caught up with. Five stars on "the place was filthy,
  the smell was disgusting and the host was rude" now reports `-1.92`.

- **Negation and intensifiers in all six languages.** "No estaba limpio" is not
  a compliment. Flips are damped rather than mirrored, so "not great" lands
  milder than "terrible".

- **Contrast handling.** "We had high hopes **but** it was terrible" used to net
  to zero, because AFINN scores `hopes` (+2) and `reach` (+1) against `terrible`
  (−3). What follows the contrast marker now carries the verdict. Applies to the
  review-level score only — aspect scores stay unweighted, since "the location
  is great but it was filthy" is equally true about both.

- **`sentimentSupported` and `sentimentLanguage`.** Reviews in a language
  outside the supported six return `null` rather than `neutral`, and the run log
  reports what share of the set was actually scored. Conflating "unknown" with
  "neutral" silently biases every aggregate toward the middle.

- **`LISTING_SUMMARY`** in the key-value store — one row per listing with the
  aspect breakdown, so listings can be compared without reading the review-grain
  dataset.

- **Recency split.** `aspectsRecent` (last 24 months by default) and
  `aspectTrend` alongside the lifetime `aspects`. A lifetime mean has the exact
  flaw this Actor exists to expose in the star rating: a problem fixed two years
  ago still drags the number down. Measured on a real listing
  (`496506725996433560`, 66 reviews), `amenities` read **−0.92 across 2022-23**
  and **+4.01 across 2024-26** — the host had fixed the internet and the
  kitchen. The lifetime figure said +1.20 and pointed at nothing useful.

  A trend is only emitted where both windows hold at least three reviews.
  Reviews whose date cannot be parsed are counted in `undatedReviews` rather
  than quietly dropped.

- 76 assertions covering both original bugs, negation and contrast in all six
  languages, negation scoping, aspect attribution, the recency split, and the
  aggregate edge cases.

#### Fixed after testing against real data

Both of these were caught by running the engine over the 66 live reviews of
listing `496506725996433560`, not by the synthetic tests:

- **Negation leaked across commas and past the word it had already negated.**
  A wholly positive review — *"The apartment was as shown in pictures if not
  better, very clean and comfy place…"* — scored `cleanliness` at **−3.30**,
  because the `not` in "if not better" sat three tokens behind `clean`.
  Negation is now scoped to its comma-delimited span and stops at the first
  sentiment word it reaches. That review now scores cleanliness **+1.39**.

- **AFINN-165 does not carry ordinary rental vocabulary.** "Fully equipped
  kitchen" scored 0 — `equipped` is simply not in the list, and the phrase
  appears in a large share of positive reviews. Added `equipped`, `stocked`,
  `comfy`, `airy`, `bright`, `modern`, `convenient`, `reliable`, `safe`,
  `secure`, `furnished`, and the negatives `leak`, `broken`, `stuffy`,
  `outdated`, `worn`, `cracked`, `clogged`, `flooded`, `uncomfortable`.

#### Changed

- `sentiment` and `@types/sentiment` dropped as dependencies; `afinn-165` added
  (the word list alone, without the English-only scoring engine).
